CTS for per-process custom application class

Fix: 197264681
Test: atest CtsProcessTest
Change-Id: Ie015de9016278ee932438f59c7c1ccea27dedd13
diff --git a/tests/process/Android.bp b/tests/process/Android.bp
index 4976112..a70625f 100644
--- a/tests/process/Android.bp
+++ b/tests/process/Android.bp
@@ -61,5 +61,41 @@
     defaults: ["CtsProcessTest_default"],
 
     manifest: "AndroidManifest_helper1.xml",
+    additional_manifests: [
+        "AndroidManifest_helper.xml",
+    ],
     package_name: "android.os.cts.process.helper1",
 }
+
+android_test_helper_app {
+    name: "CtsProcessTestHelper2",
+    defaults: ["CtsProcessTest_default"],
+
+    manifest: "AndroidManifest_helper2.xml",
+    additional_manifests: [
+        "AndroidManifest_helper.xml",
+    ],
+    package_name: "android.os.cts.process.helper2",
+}
+
+android_test_helper_app {
+    name: "CtsProcessTestHelper3",
+    defaults: ["CtsProcessTest_default"],
+
+    manifest: "AndroidManifest_helper3.xml",
+    additional_manifests: [
+        "AndroidManifest_helper.xml",
+    ],
+    package_name: "android.os.cts.process.helper3",
+}
+
+android_test_helper_app {
+    name: "CtsProcessTestHelper4",
+    defaults: ["CtsProcessTest_default"],
+
+    manifest: "AndroidManifest_helper4.xml",
+    additional_manifests: [
+        "AndroidManifest_helper.xml",
+    ],
+    package_name: "android.os.cts.process.helper4",
+}
diff --git a/tests/process/AndroidManifest_helper.xml b/tests/process/AndroidManifest_helper.xml
new file mode 100644
index 0000000..45ac951
--- /dev/null
+++ b/tests/process/AndroidManifest_helper.xml
@@ -0,0 +1,42 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2021 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.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="android.os.cts.process.helper" >
+
+    <application>
+        <receiver android:name=".MyReceiver0"
+            android:exported="true">
+            <intent-filter>
+                <action android:name="ACTION_SEND_BACK_START_TIME" />
+            </intent-filter>
+        </receiver>
+        <receiver android:name=".MyReceiver1"
+            android:exported="true"
+            android:process=":sub1">
+            <intent-filter>
+                <action android:name="ACTION_SEND_BACK_START_TIME" />
+            </intent-filter>
+        </receiver>
+        <receiver android:name=".MyReceiver2"
+            android:exported="true"
+            android:process=":sub2">
+            <intent-filter>
+                <action android:name="ACTION_SEND_BACK_START_TIME" />
+            </intent-filter>
+        </receiver>
+    </application>
+</manifest>
\ No newline at end of file
diff --git a/tests/process/AndroidManifest_helper1.xml b/tests/process/AndroidManifest_helper1.xml
index edcc187..b59ea1b 100644
--- a/tests/process/AndroidManifest_helper1.xml
+++ b/tests/process/AndroidManifest_helper1.xml
@@ -18,18 +18,10 @@
     package="android.os.cts.process.helper" >
 
     <application>
-        <receiver android:name=".MyReceiver"
-            android:exported="true">
-            <intent-filter>
-                <action android:name="ACTION_SEND_BACK_START_TIME" />
-            </intent-filter>
-        </receiver>
-        <receiver android:name=".MyReceiver2"
-            android:exported="true"
-            android:process=":sub">
-            <intent-filter>
-                <action android:name="ACTION_SEND_BACK_START_TIME" />
-            </intent-filter>
-        </receiver>
+        <processes>
+            <process /> <!-- For the main process -->
+            <process android:process=":sub1" android:name=".Application1" />
+            <process android:process=":sub2"/>
+        </processes>
     </application>
 </manifest>
\ No newline at end of file
diff --git a/tests/process/AndroidManifest_helper2.xml b/tests/process/AndroidManifest_helper2.xml
new file mode 100644
index 0000000..89346bd
--- /dev/null
+++ b/tests/process/AndroidManifest_helper2.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2021 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.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="android.os.cts.process.helper" >
+
+    <application android:name=".Application1" >
+        <processes>
+            <process /> <!-- For the main process -->
+            <process android:process=":sub1" android:name=".Application1b" />
+            <process android:process=":sub2" android:name=".Application2b" />
+        </processes>
+    </application>
+</manifest>
\ No newline at end of file
diff --git a/tests/process/AndroidManifest_helper3.xml b/tests/process/AndroidManifest_helper3.xml
new file mode 100644
index 0000000..9b9ef69
--- /dev/null
+++ b/tests/process/AndroidManifest_helper3.xml
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2021 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.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="android.os.cts.process.helper"
+    android:sharedUserId="android.os.cts.process.helper.shared">
+
+    <application android:process="android.os.cts.process.helper.shared_process">
+        <processes>
+            <process android:process="android.os.cts.process.helper.shared_process" />
+            <process android:process=":sub1" android:name=".Application1c" />
+            <process android:process=":sub2"/>
+            <process android:process="android.os.cts.process.helper.shared.sub3" android:name=".Application3" />
+        </processes>
+        <receiver android:name=".MyReceiver3"
+            android:exported="true"
+            android:process="android.os.cts.process.helper.shared.sub3">
+            <intent-filter>
+                <action android:name="ACTION_SEND_BACK_START_TIME" />
+            </intent-filter>
+        </receiver>
+    </application>
+</manifest>
\ No newline at end of file
diff --git a/tests/process/AndroidManifest_helper4.xml b/tests/process/AndroidManifest_helper4.xml
new file mode 100644
index 0000000..508dae2
--- /dev/null
+++ b/tests/process/AndroidManifest_helper4.xml
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2021 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.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="android.os.cts.process.helper"
+    android:sharedUserId="android.os.cts.process.helper.shared">
+
+    <application android:process="android.os.cts.process.helper.shared_process"
+            android:name=".Application1" >
+        <processes>
+            <process android:process="android.os.cts.process.helper.shared_process" />
+            <process android:process=":sub1"/>
+            <process android:process=":sub2" android:name=".Application2" />
+            <process android:process="android.os.cts.process.helper.shared.sub3" android:name=".Application3b" />
+        </processes>
+        <receiver android:name=".MyReceiver3"
+            android:exported="true"
+            android:process="android.os.cts.process.helper.shared.sub3">
+            <intent-filter>
+                <action android:name="ACTION_SEND_BACK_START_TIME" />
+            </intent-filter>
+        </receiver>
+    </application>
+</manifest>
\ No newline at end of file
diff --git a/tests/process/AndroidTest.xml b/tests/process/AndroidTest.xml
index 3d95af7..e3e9897 100644
--- a/tests/process/AndroidTest.xml
+++ b/tests/process/AndroidTest.xml
@@ -25,6 +25,9 @@
         <option name="cleanup-apks" value="true" />
         <option name="test-file-name" value="CtsProcessTest.apk" />
         <option name="test-file-name" value="CtsProcessTestHelper1.apk" />
