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"));
+ }
+}