Loading services/core/java/com/android/server/am/ActivityManagerService.java +18 −0 Original line number Diff line number Diff line Loading @@ -377,6 +377,7 @@ import com.android.server.SystemServiceManager; import com.android.server.ThreadPriorityBooster; import com.android.server.UserspaceRebootLogger; import com.android.server.Watchdog; import com.android.server.am.ComponentAliasResolver.Resolution; import com.android.server.am.LowMemDetector.MemFactor; import com.android.server.appop.AppOpsService; import com.android.server.compat.PlatformCompat; Loading Loading @@ -12777,9 +12778,26 @@ public class ActivityManagerService extends IActivityManager.Stub } } } // Replace the alias receivers with their targets. if (newReceivers != null) { for (int i = newReceivers.size() - 1; i >= 0; i--) { final ResolveInfo ri = newReceivers.get(i); final Resolution<ResolveInfo> resolution = mComponentAliasResolver .resolveReceiver(intent, ri, resolvedType, pmFlags, user, callingUid); if (resolution == null) { // It was an alias, but the target was not found. newReceivers.remove(i); continue; } if (resolution.isAlias()) { newReceivers.set(i, resolution.getTarget()); } } } if (newReceivers != null && newReceivers.size() == 0) { newReceivers = null; } if (receivers == null) { receivers = newReceivers; } else if (newReceivers != null) { services/core/java/com/android/server/am/ComponentAliasResolver.java +114 −44 Original line number Diff line number Diff line Loading @@ -27,7 +27,9 @@ import android.content.pm.ResolveInfo; import android.content.pm.ServiceInfo; import android.os.Binder; import android.os.UserHandle; import android.text.TextUtils; import android.util.ArrayMap; import android.util.Log; import android.util.Slog; import com.android.internal.annotations.GuardedBy; Loading @@ -38,6 +40,7 @@ import com.android.server.LocalServices; import java.io.PrintWriter; import java.util.List; import java.util.Objects; import java.util.function.Supplier; /** * Manages and handles component aliases, which is an experimental feature. Loading Loading @@ -72,6 +75,13 @@ public class ComponentAliasResolver { private static final String ALIAS_FILTER_ACTION = "android.intent.action.EXPERIMENTAL_IS_ALIAS"; private static final String META_DATA_ALIAS_TARGET = "alias_target"; private static final int PACKAGE_QUERY_FLAGS = PackageManager.MATCH_UNINSTALLED_PACKAGES | PackageManager.MATCH_ANY_USER | PackageManager.MATCH_DIRECT_BOOT_AWARE | PackageManager.MATCH_DIRECT_BOOT_UNAWARE | PackageManager.GET_META_DATA; public ComponentAliasResolver(ActivityManagerService service) { mAm = service; mContext = service.mContext; Loading Loading @@ -151,24 +161,29 @@ public class ComponentAliasResolver { */ @GuardedBy("mLock") private void loadFromMetadataLocked() { if (DEBUG) Slog.d(TAG, "Scanning aliases..."); if (DEBUG) Slog.d(TAG, "Scanning service aliases..."); Intent i = new Intent(ALIAS_FILTER_ACTION); List<ResolveInfo> services = mContext.getPackageManager().queryIntentServicesAsUser( i, PackageManager.MATCH_UNINSTALLED_PACKAGES | PackageManager.MATCH_ANY_USER | PackageManager.MATCH_DIRECT_BOOT_AWARE | PackageManager.MATCH_DIRECT_BOOT_UNAWARE | PackageManager.GET_META_DATA, UserHandle.USER_SYSTEM); final List<ResolveInfo> services = mContext.getPackageManager().queryIntentServicesAsUser( i, PACKAGE_QUERY_FLAGS, UserHandle.USER_SYSTEM); extractAliases(services); if (DEBUG) Slog.d(TAG, "Scanning receiver aliases..."); final List<ResolveInfo> receivers = mContext.getPackageManager() .queryBroadcastReceiversAsUser(i, PACKAGE_QUERY_FLAGS, UserHandle.USER_SYSTEM); extractAliases(receivers); // TODO: Scan for other component types as well. } for (ResolveInfo ri : services) { private void extractAliases(List<ResolveInfo> components) { for (ResolveInfo ri : components) { final ComponentInfo ci = ri.getComponentInfo(); final ComponentName from = ci.getComponentName(); final ComponentName to = ComponentName.unflattenFromString( ci.metaData.getString(META_DATA_ALIAS_TARGET)); if (!validateComponentName(to)) { final ComponentName to = unflatten(ci.metaData.getString(META_DATA_ALIAS_TARGET)); if (to == null) { continue; } if (DEBUG) { Loading @@ -176,8 +191,6 @@ public class ComponentAliasResolver { } mFromTo.put(from, to); } // TODO: Scan for other component types as well. } /** Loading @@ -191,8 +204,11 @@ public class ComponentAliasResolver { if (DEBUG) Slog.d(TAG, "Loading aliases overrides ..."); for (String line : mOverrideString.split("\\,+")) { final String[] fields = line.split("\\:+", 2); final ComponentName from = ComponentName.unflattenFromString(fields[0]); if (!validateComponentName(from)) { if (TextUtils.isEmpty(fields[0])) { continue; } final ComponentName from = unflatten(fields[0]); if (from == null) { continue; } Loading @@ -200,8 +216,8 @@ public class ComponentAliasResolver { if (DEBUG) Slog.d(TAG, "" + from.flattenToShortString() + " [removed]"); mFromTo.remove(from); } else { final ComponentName to = ComponentName.unflattenFromString(fields[1]); if (!validateComponentName(to)) { final ComponentName to = unflatten(fields[1]); if (to == null) { continue; } Loading @@ -214,12 +230,13 @@ public class ComponentAliasResolver { } } private boolean validateComponentName(ComponentName cn) { private ComponentName unflatten(String name) { final ComponentName cn = ComponentName.unflattenFromString(name); if (cn != null) { return true; return cn; } Slog.e(TAG, "Invalid component name detected: " + cn); return false; Slog.e(TAG, "Invalid component name detected: " + name); return null; } /** Loading Loading @@ -277,10 +294,9 @@ public class ComponentAliasResolver { } } @Nullable public Resolution<ComponentName> resolveService( @NonNull Intent service, @Nullable String resolvedType, int packageFlags, int userId, int callingUid) { @NonNull public Resolution<ComponentName> resolveComponentAlias( @NonNull Supplier<ComponentName> aliasSupplier) { final long identity = Binder.clearCallingIdentity(); try { synchronized (mLock) { Loading @@ -288,6 +304,31 @@ public class ComponentAliasResolver { return new Resolution<>(null, null); } final ComponentName alias = aliasSupplier.get(); final ComponentName target = mFromTo.get(alias); if (target != null) { if (DEBUG) { Exception stacktrace = null; if (Log.isLoggable(TAG, Log.VERBOSE)) { stacktrace = new RuntimeException("STACKTRACE"); } Slog.d(TAG, "Alias resolved: " + alias.flattenToShortString() + " -> " + target.flattenToShortString(), stacktrace); } } return new Resolution<>(alias, target); } } finally { Binder.restoreCallingIdentity(identity); } } @Nullable public Resolution<ComponentName> resolveService( @NonNull Intent service, @Nullable String resolvedType, int packageFlags, int userId, int callingUid) { Resolution<ComponentName> result = resolveComponentAlias(() -> { PackageManagerInternal pmi = LocalServices.getService(PackageManagerInternal.class); ResolveInfo rInfo = pmi.resolveService(service, Loading @@ -296,26 +337,55 @@ public class ComponentAliasResolver { if (sInfo == null) { return null; // Service not found. } final ComponentName alias = new ComponentName(sInfo.applicationInfo.packageName, sInfo.name); final ComponentName target = mFromTo.get(alias); return new ComponentName(sInfo.applicationInfo.packageName, sInfo.name); }); if (target != null) { // TODO: To make it consistent with resolveReceiver(), let's ensure the target service // is resolvable, and if not, return null. if (result != null && result.isAlias()) { // It's an alias. Keep the original intent, and rewrite it. service.setOriginalIntent(new Intent(service)); service.setPackage(null); service.setComponent(target); if (DEBUG) { Slog.d(TAG, "Alias resolved: " + alias.flattenToShortString() + " -> " + target.flattenToShortString()); } service.setComponent(result.getTarget()); } return new Resolution<>(alias, target); } } finally { Binder.restoreCallingIdentity(identity); return result; } @Nullable public Resolution<ResolveInfo> resolveReceiver(@NonNull Intent intent, @NonNull ResolveInfo receiver, @Nullable String resolvedType, int packageFlags, int userId, int callingUid) { // Resolve this alias. final Resolution<ComponentName> resolution = resolveComponentAlias(() -> receiver.activityInfo.getComponentName()); final ComponentName target = resolution.getTarget(); if (target == null) { return new Resolution<>(receiver, null); // It's not an alias. } // Convert the target component name to a ResolveInfo. final PackageManagerInternal pmi = LocalServices.getService(PackageManagerInternal.class); // Rewrite the intent to search the target intent. // - We don't actually rewrite the intent we deliver to the receiver here, which is what // resolveService() does, because this intent many be send to other receivers as well. // - But we don't have to do that here either, because the actual receiver component // will be set in BroadcastQueue anyway, before delivering the intent to each receiver. // - However, we're not able to set the original intent either, for the time being. Intent i = new Intent(intent); i.setPackage(null); i.setComponent(resolution.getTarget()); List<ResolveInfo> resolved = pmi.queryIntentReceivers(i, resolvedType, packageFlags, callingUid, userId); if (resolved == null || resolved.size() == 0) { // Target component not found. Slog.w(TAG, "Alias target " + target.flattenToShortString() + " not found"); return null; } return new Resolution<>(receiver, resolved.get(0)); } } tests/componentalias/AndroidManifest_service_aliases.xml +31 −0 Original line number Diff line number Diff line Loading @@ -46,5 +46,36 @@ <intent-filter><action android:name="android.intent.action.EXPERIMENTAL_IS_ALIAS" /></intent-filter> <intent-filter><action android:name="android.content.componentalias.tests.IS_ALIAS_04" /></intent-filter> </service> <receiver android:name=".b.Alias00" android:exported="true" android:enabled="true" > <meta-data android:name="alias_target" android:value="android.content.componentalias.tests/android.content.componentalias.tests.b.Target00" /> <intent-filter><action android:name="android.intent.action.EXPERIMENTAL_IS_ALIAS" /></intent-filter> <intent-filter><action android:name="android.content.componentalias.tests.IS_ALIAS_00" /></intent-filter> <intent-filter><action android:name="ACTION_BROADCAST" /></intent-filter> </receiver> <receiver android:name=".b.Alias01" android:exported="true" android:enabled="true" > <meta-data android:name="alias_target" android:value="android.content.componentalias.tests.sub1/android.content.componentalias.tests.b.Target01" /> <intent-filter><action android:name="android.intent.action.EXPERIMENTAL_IS_ALIAS" /></intent-filter> <intent-filter><action android:name="android.content.componentalias.tests.IS_ALIAS_01" /></intent-filter> <intent-filter><action android:name="ACTION_BROADCAST" /></intent-filter> </receiver> <receiver android:name=".b.Alias02" android:exported="true" android:enabled="true" > <meta-data android:name="alias_target" android:value="android.content.componentalias.tests.sub2/android.content.componentalias.tests.b.Target02" /> <intent-filter><action android:name="android.intent.action.EXPERIMENTAL_IS_ALIAS" /></intent-filter> <intent-filter><action android:name="android.content.componentalias.tests.IS_ALIAS_02" /></intent-filter> <intent-filter><action android:name="ACTION_BROADCAST" /></intent-filter> </receiver> <receiver android:name=".b.Alias03" android:exported="true" android:enabled="true" > <meta-data android:name="alias_target" android:value="android.content.componentalias.tests.sub1/android.content.componentalias.tests.b.Target03" /> <intent-filter><action android:name="android.intent.action.EXPERIMENTAL_IS_ALIAS" /></intent-filter> <intent-filter><action android:name="android.content.componentalias.tests.IS_ALIAS_03" /></intent-filter> <intent-filter><action android:name="ACTION_BROADCAST" /></intent-filter> </receiver> <receiver android:name=".b.Alias04" android:exported="true" android:enabled="true" > <meta-data android:name="alias_target" android:value="android.content.componentalias.tests.sub2/android.content.componentalias.tests.b.Target04" /> <intent-filter><action android:name="android.intent.action.EXPERIMENTAL_IS_ALIAS" /></intent-filter> <intent-filter><action android:name="android.content.componentalias.tests.IS_ALIAS_04" /></intent-filter> <intent-filter><action android:name="ACTION_BROADCAST" /></intent-filter> </receiver> </application> </manifest> tests/componentalias/AndroidManifest_service_targets.xml +16 −0 Original line number Diff line number Diff line Loading @@ -27,5 +27,21 @@ </service> <service android:name=".s.Target04" android:exported="true" android:enabled="true" > </service> <receiver android:name=".b.Target00" android:exported="true" android:enabled="true" > <intent-filter><action android:name="ACTION_BROADCAST" /></intent-filter> </receiver> <receiver android:name=".b.Target01" android:exported="true" android:enabled="true" > <intent-filter><action android:name="ACTION_BROADCAST" /></intent-filter> </receiver> <receiver android:name=".b.Target02" android:exported="true" android:enabled="true" > <intent-filter><action android:name="ACTION_BROADCAST" /></intent-filter> </receiver> <receiver android:name=".b.Target03" android:exported="true" android:enabled="true" > <intent-filter><action android:name="ACTION_BROADCAST" /></intent-filter> </receiver> <receiver android:name=".b.Target04" android:exported="true" android:enabled="true" > <intent-filter><action android:name="ACTION_BROADCAST" /></intent-filter> </receiver> </application> </manifest> tests/componentalias/src/android/content/componentalias/tests/BaseComponentAliasTest.java 0 → 100644 +93 −0 Original line number Diff line number Diff line /* * 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 android.content.ComponentName; import android.content.Context; import android.provider.DeviceConfig; import android.util.Log; import androidx.test.InstrumentationRegistry; import com.android.compatibility.common.util.DeviceConfigStateHelper; import com.android.compatibility.common.util.ShellUtils; import com.android.compatibility.common.util.TestUtils; import org.junit.AfterClass; import org.junit.Before; import java.util.function.Consumer; public class BaseComponentAliasTest { protected static final Context sContext = InstrumentationRegistry.getTargetContext(); protected 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(); } protected static void log(String message) { Log.i(ComponentAliasTestCommon.TAG, "[" + sContext.getPackageName() + "] " + message); } /** * Defines a test target. */ public static class Combo { public final ComponentName alias; public final ComponentName target; public final String action; public Combo(ComponentName alias, ComponentName target, String action) { this.alias = alias; this.target = target; this.action = action; } @Override public String toString() { return "Combo{" + "alias=" + toString(alias) + ", target=" + toString(target) + ", action='" + action + '\'' + '}'; } private static String toString(ComponentName cn) { return cn == null ? "[null]" : cn.flattenToShortString(); } public void apply(Consumer<Combo> callback) { log("Testing for: " + this); callback.accept(this); } } } Loading
services/core/java/com/android/server/am/ActivityManagerService.java +18 −0 Original line number Diff line number Diff line Loading @@ -377,6 +377,7 @@ import com.android.server.SystemServiceManager; import com.android.server.ThreadPriorityBooster; import com.android.server.UserspaceRebootLogger; import com.android.server.Watchdog; import com.android.server.am.ComponentAliasResolver.Resolution; import com.android.server.am.LowMemDetector.MemFactor; import com.android.server.appop.AppOpsService; import com.android.server.compat.PlatformCompat; Loading Loading @@ -12777,9 +12778,26 @@ public class ActivityManagerService extends IActivityManager.Stub } } } // Replace the alias receivers with their targets. if (newReceivers != null) { for (int i = newReceivers.size() - 1; i >= 0; i--) { final ResolveInfo ri = newReceivers.get(i); final Resolution<ResolveInfo> resolution = mComponentAliasResolver .resolveReceiver(intent, ri, resolvedType, pmFlags, user, callingUid); if (resolution == null) { // It was an alias, but the target was not found. newReceivers.remove(i); continue; } if (resolution.isAlias()) { newReceivers.set(i, resolution.getTarget()); } } } if (newReceivers != null && newReceivers.size() == 0) { newReceivers = null; } if (receivers == null) { receivers = newReceivers; } else if (newReceivers != null) {
services/core/java/com/android/server/am/ComponentAliasResolver.java +114 −44 Original line number Diff line number Diff line Loading @@ -27,7 +27,9 @@ import android.content.pm.ResolveInfo; import android.content.pm.ServiceInfo; import android.os.Binder; import android.os.UserHandle; import android.text.TextUtils; import android.util.ArrayMap; import android.util.Log; import android.util.Slog; import com.android.internal.annotations.GuardedBy; Loading @@ -38,6 +40,7 @@ import com.android.server.LocalServices; import java.io.PrintWriter; import java.util.List; import java.util.Objects; import java.util.function.Supplier; /** * Manages and handles component aliases, which is an experimental feature. Loading Loading @@ -72,6 +75,13 @@ public class ComponentAliasResolver { private static final String ALIAS_FILTER_ACTION = "android.intent.action.EXPERIMENTAL_IS_ALIAS"; private static final String META_DATA_ALIAS_TARGET = "alias_target"; private static final int PACKAGE_QUERY_FLAGS = PackageManager.MATCH_UNINSTALLED_PACKAGES | PackageManager.MATCH_ANY_USER | PackageManager.MATCH_DIRECT_BOOT_AWARE | PackageManager.MATCH_DIRECT_BOOT_UNAWARE | PackageManager.GET_META_DATA; public ComponentAliasResolver(ActivityManagerService service) { mAm = service; mContext = service.mContext; Loading Loading @@ -151,24 +161,29 @@ public class ComponentAliasResolver { */ @GuardedBy("mLock") private void loadFromMetadataLocked() { if (DEBUG) Slog.d(TAG, "Scanning aliases..."); if (DEBUG) Slog.d(TAG, "Scanning service aliases..."); Intent i = new Intent(ALIAS_FILTER_ACTION); List<ResolveInfo> services = mContext.getPackageManager().queryIntentServicesAsUser( i, PackageManager.MATCH_UNINSTALLED_PACKAGES | PackageManager.MATCH_ANY_USER | PackageManager.MATCH_DIRECT_BOOT_AWARE | PackageManager.MATCH_DIRECT_BOOT_UNAWARE | PackageManager.GET_META_DATA, UserHandle.USER_SYSTEM); final List<ResolveInfo> services = mContext.getPackageManager().queryIntentServicesAsUser( i, PACKAGE_QUERY_FLAGS, UserHandle.USER_SYSTEM); extractAliases(services); if (DEBUG) Slog.d(TAG, "Scanning receiver aliases..."); final List<ResolveInfo> receivers = mContext.getPackageManager() .queryBroadcastReceiversAsUser(i, PACKAGE_QUERY_FLAGS, UserHandle.USER_SYSTEM); extractAliases(receivers); // TODO: Scan for other component types as well. } for (ResolveInfo ri : services) { private void extractAliases(List<ResolveInfo> components) { for (ResolveInfo ri : components) { final ComponentInfo ci = ri.getComponentInfo(); final ComponentName from = ci.getComponentName(); final ComponentName to = ComponentName.unflattenFromString( ci.metaData.getString(META_DATA_ALIAS_TARGET)); if (!validateComponentName(to)) { final ComponentName to = unflatten(ci.metaData.getString(META_DATA_ALIAS_TARGET)); if (to == null) { continue; } if (DEBUG) { Loading @@ -176,8 +191,6 @@ public class ComponentAliasResolver { } mFromTo.put(from, to); } // TODO: Scan for other component types as well. } /** Loading @@ -191,8 +204,11 @@ public class ComponentAliasResolver { if (DEBUG) Slog.d(TAG, "Loading aliases overrides ..."); for (String line : mOverrideString.split("\\,+")) { final String[] fields = line.split("\\:+", 2); final ComponentName from = ComponentName.unflattenFromString(fields[0]); if (!validateComponentName(from)) { if (TextUtils.isEmpty(fields[0])) { continue; } final ComponentName from = unflatten(fields[0]); if (from == null) { continue; } Loading @@ -200,8 +216,8 @@ public class ComponentAliasResolver { if (DEBUG) Slog.d(TAG, "" + from.flattenToShortString() + " [removed]"); mFromTo.remove(from); } else { final ComponentName to = ComponentName.unflattenFromString(fields[1]); if (!validateComponentName(to)) { final ComponentName to = unflatten(fields[1]); if (to == null) { continue; } Loading @@ -214,12 +230,13 @@ public class ComponentAliasResolver { } } private boolean validateComponentName(ComponentName cn) { private ComponentName unflatten(String name) { final ComponentName cn = ComponentName.unflattenFromString(name); if (cn != null) { return true; return cn; } Slog.e(TAG, "Invalid component name detected: " + cn); return false; Slog.e(TAG, "Invalid component name detected: " + name); return null; } /** Loading Loading @@ -277,10 +294,9 @@ public class ComponentAliasResolver { } } @Nullable public Resolution<ComponentName> resolveService( @NonNull Intent service, @Nullable String resolvedType, int packageFlags, int userId, int callingUid) { @NonNull public Resolution<ComponentName> resolveComponentAlias( @NonNull Supplier<ComponentName> aliasSupplier) { final long identity = Binder.clearCallingIdentity(); try { synchronized (mLock) { Loading @@ -288,6 +304,31 @@ public class ComponentAliasResolver { return new Resolution<>(null, null); } final ComponentName alias = aliasSupplier.get(); final ComponentName target = mFromTo.get(alias); if (target != null) { if (DEBUG) { Exception stacktrace = null; if (Log.isLoggable(TAG, Log.VERBOSE)) { stacktrace = new RuntimeException("STACKTRACE"); } Slog.d(TAG, "Alias resolved: " + alias.flattenToShortString() + " -> " + target.flattenToShortString(), stacktrace); } } return new Resolution<>(alias, target); } } finally { Binder.restoreCallingIdentity(identity); } } @Nullable public Resolution<ComponentName> resolveService( @NonNull Intent service, @Nullable String resolvedType, int packageFlags, int userId, int callingUid) { Resolution<ComponentName> result = resolveComponentAlias(() -> { PackageManagerInternal pmi = LocalServices.getService(PackageManagerInternal.class); ResolveInfo rInfo = pmi.resolveService(service, Loading @@ -296,26 +337,55 @@ public class ComponentAliasResolver { if (sInfo == null) { return null; // Service not found. } final ComponentName alias = new ComponentName(sInfo.applicationInfo.packageName, sInfo.name); final ComponentName target = mFromTo.get(alias); return new ComponentName(sInfo.applicationInfo.packageName, sInfo.name); }); if (target != null) { // TODO: To make it consistent with resolveReceiver(), let's ensure the target service // is resolvable, and if not, return null. if (result != null && result.isAlias()) { // It's an alias. Keep the original intent, and rewrite it. service.setOriginalIntent(new Intent(service)); service.setPackage(null); service.setComponent(target); if (DEBUG) { Slog.d(TAG, "Alias resolved: " + alias.flattenToShortString() + " -> " + target.flattenToShortString()); } service.setComponent(result.getTarget()); } return new Resolution<>(alias, target); } } finally { Binder.restoreCallingIdentity(identity); return result; } @Nullable public Resolution<ResolveInfo> resolveReceiver(@NonNull Intent intent, @NonNull ResolveInfo receiver, @Nullable String resolvedType, int packageFlags, int userId, int callingUid) { // Resolve this alias. final Resolution<ComponentName> resolution = resolveComponentAlias(() -> receiver.activityInfo.getComponentName()); final ComponentName target = resolution.getTarget(); if (target == null) { return new Resolution<>(receiver, null); // It's not an alias. } // Convert the target component name to a ResolveInfo. final PackageManagerInternal pmi = LocalServices.getService(PackageManagerInternal.class); // Rewrite the intent to search the target intent. // - We don't actually rewrite the intent we deliver to the receiver here, which is what // resolveService() does, because this intent many be send to other receivers as well. // - But we don't have to do that here either, because the actual receiver component // will be set in BroadcastQueue anyway, before delivering the intent to each receiver. // - However, we're not able to set the original intent either, for the time being. Intent i = new Intent(intent); i.setPackage(null); i.setComponent(resolution.getTarget()); List<ResolveInfo> resolved = pmi.queryIntentReceivers(i, resolvedType, packageFlags, callingUid, userId); if (resolved == null || resolved.size() == 0) { // Target component not found. Slog.w(TAG, "Alias target " + target.flattenToShortString() + " not found"); return null; } return new Resolution<>(receiver, resolved.get(0)); } }
tests/componentalias/AndroidManifest_service_aliases.xml +31 −0 Original line number Diff line number Diff line Loading @@ -46,5 +46,36 @@ <intent-filter><action android:name="android.intent.action.EXPERIMENTAL_IS_ALIAS" /></intent-filter> <intent-filter><action android:name="android.content.componentalias.tests.IS_ALIAS_04" /></intent-filter> </service> <receiver android:name=".b.Alias00" android:exported="true" android:enabled="true" > <meta-data android:name="alias_target" android:value="android.content.componentalias.tests/android.content.componentalias.tests.b.Target00" /> <intent-filter><action android:name="android.intent.action.EXPERIMENTAL_IS_ALIAS" /></intent-filter> <intent-filter><action android:name="android.content.componentalias.tests.IS_ALIAS_00" /></intent-filter> <intent-filter><action android:name="ACTION_BROADCAST" /></intent-filter> </receiver> <receiver android:name=".b.Alias01" android:exported="true" android:enabled="true" > <meta-data android:name="alias_target" android:value="android.content.componentalias.tests.sub1/android.content.componentalias.tests.b.Target01" /> <intent-filter><action android:name="android.intent.action.EXPERIMENTAL_IS_ALIAS" /></intent-filter> <intent-filter><action android:name="android.content.componentalias.tests.IS_ALIAS_01" /></intent-filter> <intent-filter><action android:name="ACTION_BROADCAST" /></intent-filter> </receiver> <receiver android:name=".b.Alias02" android:exported="true" android:enabled="true" > <meta-data android:name="alias_target" android:value="android.content.componentalias.tests.sub2/android.content.componentalias.tests.b.Target02" /> <intent-filter><action android:name="android.intent.action.EXPERIMENTAL_IS_ALIAS" /></intent-filter> <intent-filter><action android:name="android.content.componentalias.tests.IS_ALIAS_02" /></intent-filter> <intent-filter><action android:name="ACTION_BROADCAST" /></intent-filter> </receiver> <receiver android:name=".b.Alias03" android:exported="true" android:enabled="true" > <meta-data android:name="alias_target" android:value="android.content.componentalias.tests.sub1/android.content.componentalias.tests.b.Target03" /> <intent-filter><action android:name="android.intent.action.EXPERIMENTAL_IS_ALIAS" /></intent-filter> <intent-filter><action android:name="android.content.componentalias.tests.IS_ALIAS_03" /></intent-filter> <intent-filter><action android:name="ACTION_BROADCAST" /></intent-filter> </receiver> <receiver android:name=".b.Alias04" android:exported="true" android:enabled="true" > <meta-data android:name="alias_target" android:value="android.content.componentalias.tests.sub2/android.content.componentalias.tests.b.Target04" /> <intent-filter><action android:name="android.intent.action.EXPERIMENTAL_IS_ALIAS" /></intent-filter> <intent-filter><action android:name="android.content.componentalias.tests.IS_ALIAS_04" /></intent-filter> <intent-filter><action android:name="ACTION_BROADCAST" /></intent-filter> </receiver> </application> </manifest>
tests/componentalias/AndroidManifest_service_targets.xml +16 −0 Original line number Diff line number Diff line Loading @@ -27,5 +27,21 @@ </service> <service android:name=".s.Target04" android:exported="true" android:enabled="true" > </service> <receiver android:name=".b.Target00" android:exported="true" android:enabled="true" > <intent-filter><action android:name="ACTION_BROADCAST" /></intent-filter> </receiver> <receiver android:name=".b.Target01" android:exported="true" android:enabled="true" > <intent-filter><action android:name="ACTION_BROADCAST" /></intent-filter> </receiver> <receiver android:name=".b.Target02" android:exported="true" android:enabled="true" > <intent-filter><action android:name="ACTION_BROADCAST" /></intent-filter> </receiver> <receiver android:name=".b.Target03" android:exported="true" android:enabled="true" > <intent-filter><action android:name="ACTION_BROADCAST" /></intent-filter> </receiver> <receiver android:name=".b.Target04" android:exported="true" android:enabled="true" > <intent-filter><action android:name="ACTION_BROADCAST" /></intent-filter> </receiver> </application> </manifest>
tests/componentalias/src/android/content/componentalias/tests/BaseComponentAliasTest.java 0 → 100644 +93 −0 Original line number Diff line number Diff line /* * 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 android.content.ComponentName; import android.content.Context; import android.provider.DeviceConfig; import android.util.Log; import androidx.test.InstrumentationRegistry; import com.android.compatibility.common.util.DeviceConfigStateHelper; import com.android.compatibility.common.util.ShellUtils; import com.android.compatibility.common.util.TestUtils; import org.junit.AfterClass; import org.junit.Before; import java.util.function.Consumer; public class BaseComponentAliasTest { protected static final Context sContext = InstrumentationRegistry.getTargetContext(); protected 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(); } protected static void log(String message) { Log.i(ComponentAliasTestCommon.TAG, "[" + sContext.getPackageName() + "] " + message); } /** * Defines a test target. */ public static class Combo { public final ComponentName alias; public final ComponentName target; public final String action; public Combo(ComponentName alias, ComponentName target, String action) { this.alias = alias; this.target = target; this.action = action; } @Override public String toString() { return "Combo{" + "alias=" + toString(alias) + ", target=" + toString(target) + ", action='" + action + '\'' + '}'; } private static String toString(ComponentName cn) { return cn == null ? "[null]" : cn.flattenToShortString(); } public void apply(Consumer<Combo> callback) { log("Testing for: " + this); callback.accept(this); } } }