+        <option name="test-file-name" value="CtsProcessTestHelper2.apk" />
+        <option name="test-file-name" value="CtsProcessTestHelper3.apk" />
+        <option name="test-file-name" value="CtsProcessTestHelper4.apk" />
     </target_preparer>
     <target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer">
         <option name="run-command" value="am wait-for-broadcast-idle" />
diff --git a/tests/process/src/android/os/cts/process/ProcessTest2.java b/tests/process/src/android/os/cts/process/ProcessTest2.java
index bab5d1a..c45577e 100644
--- a/tests/process/src/android/os/cts/process/ProcessTest2.java
+++ b/tests/process/src/android/os/cts/process/ProcessTest2.java
@@ -15,15 +15,41 @@
  */
 package android.os.cts.process;
 
+import static android.os.cts.process.common.Consts.HELPER1_RECEIVER0;
+import static android.os.cts.process.common.Consts.HELPER1_RECEIVER1;
+import static android.os.cts.process.common.Consts.HELPER1_RECEIVER2;
+import static android.os.cts.process.common.Consts.HELPER2_RECEIVER0;
+import static android.os.cts.process.common.Consts.HELPER2_RECEIVER1;
+import static android.os.cts.process.common.Consts.HELPER2_RECEIVER2;
+import static android.os.cts.process.common.Consts.HELPER3_RECEIVER0;
+import static android.os.cts.process.common.Consts.HELPER3_RECEIVER1;
+import static android.os.cts.process.common.Consts.HELPER3_RECEIVER2;
+import static android.os.cts.process.common.Consts.HELPER3_RECEIVER3;
+import static android.os.cts.process.common.Consts.HELPER4_RECEIVER0;
+import static android.os.cts.process.common.Consts.HELPER4_RECEIVER1;
+import static android.os.cts.process.common.Consts.HELPER4_RECEIVER2;
+import static android.os.cts.process.common.Consts.HELPER4_RECEIVER3;
+import static android.os.cts.process.common.Consts.HELPER_SHARED_PROCESS_NAME;
+import static android.os.cts.process.common.Consts.PACKAGE_HELPER1;
+import static android.os.cts.process.common.Consts.PACKAGE_HELPER2;
+import static android.os.cts.process.common.Consts.PACKAGE_HELPER3;
+import static android.os.cts.process.common.Consts.PACKAGE_HELPER4;
+
 import static com.google.common.truth.Truth.assertThat;
 
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
+import android.content.pm.ApplicationInfo;
 import android.os.Process;
 import android.os.SystemClock;
 import android.os.cts.process.common.Consts;
 import android.os.cts.process.common.Message;
 import android.util.Log;
+import android.util.LogPrinter;
+import android.util.Printer;
 
 import androidx.test.InstrumentationRegistry;
 import androidx.test.internal.runner.junit4.AndroidJUnit4ClassRunner;
