Component Alias initial (rudimentary) prototype

See go/component-alias-prototype-overview for what it does.

This is disabled by default. Enable it via device config.

Test: atest ComponentAliasTests
Bug: 196254758
Change-Id: I2d177f6a475d878abbde55c3642c09da99dfea30
diff --git a/tests/componentalias/Android.bp b/tests/componentalias/Android.bp
new file mode 100644
index 0000000..15a680d
--- /dev/null
+++ b/tests/componentalias/Android.bp
@@ -0,0 +1,48 @@
+// 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.
+
+package {
+    default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+android_library {
+    name: "ComponentAliasTestsCommon",
+    srcs: [
+        "common/**/*.java",
+    ],
+    plugins: [
+        "staledataclass-annotation-processor",
+    ],
+    platform_apis: true, // We use hidden APIs in the test.
+}
+
+android_test {
+    name: "ComponentAliasTests",
+    static_libs: [
+        "androidx.test.rules",
+        "compatibility-device-util-axt",
+        "mockito-target-extended-minus-junit4",
+        "truth-prebuilt",
+        "ub-uiautomator",
+        "ComponentAliasTestsCommon",
+    ],
+    libs: ["android.test.base"],
+    srcs: [
+        "src/**/*.java",
+    ],
+    test_suites: [
+        "general-tests",
+    ],
+    platform_apis: true, // We use hidden APIs in the test.
+}
diff --git a/tests/componentalias/AndroidManifest.xml b/tests/componentalias/AndroidManifest.xml
new file mode 100755
index 0000000..893be8e
--- /dev/null
+++ b/tests/componentalias/AndroidManifest.xml
@@ -0,0 +1,29 @@
+<?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.content.componentalias.tests" >
+
+    <application>
+        <uses-library android:name="android.test.runner" />
+    </application>
+
+    <instrumentation
+        android:name="androidx.test.runner.AndroidJUnitRunner"
+        android:targetPackage="android.content.componentalias.tests" >
+    </instrumentation>
+</manifest>
diff --git a/tests/componentalias/AndroidTest.xml b/tests/componentalias/AndroidTest.xml
new file mode 100644
index 0000000..e2c37d2
--- /dev/null
+++ b/tests/componentalias/AndroidTest.xml
@@ -0,0 +1,35 @@
+<?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.
+-->
+<configuration>
+    <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
+        <option name="cleanup-apks" value="true" />
+        <option name="test-file-name" value="ComponentAliasTests.apk" />
+        <option name="test-file-name" value="ComponentAliasAppMain.apk" />
+        <option name="test-file-name" value="ComponentAliasAppSub1.apk" />
+        <option name="test-file-name" value="ComponentAliasAppSub2.apk" />
+    </target_preparer>
+    <target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer">
+        <!-- Exempt the helper APKs from the BG restriction, so they can start BG services. -->
+        <option name="run-command" value="cmd deviceidle whitelist +android.content.componentalias.tests.app" />
+        <option name="run-command" value="cmd deviceidle whitelist +android.content.componentalias.tests.app.sub1" />
+        <option name="run-command" value="cmd deviceidle whitelist +android.content.componentalias.tests.app.sub2" />
+    </target_preparer>
+    <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
+        <option name="package" value="android.content.componentalias.tests" />
+        <option name="runtime-hint" value="2m" />
+        <option name="isolated-storage" value="false" />
+    </test>
+</configuration>
diff --git a/tests/componentalias/OWNERS b/tests/componentalias/OWNERS
new file mode 100644
index 0000000..1073c81
--- /dev/null
+++ b/tests/componentalias/OWNERS
@@ -0,0 +1,2 @@
+omakoto@google.com
+yamasani@google.com
\ No newline at end of file
diff --git a/tests/componentalias/apps/Android.bp b/tests/componentalias/apps/Android.bp
new file mode 100644
index 0000000..b2cb0f7
--- /dev/null
+++ b/tests/componentalias/apps/Android.bp
@@ -0,0 +1,76 @@
+// 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.
+// Copyright (C) 2018 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 {
+    default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+// We build three helper APKs from the same source.
+// - The main APK (ComponentAliasAppMain), which contains both the alias and the target components.
+// - The sub APKs (ComponentAliasAppSub*), which only contains the target components.
+
+java_defaults {
+    name: "component_alias_app_defaults",
+    libs: ["android.test.base"],
+    sdk_version: "test_current",
+    srcs: [
+        "src/**/*.java",
+    ],
+    static_libs: [
+        "compatibility-device-util-axt",
+        "androidx.legacy_legacy-support-v4",
+        "androidx.test.rules",
+        "ComponentAliasTestsCommon",
+    ],
+}
+
+android_test_helper_app {
+    name: "ComponentAliasAppMain",
+    defaults: [
+        "component_alias_app_defaults",
+    ],
+    package_name: "android.content.componentalias.tests.app",
+    manifest: "AndroidManifest_main.xml",
+}
+
+android_test_helper_app {
+    name: "ComponentAliasAppSub1",
+    defaults: [
+        "component_alias_app_defaults",
+    ],
+    package_name: "android.content.componentalias.tests.app.sub1",
+    manifest: "AndroidManifest_sub.xml",
+}
+
+android_test_helper_app {
+    name: "ComponentAliasAppSub2",
+    defaults: [
+        "component_alias_app_defaults",
+    ],
+    package_name: "android.content.componentalias.tests.app.sub2",
+    manifest: "AndroidManifest_sub.xml",
+}
diff --git a/tests/componentalias/apps/AndroidManifest_main.xml b/tests/componentalias/apps/AndroidManifest_main.xml
new file mode 100755
index 0000000..2caef3e
--- /dev/null
+++ b/tests/componentalias/apps/AndroidManifest_main.xml
@@ -0,0 +1,67 @@
+<?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.content.componentalias.tests.app" >
+
+    <application>
+        <!--
+            Alias components.
+            Note aliases do not have the actual implementation, because they're never called
+            directly.
+        -->
+
+        <service android:name=".s.Alias01" android:exported="true" android:enabled="true" >
+            <meta-data android:name="alias_target" android:value="android.content.componentalias.tests.app.sub1/android.content.componentalias.tests.app.s.Target01" />
+            <intent-filter><action android:name="android.intent.action.EXPERIMENTAL_IS_ALIAS" /></intent-filter>
+            <intent-filter><action android:name="android.content.componentalias.tests.app.IS_ALIAS_01" /></intent-filter>
+        </service>
+        <service android:name=".s.Alias02" android:exported="true" android:enabled="true" >
+            <meta-data android:name="alias_target" android:value="android.content.componentalias.tests.app.sub2/android.content.componentalias.tests.app.s.Target02" />
+            <intent-filter><action android:name="android.intent.action.EXPERIMENTAL_IS_ALIAS" /></intent-filter>
+            <intent-filter><action android:name="android.content.componentalias.tests.app.IS_ALIAS_02" /></intent-filter>
+        </service>
+        <service android:name=".s.Alias03" android:exported="true" android:enabled="true" >
+            <meta-data android:name="alias_target" android:value="android.content.componentalias.tests.app.sub1/android.content.componentalias.tests.app.s.Target03" />
+            <intent-filter><action android:name="android.intent.action.EXPERIMENTAL_IS_ALIAS" /></intent-filter>
+            <intent-filter><action android:name="android.content.componentalias.tests.app.IS_ALIAS_03" /></intent-filter>
+        </service>
+        <service android:name=".s.Alias04" android:exported="true" android:enabled="true" >
+            <meta-data android:name="alias_target" android:value="android.content.componentalias.tests.app.sub2/android.content.componentalias.tests.app.s.Target04" />
+            <intent-filter><action android:name="android.intent.action.EXPERIMENTAL_IS_ALIAS" /></intent-filter>
+            <intent-filter><action android:name="android.content.componentalias.tests.app.IS_ALIAS_04" /></intent-filter>
+        </service>
+        <service android:name=".s.Alias05" android:exported="true" android:enabled="true" >
+            <meta-data android:name="alias_target" android:value="android.content.componentalias.tests.app.sub1/android.content.componentalias.tests.app.s.Target05" />
+            <intent-filter><action android:name="android.intent.action.EXPERIMENTAL_IS_ALIAS" /></intent-filter>
+            <intent-filter><action android:name="android.content.componentalias.tests.app.IS_ALIAS_05" /></intent-filter>
+        </service>
+
+        <!-- Target components -->
+
+        <service android:name=".s.Target01" android:exported="true" android:enabled="true" >
+        </service>
+        <service android:name=".s.Target02" android:exported="true" android:enabled="true" >
+        </service>
+        <service android:name=".s.Target03" android:exported="true" android:enabled="true" >
+        </service>
+        <service android:name=".s.Target04" android:exported="true" android:enabled="true" >
+        </service>
+        <service android:name=".s.Target05" android:exported="true" android:enabled="true" >
+        </service>
+    </application>
+</manifest>
diff --git a/tests/componentalias/apps/AndroidManifest_sub.xml b/tests/componentalias/apps/AndroidManifest_sub.xml
new file mode 100755
index 0000000..2ddd965
--- /dev/null
+++ b/tests/componentalias/apps/AndroidManifest_sub.xml
@@ -0,0 +1,35 @@
+<?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.content.componentalias.tests.app" >
+
+    <application>
+        <!-- Only contain the target components -->
+
+        <service android:name=".s.Target01" android:exported="true" android:enabled="true" >
+        </service>
+        <service android:name=".s.Target02" android:exported="true" android:enabled="true" >
+        </service>
+        <service android:name=".s.Target03" android:exported="true" android:enabled="true" >
+        </service>
+        <service android:name=".s.Target04" android:exported="true" android:enabled="true" >
+        </service>
+        <service android:name=".s.Target05" android:exported="true" android:enabled="true" >
+        </service>
+    </application>
+</manifest>
diff --git a/tests/componentalias/apps/src/android/content/componentalias/tests/app/s/BaseService.java b/tests/componentalias/apps/src/android/content/componentalias/tests/app/s/BaseService.java
new file mode 100644
index 0000000..fb67829
--- /dev/null
+++ b/tests/componentalias/apps/src/android/content/componentalias/tests/app/s/BaseService.java
@@ -0,0 +1,71 @@
+/*
+ * 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.
+ */
+package android.content.componentalias.tests.app.s;
+
+import static android.content.componentalias.tests.common.ComponentAliasTestCommon.TAG;
+import static android.content.componentalias.tests.common.ComponentAliasTestCommon.TEST_PACKAGE;
+
+import android.app.Service;
+import android.content.ComponentName;
+import android.content.Intent;
+import android.content.componentalias.tests.common.ComponentAliasMessage;
+import android.os.Binder;
+import android.os.IBinder;
+import android.util.Log;
+
+import com.android.compatibility.common.util.BroadcastMessenger;
+
+public class BaseService extends Service {
+    private String getMyIdentity() {
+        return (new ComponentName(this.getPackageName(), this.getClass().getCanonicalName()))
+                .flattenToShortString();
+    }
+
+    @Override
+    public int onStartCommand(Intent intent, int flags, int startId) {
+        Log.i(TAG, "onStartCommand: on " + getMyIdentity() + " intent=" + intent);
+        ComponentAliasMessage m = new ComponentAliasMessage()
+                .setSenderIdentity(getMyIdentity())
+                .setMethodName("onStartCommand")
+                .setIntent(intent);
+        BroadcastMessenger.send(this, TEST_PACKAGE, m);
+
+        return START_NOT_STICKY;
+    }
+
+    @Override
+    public void onDestroy() {
+        Log.i(TAG, "onDestroy: on " + getMyIdentity());
+
+        ComponentAliasMessage m = new ComponentAliasMessage()
+                .setSenderIdentity(getMyIdentity())
+                .setMethodName("onDestroy");
+        BroadcastMessenger.send(this, TEST_PACKAGE, m);
+    }
+
+    @Override
+    public IBinder onBind(Intent intent) {
+        Log.i(TAG, "onBind: on " + getMyIdentity() + " intent=" + intent);
+
+        ComponentAliasMessage m = new ComponentAliasMessage()
+                .setSenderIdentity(getMyIdentity())
+                .setMethodName("onBind")
+                .setIntent(intent);
+        BroadcastMessenger.send(this, TEST_PACKAGE, m);
+
+        return new Binder();
+    }
+}
diff --git a/tests/componentalias/apps/src/android/content/componentalias/tests/app/s/Target01.java b/tests/componentalias/apps/src/android/content/componentalias/tests/app/s/Target01.java
new file mode 100644
index 0000000..87e48cb
--- /dev/null
+++ b/tests/componentalias/apps/src/android/content/componentalias/tests/app/s/Target01.java
@@ -0,0 +1,19 @@
+/*
+ * 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.
+ */
+package android.content.componentalias.tests.app.s;
+
+public class Target01 extends BaseService {
+}
diff --git a/tests/componentalias/apps/src/android/content/componentalias/tests/app/s/Target02.java b/tests/componentalias/apps/src/android/content/componentalias/tests/app/s/Target02.java
new file mode 100644
index 0000000..0e8a6a8
--- /dev/null
+++ b/tests/componentalias/apps/src/android/content/componentalias/tests/app/s/Target02.java
@@ -0,0 +1,19 @@
+/*
+ * 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.
+ */
+package android.content.componentalias.tests.app.s;
+
+public class Target02 extends BaseService {
+}
diff --git a/tests/componentalias/apps/src/android/content/componentalias/tests/app/s/Target03.java b/tests/componentalias/apps/src/android/content/componentalias/tests/app/s/Target03.java
new file mode 100644
index 0000000..b7990bb
--- /dev/null
+++ b/tests/componentalias/apps/src/android/content/componentalias/tests/app/s/Target03.java
@@ -0,0 +1,19 @@
+/*
+ * 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.
+ */
+package android.content.componentalias.tests.app.s;
+
+public class Target03 extends BaseService {
+}
diff --git a/tests/componentalias/apps/src/android/content/componentalias/tests/app/s/Target04.java b/tests/componentalias/apps/src/android/content/componentalias/tests/app/s/Target04.java
new file mode 100644
index 0000000..0e51a6b
--- /dev/null
+++ b/tests/componentalias/apps/src/android/content/componentalias/tests/app/s/Target04.java
@@ -0,0 +1,19 @@
+/*
+ * 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.
+ */
+package android.content.componentalias.tests.app.s;
+
+public class Target04 extends BaseService {
+}
diff --git a/tests/componentalias/apps/src/android/content/componentalias/tests/app/s/Target05.java b/tests/componentalias/apps/src/android/content/componentalias/tests/app/s/Target05.java
new file mode 100644
index 0000000..77060cd
--- /dev/null
+++ b/tests/componentalias/apps/src/android/content/componentalias/tests/app/s/Target05.java
@@ -0,0 +1,19 @@
+/*
+ * 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.
+ */
+package android.content.componentalias.tests.app.s;
+
+public class Target05 extends BaseService {
+}
diff --git a/tests/componentalias/common/android/content/componentalias/tests/common/ComponentAliasMessage.java b/tests/componentalias/common/android/content/componentalias/tests/common/ComponentAliasMessage.java
new file mode 100644
index 0000000..399c070
--- /dev/null
+++ b/tests/componentalias/common/android/content/componentalias/tests/common/ComponentAliasMessage.java
@@ -0,0 +1,215 @@
+/*
+ * 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.
+ */
+package android.content.componentalias.tests.common;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.ComponentName;
+import android.content.Intent;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import com.android.internal.util.DataClass;
+
+/**
+ * Parcelabe containing a "message" that's meant to be delivered via BroadcastMessenger.
+ *
+ * To add a new field, just add a private member field, and run:
+ * codegen $ANDROID_BUILD_TOP/frameworks/base/tests/componentalias/common/android/content/componentalias/tests/common/ComponentAliasMessage.java
+ */
+@DataClass(
+        genConstructor = false,
+        genSetters = true,
+        genToString = true,
+        genAidl = false)
+public final class ComponentAliasMessage implements Parcelable {
+    public ComponentAliasMessage() {
+    }
+
+    @Nullable
+    private String mMessage;
+
+    @Nullable
+    private String mMethodName;
+
+    @Nullable
+    private String mSenderIdentity;
+
+    @Nullable
+    private Intent mIntent;
+
+    @Nullable
+    private ComponentName mComponent;
+
+
+
+    // Code below generated by codegen v1.0.23.
+    //
+    // DO NOT MODIFY!
+    // CHECKSTYLE:OFF Generated code
+    //
+    // To regenerate run:
+    // $ codegen $ANDROID_BUILD_TOP/frameworks/base/tests/componentalias/common/android/content/componentalias/tests/common/ComponentAliasMessage.java
+    //
+    // To exclude the generated code from IntelliJ auto-formatting enable (one-time):
+    //   Settings > Editor > Code Style > Formatter Control
+    //@formatter:off
+
+
+    @DataClass.Generated.Member
+    public @Nullable String getMessage() {
+        return mMessage;
+    }
+
+    @DataClass.Generated.Member
+    public @Nullable String getMethodName() {
+        return mMethodName;
+    }
+
+    @DataClass.Generated.Member
+    public @Nullable String getSenderIdentity() {
+        return mSenderIdentity;
+    }
+
+    @DataClass.Generated.Member
+    public @Nullable Intent getIntent() {
+        return mIntent;
+    }
+
+    @DataClass.Generated.Member
+    public @Nullable ComponentName getComponent() {
+        return mComponent;
+    }
+
+    @DataClass.Generated.Member
+    public @NonNull ComponentAliasMessage setMessage(@NonNull String value) {
+        mMessage = value;
+        return this;
+    }
+
+    @DataClass.Generated.Member
+    public @NonNull ComponentAliasMessage setMethodName(@NonNull String value) {
+        mMethodName = value;
+        return this;
+    }
+
+    @DataClass.Generated.Member
+    public @NonNull ComponentAliasMessage setSenderIdentity(@NonNull String value) {
+        mSenderIdentity = value;
+        return this;
+    }
+
+    @DataClass.Generated.Member
+    public @NonNull ComponentAliasMessage setIntent(@NonNull Intent value) {
+        mIntent = value;
+        return this;
+    }
+
+    @DataClass.Generated.Member
+    public @NonNull ComponentAliasMessage setComponent(@NonNull ComponentName value) {
+        mComponent = value;
+        return this;
+    }
+
+    @Override
+    @DataClass.Generated.Member
+    public String toString() {
+        // You can override field toString logic by defining methods like:
+        // String fieldNameToString() { ... }
+
+        return "ComponentAliasMessage { " +
+                "message = " + mMessage + ", " +
+                "methodName = " + mMethodName + ", " +
+                "senderIdentity = " + mSenderIdentity + ", " +
+                "intent = " + mIntent + ", " +
+                "component = " + mComponent +
+        " }";
+    }
+
+    @Override
+    @DataClass.Generated.Member
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
+        // You can override field parcelling by defining methods like:
+        // void parcelFieldName(Parcel dest, int flags) { ... }
+
+        byte flg = 0;
+        if (mMessage != null) flg |= 0x1;
+        if (mMethodName != null) flg |= 0x2;
+        if (mSenderIdentity != null) flg |= 0x4;
+        if (mIntent != null) flg |= 0x8;
+        if (mComponent != null) flg |= 0x10;
+        dest.writeByte(flg);
+        if (mMessage != null) dest.writeString(mMessage);
+        if (mMethodName != null) dest.writeString(mMethodName);
+        if (mSenderIdentity != null) dest.writeString(mSenderIdentity);
+        if (mIntent != null) dest.writeTypedObject(mIntent, flags);
+        if (mComponent != null) dest.writeTypedObject(mComponent, flags);
+    }
+
+    @Override
+    @DataClass.Generated.Member
+    public int describeContents() { return 0; }
+
+    /** @hide */
+    @SuppressWarnings({"unchecked", "RedundantCast"})
+    @DataClass.Generated.Member
+    /* package-private */ ComponentAliasMessage(@NonNull Parcel in) {
+        // You can override field unparcelling by defining methods like:
+        // static FieldType unparcelFieldName(Parcel in) { ... }
+
+        byte flg = in.readByte();
+        String message = (flg & 0x1) == 0 ? null : in.readString();
+        String methodName = (flg & 0x2) == 0 ? null : in.readString();
+        String senderIdentity = (flg & 0x4) == 0 ? null : in.readString();
+        Intent intent = (flg & 0x8) == 0 ? null : (Intent) in.readTypedObject(Intent.CREATOR);
+        ComponentName component = (flg & 0x10) == 0 ? null : (ComponentName) in.readTypedObject(ComponentName.CREATOR);
+
+        this.mMessage = message;
+        this.mMethodName = methodName;
+        this.mSenderIdentity = senderIdentity;
+        this.mIntent = intent;
+        this.mComponent = component;
+
+        // onConstructed(); // You can define this method to get a callback
+    }
+
+    @DataClass.Generated.Member
+    public static final @NonNull Parcelable.Creator<ComponentAliasMessage> CREATOR
+            = new Parcelable.Creator<ComponentAliasMessage>() {
+        @Override
+        public ComponentAliasMessage[] newArray(int size) {
+            return new ComponentAliasMessage[size];
+        }
+
+        @Override
+        public ComponentAliasMessage createFromParcel(@NonNull Parcel in) {
+            return new ComponentAliasMessage(in);
+        }
+    };
+
+    @DataClass.Generated(
+            time = 1629137098129L,
+            codegenVersion = "1.0.23",
+            sourceFile = "frameworks/base/tests/componentalias/common/android/content/componentalias/tests/common/ComponentAliasMessage.java",
+            inputSignatures = "private @android.annotation.Nullable java.lang.String mMessage\nprivate @android.annotation.Nullable java.lang.String mMethodName\nprivate @android.annotation.Nullable java.lang.String mSenderIdentity\nprivate @android.annotation.Nullable android.content.Intent mIntent\nprivate @android.annotation.Nullable android.content.ComponentName mComponent\nclass ComponentAliasMessage extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genConstructor=false, genSetters=true, genToString=true, genAidl=false)")
+    @Deprecated
+    private void __metadata() {}
+
+
+    //@formatter:on
+    // End of generated code
+
+}
diff --git a/tests/componentalias/common/android/content/componentalias/tests/common/ComponentAliasTestCommon.java b/tests/componentalias/common/android/content/componentalias/tests/common/ComponentAliasTestCommon.java
new file mode 100644
index 0000000..f306072
--- /dev/null
+++ b/tests/componentalias/common/android/content/componentalias/tests/common/ComponentAliasTestCommon.java
@@ -0,0 +1,29 @@
+/*
+ * 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.
+ */
+package android.content.componentalias.tests.common;
+
+public final class ComponentAliasTestCommon {
+    private ComponentAliasTestCommon() {
+    }
+
+    public static final String TAG = "ComponentAliasTest";
+
+    public static final String TEST_PACKAGE = "android.content.componentalias.tests";
+
+    public static final String APP_PACKAGE = "android.content.componentalias.tests.app";
+    public static final String SUB1_PACKAGE = "android.content.componentalias.tests.app.sub1";
+    public static final String SUB2_PACKAGE = "android.content.componentalias.tests.app.sub2";
+}
diff --git a/tests/componentalias/common/com/android/compatibility/common/util/BroadcastMessenger.java b/tests/componentalias/common/com/android/compatibility/common/util/BroadcastMessenger.java
new file mode 100644
index 0000000..b6dc09f
--- /dev/null
+++ b/tests/componentalias/common/com/android/compatibility/common/util/BroadcastMessenger.java
@@ -0,0 +1,125 @@
+/*
+ * 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.
+ */
+package com.android.compatibility.common.util;
+
+import android.annotation.NonNull;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.os.Parcelable;
+import android.util.Log;
+
+import com.android.internal.annotations.GuardedBy;
+
+import java.util.ArrayList;
+
+/**
+ * Provides a one-way communication mechanism using a Parcelable as a payload, via broadcasts.
+ *
+ * TODO: Move it to compatibility-device-util-axt.
+ */
+public final class BroadcastMessenger {
+    private static final String TAG = "BroadcastMessenger";
+
+    private static final String ACTION_MESSAGE =
+            "com.android.compatibility.common.util.BroadcastMessenger.ACTION_MESSAGE";
+    private static final String EXTRA_MESSAGE =
+            "com.android.compatibility.common.util.BroadcastMessenger.EXTRA_MESSAGE";
+
+    /** Send a message to the {@link Receiver} in a given package. */
+    public static <T extends Parcelable> void send(@NonNull Context context,
+            @NonNull String receiverPackage,
+            @NonNull T message) {
+        final Intent i = new Intent(ACTION_MESSAGE);
+        i.putExtra(EXTRA_MESSAGE, message);
+        i.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
+        i.setPackage(receiverPackage);
+
+        Log.d(TAG, "Sending: " + message);
+        context.sendBroadcast(i);
+    }
+
+    /**
+     * Receive messages from the test app.
+     */
+    public static final class Receiver<T extends Parcelable> implements AutoCloseable {
+        private final Context mContext;
+        @GuardedBy("mMessages")
+        private final ArrayList<T> mMessages = new ArrayList<>();
+        private boolean mRegistered;
+
+        private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
+            @Override
+            public void onReceive(Context context, Intent intent) {
+                if (!ACTION_MESSAGE.equals(intent.getAction())) {
+                    throw new RuntimeException("Unknown message received: " + intent);
+                }
+                final T message = intent.getParcelableExtra(EXTRA_MESSAGE);
+                Log.d(TAG, "Received: " + message);
+                synchronized (mMessages) {
+                    mMessages.add(message);
+                    mMessages.notifyAll();
+                }
+            }
+        };
+
+        /**
+         * Constructor.
+         */
+        public Receiver(@NonNull Context context) {
+            mContext = context;
+
+            final IntentFilter fi = new IntentFilter(ACTION_MESSAGE);
+            context.registerReceiver(mReceiver, fi);
+            mRegistered = true;
+        }
+
+        @Override
+        public void close() {
+            if (mRegistered) {
+                mContext.unregisterReceiver(mReceiver);
+                mRegistered = false;
+            }
+        }
+
+        /**
+         * Receive the next message with a 60 second timeout.
+         */
+        @NonNull
+        public T waitForNextMessage() throws Exception {
+            return waitForNextMessage(60_000);
+        }
+
+        /**
+         * Receive the next message.
+         */
+        @NonNull
+        public T waitForNextMessage(long timeoutMillis) throws Exception {
+            synchronized (mMessages) {
+                final long timeout = System.currentTimeMillis() + timeoutMillis;
+                while (mMessages.size() == 0) {
+                    final long wait = timeout - System.currentTimeMillis();
+                    if (wait <= 0) {
+                        throw new RuntimeException("Timeout waiting for the next message");
+                    }
+                    mMessages.wait(wait);
+                }
+                return mMessages.remove(0);
+            }
+        }
+    }
+}
diff --git a/tests/componentalias/src/android/content/componentalias/tests/ComponentAliasServiceTest.java b/tests/componentalias/src/android/content/componentalias/tests/ComponentAliasServiceTest.java
new file mode 100644
index 0000000..1de6277
--- /dev/null
+++ b/tests/componentalias/src/android/content/componentalias/tests/ComponentAliasServiceTest.java
@@ -0,0 +1,278 @@
+/*
+ * 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.
+ */
+
+package android.content.componentalias.tests;
+
+import static android.content.Context.BIND_AUTO_CREATE;
+import static android.content.componentalias.tests.common.ComponentAliasTestCommon.APP_PACKAGE;
+import static android.content.componentalias.tests.common.ComponentAliasTestCommon.SUB1_PACKAGE;
+import static android.content.componentalias.tests.common.ComponentAliasTestCommon.SUB2_PACKAGE;
+import static android.content.componentalias.tests.common.ComponentAliasTestCommon.TAG;
+import static android.content.componentalias.tests.common.ComponentAliasTestCommon.TEST_PACKAGE;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.content.componentalias.tests.common.ComponentAliasMessage;
+import android.os.IBinder;
+import android.provider.DeviceConfig;
+import android.util.Log;
+
+import com.android.compatibility.common.util.BroadcastMessenger;
+import com.android.compatibility.common.util.BroadcastMessenger.Receiver;
+import com.android.compatibility.common.util.DeviceConfigStateHelper;
+import com.android.compatibility.common.util.ShellUtils;
+import com.android.compatibility.common.util.TestUtils;
+
+import androidx.test.InstrumentationRegistry;
+
+import org.junit.AfterClass;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Ignore;
+import org.junit.Test;
+
+/**
+ * Test for the experimental "Component alias" feature.
+ *
+ * Note this test exercises the relevant APIs, but don't actually check if the aliases are
+ * resolved.
+ *
+ * Note all the helper APKs are battery-exempted (via AndroidTest.xml), so they can run
+ * BG services.
+ */
+public class ComponentAliasServiceTest {
+
+    private static final Context sContext = InstrumentationRegistry.getTargetContext();
+
+    private static final DeviceConfigStateHelper sDeviceConfig = new DeviceConfigStateHelper(
+            DeviceConfig.NAMESPACE_ACTIVITY_MANAGER);
+    @Before
+    public void enableComponentAlias() throws Exception {
+        sDeviceConfig.set("component_alias_overrides", "");
+        sDeviceConfig.set("enable_experimental_component_alias", "true");
+
+        // Device config propagation happens on a handler, so we need to wait for AM to
+        // actually set it.
+        TestUtils.waitUntil("Wait until component alias is actually enabled", () -> {
+            return ShellUtils.runShellCommand("dumpsys activity component-alias")
+                    .indexOf("Enabled: true") > 0;
+        });
+    }
+
+    @AfterClass
+    public static void restoreDeviceConfig() throws Exception {
+        sDeviceConfig.close();
+    }
+
+    /**
+     * Service connection used throughout the tests. It sends a message for each callback via
+     * the messenger.
+     */
+    private static final ServiceConnection sServiceConnection = new ServiceConnection() {
+        @Override
+        public void onServiceConnected(ComponentName name, IBinder service) {
+            Log.w(TAG, "onServiceConnected: " + name);
+
+            ComponentAliasMessage m = new ComponentAliasMessage()
+                    .setSenderIdentity("sServiceConnection")
+                    .setMethodName("onServiceConnected")
+                    .setComponent(name);
+
+            BroadcastMessenger.send(sContext, TEST_PACKAGE, m);
+        }
+
+        @Override
+        public void onServiceDisconnected(ComponentName name) {
+            Log.w(TAG, "onServiceDisconnected: " + name);
+
+            ComponentAliasMessage m = new ComponentAliasMessage()
+                    .setSenderIdentity("sServiceConnection")
+                    .setMethodName("onServiceDisconnected")
+                    .setComponent(name);
+
+            BroadcastMessenger.send(sContext, TEST_PACKAGE, m);
+        }
+
+        @Override
+        public void onBindingDied(ComponentName name) {
+            Log.w(TAG, "onBindingDied: " + name);
+
+            ComponentAliasMessage m = new ComponentAliasMessage()
+                    .setSenderIdentity("sServiceConnection")
+                    .setMethodName("onBindingDied");
+
+            BroadcastMessenger.send(sContext, TEST_PACKAGE, m);
+        }
+
+        @Override
+        public void onNullBinding(ComponentName name) {
+            Log.w(TAG, "onNullBinding: " + name);
+
+            ComponentAliasMessage m = new ComponentAliasMessage()
+                    .setSenderIdentity("sServiceConnection")
+                    .setMethodName("onNullBinding");
+
+            BroadcastMessenger.send(sContext, TEST_PACKAGE, m);
+        }
+    };
+
+    private void testStartAndStopService_common(
+            Intent originalIntent,
+            ComponentName componentNameForClient,
+            ComponentName componentNameForTarget) throws Exception {
+
+        ComponentAliasMessage m;
+
+        try (Receiver<ComponentAliasMessage> receiver = new Receiver<>(sContext)) {
+            // Start the service.
+            ComponentName result = sContext.startService(originalIntent);
+            assertThat(result).isEqualTo(componentNameForClient);
+
+            // Check
+            m = receiver.waitForNextMessage();
+
+            assertThat(m.getMethodName()).isEqualTo("onStartCommand");
+            // The app sees the rewritten intent.
+            assertThat(m.getIntent().getComponent()).isEqualTo(componentNameForTarget);
+
+            // Verify the original intent.
+            assertThat(m.getIntent().getOriginalIntent().getComponent())
+                    .isEqualTo(originalIntent.getComponent());
+            assertThat(m.getIntent().getOriginalIntent().getPackage())
+                    .isEqualTo(originalIntent.getPackage());
+
+            // Stop the service.
+            sContext.stopService(originalIntent);
+
+            // Check
+            m = receiver.waitForNextMessage();
+
+            assertThat(m.getMethodName()).isEqualTo("onDestroy");
+        }
+    }
+
+    @Test
+    public void testStartAndStopService_explicitComponentName() throws Exception {
+        Intent i = new Intent().setComponent(
+                new ComponentName(APP_PACKAGE, APP_PACKAGE + ".s.Alias01"));
+
+        ComponentName alias = new ComponentName(APP_PACKAGE, APP_PACKAGE + ".s.Alias01");
+        ComponentName target = new ComponentName(SUB1_PACKAGE, APP_PACKAGE + ".s.Target01");
+
+        testStartAndStopService_common(i, alias, target);
+    }
+
+    @Test
+    public void testStartAndStopService_explicitPackageName() throws Exception {
+        Intent i = new Intent().setPackage(APP_PACKAGE);
+        i.setAction(APP_PACKAGE + ".IS_ALIAS_02");
+
+        ComponentName alias = new ComponentName(APP_PACKAGE, APP_PACKAGE + ".s.Alias02");
+        ComponentName target = new ComponentName(SUB2_PACKAGE, APP_PACKAGE + ".s.Target02");
+
+        testStartAndStopService_common(i, alias, target);
+    }
+
+    @Test
+    public void testStartAndStopService_override() throws Exception {
+        Intent i = new Intent().setPackage(APP_PACKAGE);
+        i.setAction(APP_PACKAGE + ".IS_ALIAS_02");
+
+        ComponentName alias = new ComponentName(APP_PACKAGE, APP_PACKAGE + ".s.Alias02");
+
+        // Note, alias02 originally points at sub*2* package, but we override it in this test.
+        ComponentName target = new ComponentName(SUB1_PACKAGE, APP_PACKAGE + ".s.Target02");
+
+        sDeviceConfig.set("component_alias_overrides",
+                alias.flattenToShortString() + ":" + target.flattenToShortString());
+
+        TestUtils.waitUntil("Wait until component alias is actually enabled", () -> {
+            return ShellUtils.runShellCommand("dumpsys activity component-alias")
+                    .indexOf(alias.flattenToShortString() + " -> " + target.flattenToShortString())
+                    > 0;
+        });
+
+
+        testStartAndStopService_common(i, alias, target);
+    }
+
+    private void testBindAndUnbindService_common(
+            Intent originalIntent,
+            ComponentName componentNameForClient,
+            ComponentName componentNameForTarget) throws Exception {
+        ComponentAliasMessage m;
+
+        try (Receiver<ComponentAliasMessage> receiver = new Receiver<>(sContext)) {
+            // Bind to the service.
+            assertThat(sContext.bindService(
+                    originalIntent, sServiceConnection, BIND_AUTO_CREATE)).isTrue();
+
+            // Check the target side behavior.
+            m = receiver.waitForNextMessage();
+
+            assertThat(m.getMethodName()).isEqualTo("onBind");
+            // The app sees the rewritten intent.
+            assertThat(m.getIntent().getComponent()).isEqualTo(componentNameForTarget);
+
+            // Verify the original intent.
+            assertThat(m.getIntent().getOriginalIntent().getComponent())
+                    .isEqualTo(originalIntent.getComponent());
+            assertThat(m.getIntent().getOriginalIntent().getPackage())
+                    .isEqualTo(originalIntent.getPackage());
+
+            // Check the client side behavior.
+            m = receiver.waitForNextMessage();
+
+            assertThat(m.getMethodName()).isEqualTo("onServiceConnected");
+            // The app sees the rewritten intent.
+            assertThat(m.getComponent()).isEqualTo(componentNameForClient);
+
+            // Unbind.
+            sContext.unbindService(sServiceConnection);
+
+            // Check the target side behavior.
+            m = receiver.waitForNextMessage();
+
+            assertThat(m.getMethodName()).isEqualTo("onDestroy");
+
+            // Note onServiceDisconnected() won't be called in this case.
+        }
+    }
+
+    @Test
+    public void testBindService_explicitComponentName() throws Exception {
+        Intent i = new Intent().setComponent(
+                new ComponentName(APP_PACKAGE, APP_PACKAGE + ".s.Alias01"));
+
+        testBindAndUnbindService_common(i,
+                new ComponentName(APP_PACKAGE, APP_PACKAGE + ".s.Alias01"),
+                new ComponentName(SUB1_PACKAGE, APP_PACKAGE + ".s.Target01"));
+    }
+
+    @Test
+    public void testBindService_explicitPackageName() throws Exception {
+        Intent i = new Intent().setPackage(APP_PACKAGE);
+        i.setAction(APP_PACKAGE + ".IS_ALIAS_02");
+
+        testBindAndUnbindService_common(i,
+                new ComponentName(APP_PACKAGE, APP_PACKAGE + ".s.Alias02"),
+                new ComponentName(SUB2_PACKAGE, APP_PACKAGE + ".s.Target02"));
+    }
+}