Loading packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionDeviceDiscoveryService.java +6 −1 Original line number Diff line number Diff line Loading @@ -257,7 +257,12 @@ public class CompanionDeviceDiscoveryService extends Service { // Stop BLE scanning. if (mBleScanCallback != null) { try { mBleScanner.stopScan(mBleScanCallback); } catch (IllegalStateException e) { Slog.e(TAG, "Unable to stop BLE scanner. The scanner is already" + " turned off or Bluetooth is disabled."); } } Handler.getMain().removeCallbacks(mTimeoutRunnable); Loading services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java +0 −4 Original line number Diff line number Diff line Loading @@ -251,8 +251,6 @@ public class CompanionDeviceManagerService extends SystemService { for (AssociationInfo association : associationsForPackage) { mDisassociationProcessor.disassociate(association.getId()); } mCompanionAppBinder.onPackageChanged(userId); } // Clear observable UUIDs for the package. Loading @@ -268,8 +266,6 @@ public class CompanionDeviceManagerService extends SystemService { mAssociationStore.getAssociationsByPackage(userId, packageName); if (!associations.isEmpty()) { mCompanionExemptionProcessor.exemptPackage(userId, packageName, false); mCompanionAppBinder.onPackageChanged(userId); } } Loading services/companion/java/com/android/server/companion/association/AssociationDiskStore.java +21 −11 Original line number Diff line number Diff line Loading @@ -280,7 +280,7 @@ public final class AssociationDiskStore { } @NonNull private static Associations readAssociationsFromInputStream(@UserIdInt int userId, public static Associations readAssociationsFromInputStream(@UserIdInt int userId, @NonNull InputStream in, @NonNull String rootTag) throws XmlPullParserException, IOException { final TypedXmlPullParser parser = Xml.resolvePullParser(in); Loading @@ -296,10 +296,15 @@ public final class AssociationDiskStore { case 1: while (true) { parser.nextTag(); if (isEndOfTag(parser, rootTag)) { break; } if (isStartOfTag(parser, XML_TAG_ASSOCIATIONS)) { associations = readAssociationsV1(parser, userId); } else if (isEndOfTag(parser, rootTag)) { break; } else { Slog.e(TAG, "Unexpected tag " + parser.getName() + " inside <" + rootTag + "> for user " + userId); XmlUtils.skipCurrentTag(parser); } } break; Loading @@ -314,7 +319,7 @@ public final class AssociationDiskStore { writeToFileSafely(file, out -> { final TypedXmlSerializer serializer = Xml.resolveSerializer(out); serializer.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true); serializer.startDocument(null, true); serializer.startDocument("UTF-8", true); serializer.startTag(null, XML_TAG_STATE); writeIntAttribute(serializer, XML_ATTR_PERSISTENCE_VERSION, CURRENT_PERSISTENCE_VERSION); Loading Loading @@ -424,13 +429,18 @@ public final class AssociationDiskStore { while (true) { parser.nextTag(); if (isEndOfTag(parser, XML_TAG_ASSOCIATIONS)) break; if (!isStartOfTag(parser, XML_TAG_ASSOCIATION)) continue; if (isEndOfTag(parser, XML_TAG_ASSOCIATIONS)) { break; } if (isStartOfTag(parser, XML_TAG_ASSOCIATION)) { AssociationInfo association = readAssociationV1(parser, userId); associations.addAssociation(association); maxId = Math.max(maxId, association.getId()); } else { Slog.e(TAG, "Unexpected tag " + parser.getName() + " inside <" + XML_TAG_ASSOCIATIONS + "> for user " + userId); XmlUtils.skipCurrentTag(parser); } } associations.setMaxId(maxId); Loading services/companion/java/com/android/server/companion/devicepresence/CompanionAppBinder.java +55 −34 Original line number Diff line number Diff line Loading @@ -16,6 +16,7 @@ package com.android.server.companion.devicepresence; import android.Manifest; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SuppressLint; Loading @@ -25,14 +26,16 @@ import android.companion.CompanionDeviceService; import android.companion.DevicePresenceEvent; import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; import android.content.pm.ServiceInfo; import android.os.Handler; import android.util.Pair; import android.util.Slog; import com.android.internal.annotations.GuardedBy; import com.android.internal.infra.PerUser; import com.android.server.companion.CompanionDeviceManagerService; import com.android.server.companion.utils.PackageUtils; import java.io.PrintWriter; import java.util.ArrayList; Loading Loading @@ -70,12 +73,16 @@ import java.util.Set; public class CompanionAppBinder { private static final String TAG = "CDM_CompanionAppBinder"; private static final Intent COMPANION_SERVICE_INTENT = new Intent(CompanionDeviceService.SERVICE_INTERFACE); private static final String PROPERTY_PRIMARY_TAG = "android.companion.PROPERTY_PRIMARY_COMPANION_DEVICE_SERVICE"; private static final long REBIND_TIMEOUT = 10 * 1000; // 10 sec @NonNull private final Context mContext; @NonNull private final CompanionServicesRegister mCompanionServicesRegister; @NonNull @GuardedBy("mBoundCompanionApplications") Loading @@ -87,20 +94,10 @@ public class CompanionAppBinder { public CompanionAppBinder(@NonNull Context context) { mContext = context; mCompanionServicesRegister = new CompanionServicesRegister(); mBoundCompanionApplications = new HashMap<>(); mScheduledForRebindingCompanionApplications = new HashSet<>(); } /** * On package changed. */ public void onPackageChanged(@UserIdInt int userId) { // Note: To invalidate the user space for simplicity. We could alternatively manage each // package, but that would easily cause errors if one case is mis-handled. mCompanionServicesRegister.invalidate(userId); } /** * CDM binds to the companion app. */ Loading @@ -109,8 +106,9 @@ public class CompanionAppBinder { Slog.i(TAG, "Binding user=[" + userId + "], package=[" + packageName + "], isSelfManaged=[" + isSelfManaged + "]..."); final List<ComponentName> companionServices = mCompanionServicesRegister.forPackage(userId, packageName); final List<ComponentName> companionServices = getCompanionServiceComponentsForPackage( mContext, packageName, userId); if (companionServices.isEmpty()) { Slog.e(TAG, "Can not bind companion applications u" + userId + "/" + packageName + ": " + "eligible CompanionDeviceService not found.\n" Loading Loading @@ -299,28 +297,51 @@ public class CompanionAppBinder { return connectors != null ? connectors.get(0) : null; } private class CompanionServicesRegister extends PerUser<Map<String, List<ComponentName>>> { @Override @NonNull public synchronized Map<String, List<ComponentName>> forUser( @UserIdInt int userId) { return super.forUser(userId); /** * @return list of {@link CompanionDeviceService}-s per package for a given user. * Services marked as "primary" would always appear at the head of the lists, *before* * all non-primary services. */ private @NonNull List<ComponentName> getCompanionServiceComponentsForPackage( @NonNull Context context, @NonNull String packageName, @UserIdInt int userId) { final PackageManager pm = context.getPackageManager(); final List<ResolveInfo> companionServices = pm.queryIntentServicesAsUser( COMPANION_SERVICE_INTENT, PackageManager.ResolveInfoFlags.of(0), userId); final List<ComponentName> componentNames = new ArrayList<>(); for (ResolveInfo resolveInfo : companionServices) { final ServiceInfo service = resolveInfo.serviceInfo; final ComponentName componentName = service.getComponentName(); if (!componentName.getPackageName().equals(packageName)) continue; final boolean requiresPermission = Manifest.permission.BIND_COMPANION_DEVICE_SERVICE .equals(resolveInfo.serviceInfo.permission); if (!requiresPermission) { Slog.w(TAG, "CompanionDeviceService " + service.getComponentName().flattenToShortString() + " must require " + "android.permission.BIND_COMPANION_DEVICE_SERVICE"); break; } if (isPrimaryCompanionDeviceService(pm, componentName, userId)) { // "Primary" service should be at the head of the list. componentNames.add(0, componentName); } else { componentNames.add(componentName); } @NonNull synchronized List<ComponentName> forPackage( @UserIdInt int userId, @NonNull String packageName) { return forUser(userId).getOrDefault(packageName, Collections.emptyList()); } synchronized void invalidate(@UserIdInt int userId) { remove(userId); return componentNames; } @Override @NonNull protected final Map<String, List<ComponentName>> create(@UserIdInt int userId) { return PackageUtils.getCompanionServicesForUser(mContext, userId); private boolean isPrimaryCompanionDeviceService(@NonNull PackageManager pm, @NonNull ComponentName componentName, @UserIdInt int userId) { try { return pm.getPropertyAsUser(PROPERTY_PRIMARY_TAG, componentName.getPackageName(), componentName.getClassName(), userId).getBoolean(); } catch (PackageManager.NameNotFoundException e) { return false; } } } services/companion/java/com/android/server/companion/utils/PackageUtils.java +1 −70 Original line number Diff line number Diff line Loading @@ -26,24 +26,19 @@ import static com.android.internal.R.array.config_companionDevicePackages; import static com.android.internal.R.array.config_companionPermSyncEnabledCerts; import static com.android.internal.R.array.config_companionPermSyncEnabledPackages; import android.Manifest; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.UserIdInt; import android.app.AppOpsManager; import android.app.ecm.EnhancedConfirmationManager; import android.companion.CompanionDeviceService; import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.pm.ApplicationInfo; import android.content.pm.FeatureInfo; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; import android.content.pm.PackageManager.PackageInfoFlags; import android.content.pm.PackageManager.ResolveInfoFlags; import android.content.pm.PackageManagerInternal; import android.content.pm.ResolveInfo; import android.content.pm.ServiceInfo; import android.content.pm.Signature; import android.os.Binder; import android.os.Process; Loading @@ -52,11 +47,7 @@ import android.util.Slog; import com.android.internal.util.ArrayUtils; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; /** Loading @@ -66,11 +57,6 @@ public final class PackageUtils { private static final String TAG = "CDM_PackageUtils"; private static final Intent COMPANION_SERVICE_INTENT = new Intent(CompanionDeviceService.SERVICE_INTERFACE); private static final String PROPERTY_PRIMARY_TAG = "android.companion.PROPERTY_PRIMARY_COMPANION_DEVICE_SERVICE"; /** * Get package info */ Loading Loading @@ -118,61 +104,6 @@ public final class PackageUtils { + " in manifest to use this API"); } /** * @return list of {@link CompanionDeviceService}-s per package for a given user. * Services marked as "primary" would always appear at the head of the lists, *before* * all non-primary services. */ public static @NonNull Map<String, List<ComponentName>> getCompanionServicesForUser( @NonNull Context context, @UserIdInt int userId) { final PackageManager pm = context.getPackageManager(); final List<ResolveInfo> companionServices = pm.queryIntentServicesAsUser( COMPANION_SERVICE_INTENT, ResolveInfoFlags.of(0), userId); final Map<String, List<ComponentName>> packageNameToServiceInfoList = new HashMap<>(companionServices.size()); for (ResolveInfo resolveInfo : companionServices) { final ServiceInfo service = resolveInfo.serviceInfo; final boolean requiresPermission = Manifest.permission.BIND_COMPANION_DEVICE_SERVICE .equals(resolveInfo.serviceInfo.permission); if (!requiresPermission) { Slog.w(TAG, "CompanionDeviceService " + service.getComponentName().flattenToShortString() + " must require " + "android.permission.BIND_COMPANION_DEVICE_SERVICE"); continue; } // We'll need to prepend "primary" services, while appending the other (non-primary) // services to the list. final ArrayList<ComponentName> services = (ArrayList<ComponentName>) packageNameToServiceInfoList.computeIfAbsent( service.packageName, it -> new ArrayList<>(1)); final ComponentName componentName = service.getComponentName(); if (isPrimaryCompanionDeviceService(pm, componentName, userId)) { // "Primary" service should be at the head of the list. services.add(0, componentName); } else { services.add(componentName); } } return packageNameToServiceInfoList; } private static boolean isPrimaryCompanionDeviceService(@NonNull PackageManager pm, @NonNull ComponentName componentName, @UserIdInt int userId) { try { return pm.getPropertyAsUser(PROPERTY_PRIMARY_TAG, componentName.getPackageName(), componentName.getClassName(), userId).getBoolean(); } catch (PackageManager.NameNotFoundException e) { return false; } } /** * Check if the package is allowlisted in the overlay config. * For this we'll check to config arrays: Loading Loading
packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionDeviceDiscoveryService.java +6 −1 Original line number Diff line number Diff line Loading @@ -257,7 +257,12 @@ public class CompanionDeviceDiscoveryService extends Service { // Stop BLE scanning. if (mBleScanCallback != null) { try { mBleScanner.stopScan(mBleScanCallback); } catch (IllegalStateException e) { Slog.e(TAG, "Unable to stop BLE scanner. The scanner is already" + " turned off or Bluetooth is disabled."); } } Handler.getMain().removeCallbacks(mTimeoutRunnable); Loading
services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java +0 −4 Original line number Diff line number Diff line Loading @@ -251,8 +251,6 @@ public class CompanionDeviceManagerService extends SystemService { for (AssociationInfo association : associationsForPackage) { mDisassociationProcessor.disassociate(association.getId()); } mCompanionAppBinder.onPackageChanged(userId); } // Clear observable UUIDs for the package. Loading @@ -268,8 +266,6 @@ public class CompanionDeviceManagerService extends SystemService { mAssociationStore.getAssociationsByPackage(userId, packageName); if (!associations.isEmpty()) { mCompanionExemptionProcessor.exemptPackage(userId, packageName, false); mCompanionAppBinder.onPackageChanged(userId); } } Loading
services/companion/java/com/android/server/companion/association/AssociationDiskStore.java +21 −11 Original line number Diff line number Diff line Loading @@ -280,7 +280,7 @@ public final class AssociationDiskStore { } @NonNull private static Associations readAssociationsFromInputStream(@UserIdInt int userId, public static Associations readAssociationsFromInputStream(@UserIdInt int userId, @NonNull InputStream in, @NonNull String rootTag) throws XmlPullParserException, IOException { final TypedXmlPullParser parser = Xml.resolvePullParser(in); Loading @@ -296,10 +296,15 @@ public final class AssociationDiskStore { case 1: while (true) { parser.nextTag(); if (isEndOfTag(parser, rootTag)) { break; } if (isStartOfTag(parser, XML_TAG_ASSOCIATIONS)) { associations = readAssociationsV1(parser, userId); } else if (isEndOfTag(parser, rootTag)) { break; } else { Slog.e(TAG, "Unexpected tag " + parser.getName() + " inside <" + rootTag + "> for user " + userId); XmlUtils.skipCurrentTag(parser); } } break; Loading @@ -314,7 +319,7 @@ public final class AssociationDiskStore { writeToFileSafely(file, out -> { final TypedXmlSerializer serializer = Xml.resolveSerializer(out); serializer.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true); serializer.startDocument(null, true); serializer.startDocument("UTF-8", true); serializer.startTag(null, XML_TAG_STATE); writeIntAttribute(serializer, XML_ATTR_PERSISTENCE_VERSION, CURRENT_PERSISTENCE_VERSION); Loading Loading @@ -424,13 +429,18 @@ public final class AssociationDiskStore { while (true) { parser.nextTag(); if (isEndOfTag(parser, XML_TAG_ASSOCIATIONS)) break; if (!isStartOfTag(parser, XML_TAG_ASSOCIATION)) continue; if (isEndOfTag(parser, XML_TAG_ASSOCIATIONS)) { break; } if (isStartOfTag(parser, XML_TAG_ASSOCIATION)) { AssociationInfo association = readAssociationV1(parser, userId); associations.addAssociation(association); maxId = Math.max(maxId, association.getId()); } else { Slog.e(TAG, "Unexpected tag " + parser.getName() + " inside <" + XML_TAG_ASSOCIATIONS + "> for user " + userId); XmlUtils.skipCurrentTag(parser); } } associations.setMaxId(maxId); Loading
services/companion/java/com/android/server/companion/devicepresence/CompanionAppBinder.java +55 −34 Original line number Diff line number Diff line Loading @@ -16,6 +16,7 @@ package com.android.server.companion.devicepresence; import android.Manifest; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SuppressLint; Loading @@ -25,14 +26,16 @@ import android.companion.CompanionDeviceService; import android.companion.DevicePresenceEvent; import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; import android.content.pm.ServiceInfo; import android.os.Handler; import android.util.Pair; import android.util.Slog; import com.android.internal.annotations.GuardedBy; import com.android.internal.infra.PerUser; import com.android.server.companion.CompanionDeviceManagerService; import com.android.server.companion.utils.PackageUtils; import java.io.PrintWriter; import java.util.ArrayList; Loading Loading @@ -70,12 +73,16 @@ import java.util.Set; public class CompanionAppBinder { private static final String TAG = "CDM_CompanionAppBinder"; private static final Intent COMPANION_SERVICE_INTENT = new Intent(CompanionDeviceService.SERVICE_INTERFACE); private static final String PROPERTY_PRIMARY_TAG = "android.companion.PROPERTY_PRIMARY_COMPANION_DEVICE_SERVICE"; private static final long REBIND_TIMEOUT = 10 * 1000; // 10 sec @NonNull private final Context mContext; @NonNull private final CompanionServicesRegister mCompanionServicesRegister; @NonNull @GuardedBy("mBoundCompanionApplications") Loading @@ -87,20 +94,10 @@ public class CompanionAppBinder { public CompanionAppBinder(@NonNull Context context) { mContext = context; mCompanionServicesRegister = new CompanionServicesRegister(); mBoundCompanionApplications = new HashMap<>(); mScheduledForRebindingCompanionApplications = new HashSet<>(); } /** * On package changed. */ public void onPackageChanged(@UserIdInt int userId) { // Note: To invalidate the user space for simplicity. We could alternatively manage each // package, but that would easily cause errors if one case is mis-handled. mCompanionServicesRegister.invalidate(userId); } /** * CDM binds to the companion app. */ Loading @@ -109,8 +106,9 @@ public class CompanionAppBinder { Slog.i(TAG, "Binding user=[" + userId + "], package=[" + packageName + "], isSelfManaged=[" + isSelfManaged + "]..."); final List<ComponentName> companionServices = mCompanionServicesRegister.forPackage(userId, packageName); final List<ComponentName> companionServices = getCompanionServiceComponentsForPackage( mContext, packageName, userId); if (companionServices.isEmpty()) { Slog.e(TAG, "Can not bind companion applications u" + userId + "/" + packageName + ": " + "eligible CompanionDeviceService not found.\n" Loading Loading @@ -299,28 +297,51 @@ public class CompanionAppBinder { return connectors != null ? connectors.get(0) : null; } private class CompanionServicesRegister extends PerUser<Map<String, List<ComponentName>>> { @Override @NonNull public synchronized Map<String, List<ComponentName>> forUser( @UserIdInt int userId) { return super.forUser(userId); /** * @return list of {@link CompanionDeviceService}-s per package for a given user. * Services marked as "primary" would always appear at the head of the lists, *before* * all non-primary services. */ private @NonNull List<ComponentName> getCompanionServiceComponentsForPackage( @NonNull Context context, @NonNull String packageName, @UserIdInt int userId) { final PackageManager pm = context.getPackageManager(); final List<ResolveInfo> companionServices = pm.queryIntentServicesAsUser( COMPANION_SERVICE_INTENT, PackageManager.ResolveInfoFlags.of(0), userId); final List<ComponentName> componentNames = new ArrayList<>(); for (ResolveInfo resolveInfo : companionServices) { final ServiceInfo service = resolveInfo.serviceInfo; final ComponentName componentName = service.getComponentName(); if (!componentName.getPackageName().equals(packageName)) continue; final boolean requiresPermission = Manifest.permission.BIND_COMPANION_DEVICE_SERVICE .equals(resolveInfo.serviceInfo.permission); if (!requiresPermission) { Slog.w(TAG, "CompanionDeviceService " + service.getComponentName().flattenToShortString() + " must require " + "android.permission.BIND_COMPANION_DEVICE_SERVICE"); break; } if (isPrimaryCompanionDeviceService(pm, componentName, userId)) { // "Primary" service should be at the head of the list. componentNames.add(0, componentName); } else { componentNames.add(componentName); } @NonNull synchronized List<ComponentName> forPackage( @UserIdInt int userId, @NonNull String packageName) { return forUser(userId).getOrDefault(packageName, Collections.emptyList()); } synchronized void invalidate(@UserIdInt int userId) { remove(userId); return componentNames; } @Override @NonNull protected final Map<String, List<ComponentName>> create(@UserIdInt int userId) { return PackageUtils.getCompanionServicesForUser(mContext, userId); private boolean isPrimaryCompanionDeviceService(@NonNull PackageManager pm, @NonNull ComponentName componentName, @UserIdInt int userId) { try { return pm.getPropertyAsUser(PROPERTY_PRIMARY_TAG, componentName.getPackageName(), componentName.getClassName(), userId).getBoolean(); } catch (PackageManager.NameNotFoundException e) { return false; } } }
services/companion/java/com/android/server/companion/utils/PackageUtils.java +1 −70 Original line number Diff line number Diff line Loading @@ -26,24 +26,19 @@ import static com.android.internal.R.array.config_companionDevicePackages; import static com.android.internal.R.array.config_companionPermSyncEnabledCerts; import static com.android.internal.R.array.config_companionPermSyncEnabledPackages; import android.Manifest; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.UserIdInt; import android.app.AppOpsManager; import android.app.ecm.EnhancedConfirmationManager; import android.companion.CompanionDeviceService; import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.pm.ApplicationInfo; import android.content.pm.FeatureInfo; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; import android.content.pm.PackageManager.PackageInfoFlags; import android.content.pm.PackageManager.ResolveInfoFlags; import android.content.pm.PackageManagerInternal; import android.content.pm.ResolveInfo; import android.content.pm.ServiceInfo; import android.content.pm.Signature; import android.os.Binder; import android.os.Process; Loading @@ -52,11 +47,7 @@ import android.util.Slog; import com.android.internal.util.ArrayUtils; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; /** Loading @@ -66,11 +57,6 @@ public final class PackageUtils { private static final String TAG = "CDM_PackageUtils"; private static final Intent COMPANION_SERVICE_INTENT = new Intent(CompanionDeviceService.SERVICE_INTERFACE); private static final String PROPERTY_PRIMARY_TAG = "android.companion.PROPERTY_PRIMARY_COMPANION_DEVICE_SERVICE"; /** * Get package info */ Loading Loading @@ -118,61 +104,6 @@ public final class PackageUtils { + " in manifest to use this API"); } /** * @return list of {@link CompanionDeviceService}-s per package for a given user. * Services marked as "primary" would always appear at the head of the lists, *before* * all non-primary services. */ public static @NonNull Map<String, List<ComponentName>> getCompanionServicesForUser( @NonNull Context context, @UserIdInt int userId) { final PackageManager pm = context.getPackageManager(); final List<ResolveInfo> companionServices = pm.queryIntentServicesAsUser( COMPANION_SERVICE_INTENT, ResolveInfoFlags.of(0), userId); final Map<String, List<ComponentName>> packageNameToServiceInfoList = new HashMap<>(companionServices.size()); for (ResolveInfo resolveInfo : companionServices) { final ServiceInfo service = resolveInfo.serviceInfo; final boolean requiresPermission = Manifest.permission.BIND_COMPANION_DEVICE_SERVICE .equals(resolveInfo.serviceInfo.permission); if (!requiresPermission) { Slog.w(TAG, "CompanionDeviceService " + service.getComponentName().flattenToShortString() + " must require " + "android.permission.BIND_COMPANION_DEVICE_SERVICE"); continue; } // We'll need to prepend "primary" services, while appending the other (non-primary) // services to the list. final ArrayList<ComponentName> services = (ArrayList<ComponentName>) packageNameToServiceInfoList.computeIfAbsent( service.packageName, it -> new ArrayList<>(1)); final ComponentName componentName = service.getComponentName(); if (isPrimaryCompanionDeviceService(pm, componentName, userId)) { // "Primary" service should be at the head of the list. services.add(0, componentName); } else { services.add(componentName); } } return packageNameToServiceInfoList; } private static boolean isPrimaryCompanionDeviceService(@NonNull PackageManager pm, @NonNull ComponentName componentName, @UserIdInt int userId) { try { return pm.getPropertyAsUser(PROPERTY_PRIMARY_TAG, componentName.getPackageName(), componentName.getClassName(), userId).getBoolean(); } catch (PackageManager.NameNotFoundException e) { return false; } } /** * Check if the package is allowlisted in the overlay config. * For this we'll check to config arrays: Loading