@@ -43,6 +69,49 @@
 public class ProcessTest2 {
     protected static final Context sContext = InstrumentationRegistry.getTargetContext();
 
+    /** Tell all the helper app processes to stop */
+    private static void stopAllHelperApps() throws Exception {
+        // Make sure all the broadcasts are delivered.
+        ShellUtils.runShellCommand("am wait-for-broadcast-idle");
+        Thread.sleep(500); // Just give the system a bit time to breathe.
+        ShellUtils.runShellCommand("am force-stop " + PACKAGE_HELPER1);
+        ShellUtils.runShellCommand("am force-stop " + PACKAGE_HELPER2);
+        ShellUtils.runShellCommand("am force-stop " + PACKAGE_HELPER3);
+        ShellUtils.runShellCommand("am force-stop " + PACKAGE_HELPER4);
+        Thread.sleep(500); // Just give the system a bit time to breathe.
+    }
+
+    public void checkStartTime(ComponentName cn, String expectedProcessName) throws Exception {
+        // Start the target process by sending a broadcast, and get back the results
+        // from the target APIs.
+        try (Receiver<Message> receiver = new Receiver<>(sContext, Consts.TAG)) {
+
+            // Start the first process.
+            Intent intent = new Intent(Consts.ACTION_SEND_BACK_START_TIME)
+                    .setComponent(cn)
+                    .setFlags(Intent.FLAG_RECEIVER_FOREGROUND);
+
+            final long beforeStartElapsedRealtime = SystemClock.elapsedRealtime();
+            final long beforeStartUptimeMillis = SystemClock.uptimeMillis();
+
+            sContext.sendBroadcast(intent);
+
+            final Message m = receiver.waitForNextMessage();
+
+            // Check the start times.
+            assertThat(m.startRequestedElapsedRealtime).isAtLeast(beforeStartElapsedRealtime);
+            assertThat(m.startElapsedRealtime).isAtLeast(m.startRequestedElapsedRealtime);
+
+            assertThat(m.startRequestedUptimeMillis).isAtLeast(beforeStartUptimeMillis);
+            assertThat(m.startUptimeMillis).isAtLeast(m.startRequestedUptimeMillis);
+
+            // Check the process name.
+            assertThat(m.processName).isEqualTo(expectedProcessName);
+
+            // There may be more message, if the process has a custom app class, but ignore that.
+        }
+    }
+
     /**
      * Test for:
      * {@link Process#getStartElapsedRealtime()}
@@ -51,15 +120,34 @@
      * {@link Process#getStartRequestedUptimeMillis()}
      */
     @Test
-    public void testStartTime() {
-        // First, make sure the helper app is not running.
-        ShellUtils.runShellCommand("am force-stop " + Consts.PACKAGE_NAME_HELPER_1);
+    public void testStartTime() throws Exception {
+        stopAllHelperApps();
 
-        // Then, start the target process by sending a receiver, and get back the results
-        // from the target APIs.
+        // Main process.
+        checkStartTime(HELPER1_RECEIVER0, PACKAGE_HELPER1);
+
+        // Sub process.
+        checkStartTime(HELPER1_RECEIVER1, PACKAGE_HELPER1 + ":sub1");
+    }
+
+    /**
+     * Test for:
+     * {@link Process#getStartElapsedRealtime()}
+     * {@link Process#getStartUptimeMillis()}
+     * {@link Process#getStartRequestedElapsedRealtime()}
+     * {@link Process#getStartRequestedUptimeMillis()}
+     *
+     * but for a shared process.
+     */
+    @Test
+    public void testStartTime_sharedProcess() throws Exception {
+        stopAllHelperApps();
+
         try (Receiver<Message> receiver = new Receiver<>(sContext, Consts.TAG)) {
-            Intent intent = new Intent(Consts.ACTION_SEND_BACK_START_TIME)
-                    .setComponent(Consts.RECEIVER2_HELPER_1)
+
+            // Bring up the first package on the same process.
+            final Intent intent = new Intent(Consts.ACTION_SEND_BACK_START_TIME)
+                    .setComponent(Consts.HELPER3_RECEIVER0)
                     .setFlags(Intent.FLAG_RECEIVER_FOREGROUND);
 
             final long beforeStartElapsedRealtime = SystemClock.elapsedRealtime();
@@ -67,12 +155,9 @@
 
             sContext.sendBroadcast(intent);
 
-            Message m = receiver.waitForNextMessage();
+            final Message m = receiver.waitForNextMessage();
 
             // Check the start times.
-            Log.i(Consts.TAG, "beforeStartElapsedRealtime: " + beforeStartElapsedRealtime);
-            Log.i(Consts.TAG, "beforeStartUptimeMillis: " + beforeStartUptimeMillis);
-            Log.i(Consts.TAG, "Message: " + m);
 
             assertThat(m.startRequestedElapsedRealtime).isAtLeast(beforeStartElapsedRealtime);
             assertThat(m.startElapsedRealtime).isAtLeast(m.startRequestedElapsedRealtime);
@@ -81,12 +166,140 @@
             assertThat(m.startUptimeMillis).isAtLeast(m.startRequestedUptimeMillis);
 
             // Check the process name.
-            assertThat(m.processName).isEqualTo(Consts.PACKAGE_NAME_HELPER_1 + ":sub");
+            assertThat(m.processName).isEqualTo(HELPER_SHARED_PROCESS_NAME);
 
+            // Bring up the first package on the same process.
+            // The start request time should still be the same as the above result.
+            final Intent intent2 = new Intent(Consts.ACTION_SEND_BACK_START_TIME)
+                    .setComponent(Consts.HELPER4_RECEIVER0)
+                    .setFlags(Intent.FLAG_RECEIVER_FOREGROUND);
+
+            sContext.sendBroadcast(intent);
+
+            final Message m2 = receiver.waitForNextMessage();
+
+            assertThat(m2.startRequestedElapsedRealtime)
+                    .isEqualTo(m.startRequestedElapsedRealtime);
+            assertThat(m2.startRequestedUptimeMillis)
+                    .isEqualTo(m.startRequestedUptimeMillis);
+
+            assertThat(m2.processName).isEqualTo(HELPER_SHARED_PROCESS_NAME);
 
             receiver.ensureNoMoreMessages();
         }
     }
 
-    // TODO Test with a secodary process.
+    private void checkApplicationClass(ComponentName receiverComponent,
+            @NonNull String expectedPackageName, @NonNull String expectedProcessName,
+            @Nullable String expectedApplicationClassName) throws Exception {
+
+        // Start the target process by sending a receiver, and get back the results
+        // from the target APIs.
+        try (Receiver<Message> receiver = new Receiver<>(sContext, Consts.TAG)) {
+
+            // Start the first process.
+            Intent intent = new Intent(Consts.ACTION_SEND_BACK_START_TIME)
+                    .setComponent(receiverComponent)
+                    .setFlags(Intent.FLAG_RECEIVER_FOREGROUND);
+
+            sContext.sendBroadcast(intent);
+
+            // If the process has a custom application class, then the first message should be from
+            // the application class.
+            if (expectedApplicationClassName != null) {
+                final Message m = receiver.waitForNextMessage();
+                Log.i(Consts.TAG, "Message (which is supposed to be from the application class): "
+                        + m);
+
+                assertThat(m.packageName).isEqualTo(expectedPackageName);
+                assertThat(m.processName).isEqualTo(expectedProcessName);
+                assertThat(m.applicationClassName).isEqualTo(expectedApplicationClassName);
+            }
+
+            // Then there should be a message from the receiver.
+            final Message m = receiver.waitForNextMessage();
+            Log.i(Consts.TAG, "Message (which is supposed to be from the receiver): " + m);
+
+            assertThat(m.packageName).isEqualTo(expectedPackageName);
+            assertThat(m.processName).isEqualTo(expectedProcessName);
+
+            if (expectedApplicationClassName != null) {
+                assertThat(m.applicationContextClassName).isEqualTo(expectedApplicationClassName);
+            } else {
+                // No custom app class, so the default app class should be used.
+                assertThat(m.applicationContextClassName)
+                        .isEqualTo(android.app.Application.class.getCanonicalName());
+            }
+
+            receiver.ensureNoMoreMessages();
+        }
+    }
+
+    /**
+     * Make sure the correct app class is instantiated in the app processes.
+     */
+    @Test
+    public void testApplicationClass() throws Exception {
+        stopAllHelperApps();
+
+        // Each receiver in each helper package runs on different processes, which may or may
+        // not have a custom application class.
+        checkApplicationClass(HELPER1_RECEIVER0, PACKAGE_HELPER1, PACKAGE_HELPER1,
+                null);
+        checkApplicationClass(HELPER1_RECEIVER1, PACKAGE_HELPER1, PACKAGE_HELPER1 + ":sub1",
+                "android.os.cts.process.helper.Application1");
+        checkApplicationClass(HELPER1_RECEIVER2, PACKAGE_HELPER1, PACKAGE_HELPER1 + ":sub2",
+                null);
+
+        checkApplicationClass(HELPER2_RECEIVER0, PACKAGE_HELPER2, PACKAGE_HELPER2,
+                "android.os.cts.process.helper.Application1");
+        checkApplicationClass(HELPER2_RECEIVER1, PACKAGE_HELPER2, PACKAGE_HELPER2 + ":sub1",
+                "android.os.cts.process.helper.Application1b");
+        checkApplicationClass(HELPER2_RECEIVER2, PACKAGE_HELPER2, PACKAGE_HELPER2 + ":sub2",
+                "android.os.cts.process.helper.Application2b");
+
+        checkApplicationClass(HELPER3_RECEIVER0, PACKAGE_HELPER3,
+                "android.os.cts.process.helper.shared_process",
+                null);
+        checkApplicationClass(HELPER3_RECEIVER1, PACKAGE_HELPER3, PACKAGE_HELPER3 + ":sub1",
+                "android.os.cts.process.helper.Application1c");
+        checkApplicationClass(HELPER3_RECEIVER2, PACKAGE_HELPER3, PACKAGE_HELPER3 + ":sub2",
+                null);
+        checkApplicationClass(HELPER3_RECEIVER3, PACKAGE_HELPER3,
+                "android.os.cts.process.helper.shared.sub3",
+                "android.os.cts.process.helper.Application3");
+
+        checkApplicationClass(HELPER4_RECEIVER0, PACKAGE_HELPER4,
+                "android.os.cts.process.helper.shared_process",
+                "android.os.cts.process.helper.Application1");
+        checkApplicationClass(HELPER4_RECEIVER1, PACKAGE_HELPER4, PACKAGE_HELPER4 + ":sub1",
+                "android.os.cts.process.helper.Application1");
+        checkApplicationClass(HELPER4_RECEIVER2, PACKAGE_HELPER4, PACKAGE_HELPER4 + ":sub2",
+                "android.os.cts.process.helper.Application2");
+        checkApplicationClass(HELPER4_RECEIVER3, PACKAGE_HELPER4,
+                "android.os.cts.process.helper.shared.sub3",
+                "android.os.cts.process.helper.Application3b");
+    }
+
+    /**
+     * This doesn't do any assertions, but just dump the ApplicationInfo's for the helper APKs
+     * on logcat, so if some of the tests fail, we can look at the log and verify the
+     * ApplicationInfo is correct.
+     *
+     * (`dumpsys package` doesn't have a way to dump ApplicationInfo at the moment.)
+     */
+    @Test
+    public void dumpApplicationInfo() throws Exception {
+        LogPrinter pw = new LogPrinter(Log.VERBOSE, Consts.TAG);
+        dumpApplicationInfo(pw, PACKAGE_HELPER1);
+        dumpApplicationInfo(pw, Consts.PACKAGE_HELPER2);
+        dumpApplicationInfo(pw, Consts.PACKAGE_HELPER3);
+        dumpApplicationInfo(pw, Consts.PACKAGE_HELPER4);
+    }
+
+    private void dumpApplicationInfo(Printer pw, String packageName) throws Exception {
+        ApplicationInfo ai = sContext.getPackageManager().getApplicationInfo(packageName, 0);
+        pw.println("Dumping " + packageName);
+        ai.dump(pw, "    ");
+    }
 }
diff --git a/tests/process/src/android/os/cts/process/common/Consts.java b/tests/process/src/android/os/cts/process/common/Consts.java
index 57a1c7e..dc7409f 100644
--- a/tests/process/src/android/os/cts/process/common/Consts.java
+++ b/tests/process/src/android/os/cts/process/common/Consts.java
@@ -16,8 +16,10 @@
 package android.os.cts.process.common;
 
 import android.content.ComponentName;
-import android.os.cts.process.helper.MyReceiver;
+import android.os.cts.process.helper.MyReceiver0;
+import android.os.cts.process.helper.MyReceiver1;
 import android.os.cts.process.helper.MyReceiver2;
+import android.os.cts.process.helper.MyReceiver3;
 
 public class Consts {
     private Consts() {
@@ -25,11 +27,46 @@
 
     public static final String TAG = "CtsProcessTest";
 
-    public static final String PACKAGE_NAME_HELPER_1 = "android.os.cts.process.helper1";
-    public static final ComponentName RECEIVER_HELPER_1 = new ComponentName(
-            PACKAGE_NAME_HELPER_1, MyReceiver.class.getName());
-    public static final ComponentName RECEIVER2_HELPER_1 = new ComponentName(
-            PACKAGE_NAME_HELPER_1, MyReceiver2.class.getName());
+    public static final String PACKAGE_HELPER1 = "android.os.cts.process.helper1";
+    public static final String PACKAGE_HELPER2 = "android.os.cts.process.helper2";
+    public static final String PACKAGE_HELPER3 = "android.os.cts.process.helper3";
+    public static final String PACKAGE_HELPER4 = "android.os.cts.process.helper4";
+
+    public static final String HELPER_SHARED_PROCESS_NAME =
+            "android.os.cts.process.helper.shared_process";
+
+    private static ComponentName buildReceiver(String packageName, int receiverId) {
+        switch (receiverId) {
+            case 0:
+                return new ComponentName(packageName, MyReceiver0.class.getName());
+            case 1:
+                return new ComponentName(packageName, MyReceiver1.class.getName());
+            case 2:
+                return new ComponentName(packageName, MyReceiver2.class.getName());
+            case 3:
+                return new ComponentName(packageName, MyReceiver3.class.getName());
+            default:
+                throw new RuntimeException("Unsupported ID detected: " + receiverId);
+        }
+    }
+
+    public static final ComponentName HELPER1_RECEIVER0 = buildReceiver(PACKAGE_HELPER1, 0);
+    public static final ComponentName HELPER1_RECEIVER1 = buildReceiver(PACKAGE_HELPER1, 1);
+    public static final ComponentName HELPER1_RECEIVER2 = buildReceiver(PACKAGE_HELPER1, 2);
+
+    public static final ComponentName HELPER2_RECEIVER0 = buildReceiver(PACKAGE_HELPER2, 0);
+    public static final ComponentName HELPER2_RECEIVER1 = buildReceiver(PACKAGE_HELPER2, 1);
+    public static final ComponentName HELPER2_RECEIVER2 = buildReceiver(PACKAGE_HELPER2, 2);
+
+    public static final ComponentName HELPER3_RECEIVER0 = buildReceiver(PACKAGE_HELPER3, 0);
+    public static final ComponentName HELPER3_RECEIVER1 = buildReceiver(PACKAGE_HELPER3, 1);
+    public static final ComponentName HELPER3_RECEIVER2 = buildReceiver(PACKAGE_HELPER3, 2);
+    public static final ComponentName HELPER3_RECEIVER3 = buildReceiver(PACKAGE_HELPER3, 3);
+
+    public static final ComponentName HELPER4_RECEIVER0 = buildReceiver(PACKAGE_HELPER4, 0);
+    public static final ComponentName HELPER4_RECEIVER1 = buildReceiver(PACKAGE_HELPER4, 1);
+    public static final ComponentName HELPER4_RECEIVER2 = buildReceiver(PACKAGE_HELPER4, 2);
+    public static final ComponentName HELPER4_RECEIVER3 = buildReceiver(PACKAGE_HELPER4, 3);
 
     public static final String ACTION_SEND_BACK_START_TIME = "ACTION_SEND_BACK_START_TIME";
 }
diff --git a/tests/process/src/android/os/cts/process/common/Message.java b/tests/process/src/android/os/cts/process/common/Message.java
index 75f4b8c..f7b77739 100644
--- a/tests/process/src/android/os/cts/process/common/Message.java
+++ b/tests/process/src/android/os/cts/process/common/Message.java
@@ -16,7 +16,10 @@
 package android.os.cts.process.common;
 
 import android.annotation.Nullable;
+import android.content.Context;
 import android.os.Parcelable;
+import android.os.Process;
+import android.os.SystemClock;
 
 import com.android.internal.util.DataClass;
 
@@ -32,6 +35,30 @@
     public Message() {
     }
 
+    public void fillInBasicInfo(Context context) {
+        // codegen fails for whatever reason if it's after the fields.
+        packageName = context.getPackageName();
+        processName = Process.myProcessName();
+
+        applicationContextClassName = context.getApplicationContext().getClass().getCanonicalName();
+
+        nowElapsedRealtime = SystemClock.elapsedRealtime();
+        nowUptimeMillis = SystemClock.uptimeMillis();
+
+        startElapsedRealtime = Process.getStartElapsedRealtime();
+        startUptimeMillis = Process.getStartUptimeMillis();
+        startRequestedElapsedRealtime = Process.getStartRequestedElapsedRealtime();
+        startRequestedUptimeMillis = Process.getStartRequestedUptimeMillis();
+    }
+
+    @Nullable
+    public String packageName;
+    @Nullable
+    public String applicationClassName;
+    @Nullable
+    public String receiverClassName;
+    @Nullable
+    public String applicationContextClassName;
     @Nullable
     public String processName;
     public long startElapsedRealtime;
@@ -63,6 +90,10 @@
         // String fieldNameToString() { ... }
 
         return "Message { " +
+                "packageName = " + packageName + ", " +
+                "applicationClassName = " + applicationClassName + ", " +
+                "receiverClassName = " + receiverClassName + ", " +
+                "applicationContextClassName = " + applicationContextClassName + ", " +
                 "processName = " + processName + ", " +
                 "startElapsedRealtime = " + startElapsedRealtime + ", " +
                 "startUptimeMillis = " + startUptimeMillis + ", " +
@@ -79,9 +110,17 @@
         // You can override field parcelling by defining methods like:
         // void parcelFieldName(Parcel dest, int flags) { ... }
 
-        byte flg = 0;
-        if (processName != null) flg |= 0x1;
-        dest.writeByte(flg);
+        int flg = 0;
+        if (packageName != null) flg |= 0x1;
+        if (applicationClassName != null) flg |= 0x2;
+        if (receiverClassName != null) flg |= 0x4;
+        if (applicationContextClassName != null) flg |= 0x8;
+        if (processName != null) flg |= 0x10;
+        dest.writeInt(flg);
+        if (packageName != null) dest.writeString(packageName);
+        if (applicationClassName != null) dest.writeString(applicationClassName);
+        if (receiverClassName != null) dest.writeString(receiverClassName);
+        if (applicationContextClassName != null) dest.writeString(applicationContextClassName);
         if (processName != null) dest.writeString(processName);
         dest.writeLong(startElapsedRealtime);
         dest.writeLong(startUptimeMillis);
@@ -102,8 +141,12 @@
         // You can override field unparcelling by defining methods like:
         // static FieldType unparcelFieldName(Parcel in) { ... }
 
-        byte flg = in.readByte();
-        String _processName = (flg & 0x1) == 0 ? null : in.readString();
+        int flg = in.readInt();
+        String _packageName = (flg & 0x1) == 0 ? null : in.readString();
+        String _applicationClassName = (flg & 0x2) == 0 ? null : in.readString();
+        String _receiverClassName = (flg & 0x4) == 0 ? null : in.readString();
+        String _applicationContextClassName = (flg & 0x8) == 0 ? null : in.readString();
+        String _processName = (flg & 0x10) == 0 ? null : in.readString();
         long _startElapsedRealtime = in.readLong();
         long _startUptimeMillis = in.readLong();
         long _startRequestedElapsedRealtime = in.readLong();
@@ -111,6 +154,10 @@
         long _nowElapsedRealtime = in.readLong();
         long _nowUptimeMillis = in.readLong();
 
+        this.packageName = _packageName;
+        this.applicationClassName = _applicationClassName;
+        this.receiverClassName = _receiverClassName;
+        this.applicationContextClassName = _applicationContextClassName;
         this.processName = _processName;
         this.startElapsedRealtime = _startElapsedRealtime;
         this.startUptimeMillis = _startUptimeMillis;
@@ -137,10 +184,10 @@
     };
 
     @DataClass.Generated(
-            time = 1636764125263L,
+            time = 1639154672715L,
             codegenVersion = "1.0.23",
             sourceFile = "cts/tests/process/src/android/os/cts/process/common/Message.java",
-            inputSignatures = "public @android.annotation.Nullable java.lang.String processName\npublic  long startElapsedRealtime\npublic  long startUptimeMillis\npublic  long startRequestedElapsedRealtime\npublic  long startRequestedUptimeMillis\npublic  long nowElapsedRealtime\npublic  long nowUptimeMillis\nclass Message extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genConstructor=false, genSetters=false, genToString=true, genAidl=false)")
+            inputSignatures = "public @android.annotation.Nullable java.lang.String packageName\npublic @android.annotation.Nullable java.lang.String applicationClassName\npublic @android.annotation.Nullable java.lang.String receiverClassName\npublic @android.annotation.Nullable java.lang.String applicationContextClassName\npublic @android.annotation.Nullable java.lang.String processName\npublic  long startElapsedRealtime\npublic  long startUptimeMillis\npublic  long startRequestedElapsedRealtime\npublic  long startRequestedUptimeMillis\npublic  long nowElapsedRealtime\npublic  long nowUptimeMillis\npublic  void fillInBasicInfo(android.content.Context)\nclass Message extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genConstructor=false, genSetters=false, genToString=true, genAidl=false)")
     @Deprecated
     private void __metadata() {}
 
