Fix vulnerability that allowed attackers to start arbitary
activities

Test: atest DreamServiceTest
Test: flashed device and verified dream settings works as expected
Fixes: 242845514
Change-Id: I6e90e3a0d513dceb7d7f5c59d6807ebe164c5716
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index 521793b..1872352 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -2412,6 +2412,16 @@
     method public final boolean shouldShowComplications();
   }
 
+  public class DreamService extends android.app.Service implements android.view.Window.Callback {
+    method @Nullable public static android.service.dreams.DreamService.DreamMetadata getDreamMetadata(@NonNull android.content.Context, @Nullable android.content.pm.ServiceInfo);
+  }
+
+  public static final class DreamService.DreamMetadata {
+    field @Nullable public final android.graphics.drawable.Drawable previewImage;
+    field @Nullable public final android.content.ComponentName settingsActivity;
+    field @NonNull public final boolean showComplications;
+  }
+
 }
 
 package android.service.notification {
diff --git a/core/java/android/service/dreams/DreamService.java b/core/java/android/service/dreams/DreamService.java
index 2d461c6..7515538 100644
--- a/core/java/android/service/dreams/DreamService.java
+++ b/core/java/android/service/dreams/DreamService.java
@@ -22,6 +22,7 @@
 import android.annotation.Nullable;
 import android.annotation.SdkConstant;
 import android.annotation.SdkConstant.SdkConstantType;
+import android.annotation.TestApi;
 import android.app.Activity;
 import android.app.ActivityTaskManager;
 import android.app.AlarmManager;
@@ -1124,7 +1125,8 @@
      * @hide
      */
     @Nullable
-    public static DreamMetadata getDreamMetadata(Context context,
+    @TestApi
+    public static DreamMetadata getDreamMetadata(@NonNull Context context,
             @Nullable ServiceInfo serviceInfo) {
         if (serviceInfo == null) return null;
 
@@ -1183,7 +1185,8 @@
         }
     }
 
-    private static ComponentName convertToComponentName(String flattenedString,
+    @Nullable
+    private static ComponentName convertToComponentName(@Nullable String flattenedString,
             ServiceInfo serviceInfo) {
         if (flattenedString == null) {
             return null;
@@ -1193,7 +1196,17 @@
             return new ComponentName(serviceInfo.packageName, flattenedString);
         }
 
-        return ComponentName.unflattenFromString(flattenedString);
+        // Ensure that the component is from the same package as the dream service. If not,
+        // treat the component as invalid and return null instead.
+        final ComponentName cn = ComponentName.unflattenFromString(flattenedString);
+        if (cn == null) return null;
+        if (!cn.getPackageName().equals(serviceInfo.packageName)) {
+            Log.w(TAG,
+                    "Inconsistent package name in component: " + cn.getPackageName()
+                            + ", should be: " + serviceInfo.packageName);
+            return null;
+        }
+        return cn;
     }
 
     /**
@@ -1489,6 +1502,7 @@
      *
      * @hide
      */
+    @TestApi
     public static final class DreamMetadata {
         @Nullable
         public final ComponentName settingsActivity;
diff --git a/services/tests/servicestests/AndroidManifest.xml b/services/tests/servicestests/AndroidManifest.xml
index 0483a60..7ae70eb 100644
--- a/services/tests/servicestests/AndroidManifest.xml
+++ b/services/tests/servicestests/AndroidManifest.xml
@@ -147,6 +147,19 @@
                 android:resource="@xml/test_dream_metadata" />
         </service>
 
+        <service
+            android:name="com.android.server.dreams.TestDreamServiceWithInvalidSettings"
+            android:exported="false"
+            android:label="Test Dream" >
+            <intent-filter>
+                <action android:name="android.service.dreams.DreamService" />
+                <category android:name="android.intent.category.DEFAULT" />
+            </intent-filter>
+            <meta-data
+                android:name="android.service.dream"
+                android:resource="@xml/test_dream_metadata_invalid" />
+        </service>
+
         <receiver android:name="com.android.server.devicepolicy.ApplicationRestrictionsTest$AdminReceiver"
              android:permission="android.permission.BIND_DEVICE_ADMIN"
              android:exported="true">
diff --git a/services/tests/servicestests/res/xml/test_dream_metadata.xml b/services/tests/servicestests/res/xml/test_dream_metadata.xml
index aa054f1..9905c69 100644
--- a/services/tests/servicestests/res/xml/test_dream_metadata.xml
+++ b/services/tests/servicestests/res/xml/test_dream_metadata.xml
@@ -15,5 +15,5 @@
   -->
 
 <dream xmlns:android="http://schemas.android.com/apk/res/android"
-       android:settingsActivity="com.android.server.dreams/.TestDreamSettingsActivity"
+       android:settingsActivity="com.android.frameworks.servicestests/.TestDreamSettingsActivity"
        android:showClockAndComplications="false" />
diff --git a/services/tests/servicestests/res/xml/test_dream_metadata_invalid.xml b/services/tests/servicestests/res/xml/test_dream_metadata_invalid.xml
new file mode 100644
index 0000000..47864d9
--- /dev/null
+++ b/services/tests/servicestests/res/xml/test_dream_metadata_invalid.xml
@@ -0,0 +1,20 @@
+<!--
+  ~ Copyright (C) 2022 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+
+<!-- The settings activity is in a different package, which is invalid -->
+<dream xmlns:android="http://schemas.android.com/apk/res/android"
+       android:settingsActivity="com.android.server.dreams/.TestDreamSettingsActivity"
+       android:showClockAndComplications="false"/>
diff --git a/services/tests/servicestests/src/com/android/server/dreams/DreamServiceTest.java b/services/tests/servicestests/src/com/android/server/dreams/DreamServiceTest.java
index 74d2e0f..0efd296 100644
--- a/services/tests/servicestests/src/com/android/server/dreams/DreamServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/dreams/DreamServiceTest.java
@@ -16,7 +16,8 @@
 
 package com.android.server.dreams;
 
-import static org.junit.Assert.assertEquals;
+import static com.google.common.truth.Truth.assertThat;
+
 import static org.junit.Assert.assertFalse;
 
 import android.content.ComponentName;
@@ -35,21 +36,36 @@
 @SmallTest
 @RunWith(AndroidJUnit4.class)
 public class DreamServiceTest {
+    private static final String TEST_PACKAGE_NAME = "com.android.frameworks.servicestests";
+
     @Test
     public void testMetadataParsing() throws PackageManager.NameNotFoundException {
-        final String testPackageName = "com.android.frameworks.servicestests";
         final String testDreamClassName = "com.android.server.dreams.TestDreamService";
-        final String testSettingsActivity = "com.android.server.dreams/.TestDreamSettingsActivity";
+        final String testSettingsActivity =
+                "com.android.frameworks.servicestests/.TestDreamSettingsActivity";
+        final DreamService.DreamMetadata metadata = getDreamMetadata(testDreamClassName);
 
-        final Context context = InstrumentationRegistry.getInstrumentation().getTargetContext();
-
-        final ServiceInfo si = context.getPackageManager().getServiceInfo(
-                new ComponentName(testPackageName, testDreamClassName),
-                PackageManager.ComponentInfoFlags.of(PackageManager.GET_META_DATA));
-        final DreamService.DreamMetadata metadata = DreamService.getDreamMetadata(context, si);
-
-        assertEquals(0, metadata.settingsActivity.compareTo(
-                ComponentName.unflattenFromString(testSettingsActivity)));
+        assertThat(metadata.settingsActivity).isEqualTo(
+                ComponentName.unflattenFromString(testSettingsActivity));
         assertFalse(metadata.showComplications);
     }
+
+    @Test
+    public void testMetadataParsing_invalidSettingsActivity()
+            throws PackageManager.NameNotFoundException {
+        final String testDreamClassName =
+                "com.android.server.dreams.TestDreamServiceWithInvalidSettings";
+        final DreamService.DreamMetadata metadata = getDreamMetadata(testDreamClassName);
+
+        assertThat(metadata.settingsActivity).isNull();
+    }
+
+    private DreamService.DreamMetadata getDreamMetadata(String dreamClassName)
+            throws PackageManager.NameNotFoundException {
+        final Context context = InstrumentationRegistry.getInstrumentation().getTargetContext();
+        final ServiceInfo si = context.getPackageManager().getServiceInfo(
+                new ComponentName(TEST_PACKAGE_NAME, dreamClassName),
+                PackageManager.ComponentInfoFlags.of(PackageManager.GET_META_DATA));
+        return DreamService.getDreamMetadata(context, si);
+    }
 }
diff --git a/services/tests/servicestests/src/com/android/server/dreams/TestDreamServiceWithInvalidSettings.java b/services/tests/servicestests/src/com/android/server/dreams/TestDreamServiceWithInvalidSettings.java
new file mode 100644
index 0000000..5c7d02f
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/dreams/TestDreamServiceWithInvalidSettings.java
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.dreams;
+
+import android.service.dreams.DreamService;
+
+/**
+ * Dream service implementation for unit testing, where the settings activity is invalid.
+ */
+public class TestDreamServiceWithInvalidSettings extends DreamService {
+}