diff --git a/tests/process/src/android/os/cts/process/helper/MyReceiver.java b/tests/process/src/android/os/cts/process/helper/Application0.java
similarity index 85%
copy from tests/process/src/android/os/cts/process/helper/MyReceiver.java
copy to tests/process/src/android/os/cts/process/helper/Application0.java
index 7aad3b5..8c9a860 100644
--- a/tests/process/src/android/os/cts/process/helper/MyReceiver.java
+++ b/tests/process/src/android/os/cts/process/helper/Application0.java
@@ -1,5 +1,5 @@
 /*
- * Copyright 2021 The Android Open Source Project
+ * Copyright 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.
@@ -13,8 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-
 package android.os.cts.process.helper;
 
-public class MyReceiver extends BaseReceiver {
+public class Application0 extends BaseApplication {
 }
diff --git a/tests/process/src/android/os/cts/process/helper/MyReceiver.java b/tests/process/src/android/os/cts/process/helper/Application1.java
similarity index 85%
copy from tests/process/src/android/os/cts/process/helper/MyReceiver.java
copy to tests/process/src/android/os/cts/process/helper/Application1.java
index 7aad3b5..4deae2c 100644
--- a/tests/process/src/android/os/cts/process/helper/MyReceiver.java
+++ b/tests/process/src/android/os/cts/process/helper/Application1.java
@@ -1,5 +1,5 @@
 /*
- * Copyright 2021 The Android Open Source Project
+ * Copyright 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.
@@ -13,8 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-
 package android.os.cts.process.helper;
 
-public class MyReceiver extends BaseReceiver {
+public class Application1 extends BaseApplication {
 }
diff --git a/tests/process/src/android/os/cts/process/helper/MyReceiver.java b/tests/process/src/android/os/cts/process/helper/Application1b.java
similarity index 85%
copy from tests/process/src/android/os/cts/process/helper/MyReceiver.java
copy to tests/process/src/android/os/cts/process/helper/Application1b.java
index 7aad3b5..28efd23 100644
--- a/tests/process/src/android/os/cts/process/helper/MyReceiver.java
+++ b/tests/process/src/android/os/cts/process/helper/Application1b.java
@@ -1,5 +1,5 @@
 /*
- * Copyright 2021 The Android Open Source Project
+ * Copyright 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.
@@ -13,8 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-
 package android.os.cts.process.helper;
 
-public class MyReceiver extends BaseReceiver {
+public class Application1b extends BaseApplication {
 }
diff --git a/tests/process/src/android/os/cts/process/helper/MyReceiver.java b/tests/process/src/android/os/cts/process/helper/Application1c.java
similarity index 85%
copy from tests/process/src/android/os/cts/process/helper/MyReceiver.java
copy to tests/process/src/android/os/cts/process/helper/Application1c.java
index 7aad3b5..b083e26 100644
--- a/tests/process/src/android/os/cts/process/helper/MyReceiver.java
+++ b/tests/process/src/android/os/cts/process/helper/Application1c.java
@@ -1,5 +1,5 @@
 /*
- * Copyright 2021 The Android Open Source Project
+ * Copyright 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.
@@ -13,8 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-
 package android.os.cts.process.helper;
 
-public class MyReceiver extends BaseReceiver {
+public class Application1c extends BaseApplication {
 }
diff --git a/tests/process/src/android/os/cts/process/helper/MyReceiver.java b/tests/process/src/android/os/cts/process/helper/Application2.java
similarity index 85%
copy from tests/process/src/android/os/cts/process/helper/MyReceiver.java
copy to tests/process/src/android/os/cts/process/helper/Application2.java
index 7aad3b5..396ef79 100644
--- a/tests/process/src/android/os/cts/process/helper/MyReceiver.java
+++ b/tests/process/src/android/os/cts/process/helper/Application2.java
@@ -1,5 +1,5 @@
 /*
- * Copyright 2021 The Android Open Source Project
+ * Copyright 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.
@@ -13,8 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-
 package android.os.cts.process.helper;
 
-public class MyReceiver extends BaseReceiver {
+public class Application2 extends BaseApplication {
 }
diff --git a/tests/process/src/android/os/cts/process/helper/MyReceiver.java b/tests/process/src/android/os/cts/process/helper/Application2b.java
similarity index 85%
copy from tests/process/src/android/os/cts/process/helper/MyReceiver.java
copy to tests/process/src/android/os/cts/process/helper/Application2b.java
index 7aad3b5..c88300f 100644
--- a/tests/process/src/android/os/cts/process/helper/MyReceiver.java
+++ b/tests/process/src/android/os/cts/process/helper/Application2b.java
@@ -1,5 +1,5 @@
 /*
- * Copyright 2021 The Android Open Source Project
+ * Copyright 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.
@@ -13,8 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-
 package android.os.cts.process.helper;
 
-public class MyReceiver extends BaseReceiver {
+public class Application2b extends BaseApplication {
 }
diff --git a/tests/process/src/android/os/cts/process/helper/MyReceiver.java b/tests/process/src/android/os/cts/process/helper/Application3.java
similarity index 85%
copy from tests/process/src/android/os/cts/process/helper/MyReceiver.java
copy to tests/process/src/android/os/cts/process/helper/Application3.java
index 7aad3b5..b238f51 100644
--- a/tests/process/src/android/os/cts/process/helper/MyReceiver.java
+++ b/tests/process/src/android/os/cts/process/helper/Application3.java
@@ -1,5 +1,5 @@
 /*
- * Copyright 2021 The Android Open Source Project
+ * Copyright 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.
@@ -13,8 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-
 package android.os.cts.process.helper;
 
-public class MyReceiver extends BaseReceiver {
+public class Application3 extends BaseApplication {
 }
diff --git a/tests/process/src/android/os/cts/process/helper/MyReceiver.java b/tests/process/src/android/os/cts/process/helper/Application3b.java
similarity index 85%
copy from tests/process/src/android/os/cts/process/helper/MyReceiver.java
copy to tests/process/src/android/os/cts/process/helper/Application3b.java
index 7aad3b5..ab7eef9 100644
--- a/tests/process/src/android/os/cts/process/helper/MyReceiver.java
+++ b/tests/process/src/android/os/cts/process/helper/Application3b.java
@@ -1,5 +1,5 @@
 /*
- * Copyright 2021 The Android Open Source Project
+ * Copyright 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.
@@ -13,8 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-
 package android.os.cts.process.helper;
 
-public class MyReceiver extends BaseReceiver {
+public class Application3b extends BaseApplication {
 }
diff --git a/tests/process/src/android/os/cts/process/helper/BaseApplication.java b/tests/process/src/android/os/cts/process/helper/BaseApplication.java
new file mode 100644
index 0000000..a9aa1f9
--- /dev/null
+++ b/tests/process/src/android/os/cts/process/helper/BaseApplication.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright 2021 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 android.os.cts.process.helper;
+
+import android.app.Application;
+import android.os.cts.process.common.Consts;
+import android.os.cts.process.common.Message;
+import android.util.Log;
+
+import com.android.compatibility.common.util.BroadcastMessenger;
+
+public abstract class BaseApplication extends Application {
+    @Override
+    public void onCreate() {
+        super.onCreate();
+
+        Log.i(Consts.TAG, "onCreate: this=" + this);
+
+        sendBackApplicationCreated();
+    }
+
+    private void sendBackApplicationCreated() {
+        Message m = new Message();
+
+        m.fillInBasicInfo(this);
+
+        m.applicationClassName = this.getClass().getCanonicalName();
+
+        BroadcastMessenger.send(this, Consts.TAG, m);
+    }
+}
diff --git a/tests/process/src/android/os/cts/process/helper/BaseReceiver.java b/tests/process/src/android/os/cts/process/helper/BaseReceiver.java
index 2217238..0e4f8e1 100644
--- a/tests/process/src/android/os/cts/process/helper/BaseReceiver.java
+++ b/tests/process/src/android/os/cts/process/helper/BaseReceiver.java
@@ -18,8 +18,6 @@
 import android.content.BroadcastReceiver;
 import android.content.Context;
 import android.content.Intent;
-import android.os.Process;
-import android.os.SystemClock;
 import android.os.cts.process.common.Consts;
 import android.os.cts.process.common.Message;
 import android.util.Log;
@@ -37,18 +35,12 @@
         }
     }
 
-    private static void sendBackStartTime(Context context) {
+    private void sendBackStartTime(Context context) {
         Message m = new Message();
 
-        m.processName = Process.myProcessName();
+        m.fillInBasicInfo(context);
 
-        m.nowElapsedRealtime = SystemClock.elapsedRealtime();
-        m.nowUptimeMillis = SystemClock.uptimeMillis();
-
-        m.startElapsedRealtime = Process.getStartElapsedRealtime();
-        m.startUptimeMillis = Process.getStartUptimeMillis();
-        m.startRequestedElapsedRealtime = Process.getStartRequestedElapsedRealtime();
-        m.startRequestedUptimeMillis = Process.getStartRequestedUptimeMillis();
+        m.receiverClassName = this.getClass().getCanonicalName();
 
         BroadcastMessenger.send(context, Consts.TAG, m);
     }
diff --git a/tests/process/src/android/os/cts/process/helper/MyReceiver.java b/tests/process/src/android/os/cts/process/helper/MyReceiver0.java
similarity index 92%
rename from tests/process/src/android/os/cts/process/helper/MyReceiver.java
rename to tests/process/src/android/os/cts/process/helper/MyReceiver0.java
index 7aad3b5..a5237a2 100644
--- a/tests/process/src/android/os/cts/process/helper/MyReceiver.java
+++ b/tests/process/src/android/os/cts/process/helper/MyReceiver0.java
@@ -16,5 +16,5 @@
 
 package android.os.cts.process.helper;
 
-public class MyReceiver extends BaseReceiver {
+public class MyReceiver0 extends BaseReceiver {
 }
diff --git a/tests/process/src/android/os/cts/process/helper/MyReceiver.java b/tests/process/src/android/os/cts/process/helper/MyReceiver1.java
similarity index 92%
copy from tests/process/src/android/os/cts/process/helper/MyReceiver.java
copy to tests/process/src/android/os/cts/process/helper/MyReceiver1.java
index 7aad3b5..a349fab 100644
--- a/tests/process/src/android/os/cts/process/helper/MyReceiver.java
+++ b/tests/process/src/android/os/cts/process/helper/MyReceiver1.java
@@ -16,5 +16,5 @@
 
 package android.os.cts.process.helper;
 
-public class MyReceiver extends BaseReceiver {
+public class MyReceiver1 extends BaseReceiver {
 }
diff --git a/tests/process/src/android/os/cts/process/helper/MyReceiver2.java b/tests/process/src/android/os/cts/process/helper/MyReceiver2.java
index 238b8a8..d3b41a3 100644
--- a/tests/process/src/android/os/cts/process/helper/MyReceiver2.java
+++ b/tests/process/src/android/os/cts/process/helper/MyReceiver2.java
@@ -16,8 +16,5 @@
 
 package android.os.cts.process.helper;
 
-/**
- * Broadacst receiver that runs on a secondary process.
- */
 public class MyReceiver2 extends BaseReceiver {
 }
diff --git a/tests/process/src/android/os/cts/process/helper/MyReceiver.java b/tests/process/src/android/os/cts/process/helper/MyReceiver3.java
similarity index 92%
copy from tests/process/src/android/os/cts/process/helper/MyReceiver.java
copy to tests/process/src/android/os/cts/process/helper/MyReceiver3.java
index 7aad3b5..075b796 100644
--- a/tests/process/src/android/os/cts/process/helper/MyReceiver.java
+++ b/tests/process/src/android/os/cts/process/helper/MyReceiver3.java
@@ -16,5 +16,5 @@
 
 package android.os.cts.process.helper;
 
-public class MyReceiver extends BaseReceiver {
+public class MyReceiver3 extends BaseReceiver {
 }