diff options
| -rw-r--r-- | services/companion/java/com/android/server/companion/AssociationRequestsProcessor.java | 8 | ||||
| -rw-r--r-- | services/companion/java/com/android/server/companion/PackageUtils.java | 126 |
2 files changed, 131 insertions, 3 deletions
diff --git a/services/companion/java/com/android/server/companion/AssociationRequestsProcessor.java b/services/companion/java/com/android/server/companion/AssociationRequestsProcessor.java index 1914164f195c..93fc0e7262aa 100644 --- a/services/companion/java/com/android/server/companion/AssociationRequestsProcessor.java +++ b/services/companion/java/com/android/server/companion/AssociationRequestsProcessor.java @@ -23,7 +23,7 @@ import static android.companion.CompanionDeviceManager.COMPANION_DEVICE_DISCOVER import static android.content.ComponentName.createRelative; import static com.android.server.companion.CompanionDeviceManagerService.DEBUG; -import static com.android.server.companion.CompanionDeviceManagerService.LOG_TAG; +import static com.android.server.companion.PackageUtils.enforceUsesCompanionDeviceFeature; import static com.android.server.companion.PermissionsUtils.enforcePermissionsForAssociation; import static com.android.server.companion.RolesUtils.isRoleHolder; @@ -31,6 +31,7 @@ import static java.util.Objects.requireNonNull; import android.annotation.NonNull; import android.annotation.Nullable; +import android.annotation.SuppressLint; import android.annotation.UserIdInt; import android.app.PendingIntent; import android.companion.AssociationInfo; @@ -102,8 +103,9 @@ import java.util.Set; * @see #processAssociationRequestApproval(AssociationRequest, IAssociationRequestCallback, * ResultReceiver, MacAddress) */ +@SuppressLint("LongLogTag") class AssociationRequestsProcessor { - private static final String TAG = LOG_TAG + ".AssociationRequestsProcessor"; + private static final String TAG = "CompanionDevice_AssociationRequestsProcessor"; private static final ComponentName ASSOCIATION_REQUEST_APPROVAL_ACTIVITY = createRelative(COMPANION_DEVICE_DISCOVERY_PACKAGE_NAME, ".CompanionDeviceActivity"); @@ -161,7 +163,7 @@ class AssociationRequestsProcessor { // 1. Enforce permissions and other requirements. enforcePermissionsForAssociation(mContext, request, packageUid); - mService.checkUsesFeature(packageName, userId); + enforceUsesCompanionDeviceFeature(mContext, userId, packageName); // 2. Check if association can be created without launching UI (i.e. CDM needs NEITHER // to perform discovery NOR to collect user consent). diff --git a/services/companion/java/com/android/server/companion/PackageUtils.java b/services/companion/java/com/android/server/companion/PackageUtils.java new file mode 100644 index 000000000000..985daa356a53 --- /dev/null +++ b/services/companion/java/com/android/server/companion/PackageUtils.java @@ -0,0 +1,126 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * 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.server.companion; + +import static android.content.pm.PackageManager.FEATURE_COMPANION_DEVICE_SETUP; +import static android.content.pm.PackageManager.GET_CONFIGURATIONS; +import static android.content.pm.PackageManager.GET_META_DATA; +import static android.content.pm.PackageManager.GET_PERMISSIONS; + +import static com.android.server.companion.CompanionDeviceManagerService.LOG_TAG; + +import android.Manifest; +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.annotation.UserIdInt; +import android.companion.CompanionDeviceService; +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +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.ResolveInfo; +import android.content.pm.ServiceInfo; +import android.os.Binder; +import android.util.Slog; + +import com.android.internal.util.ArrayUtils; + +import java.util.HashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; + +/** + * Utility methods for working with {@link PackageInfo}-s. + */ +final class PackageUtils { + private static final Intent COMPANION_SERVICE_INTENT = + new Intent(CompanionDeviceService.SERVICE_INTERFACE); + private static final String META_DATA_KEY_PRIMARY = "primary"; + + static @Nullable PackageInfo getPackageInfo(@NonNull Context context, + @UserIdInt int userId, @NonNull String packageName) { + final PackageManager pm = context.getPackageManager(); + final PackageInfoFlags flags = PackageInfoFlags.of(GET_PERMISSIONS | GET_CONFIGURATIONS); + return Binder.withCleanCallingIdentity(() -> + pm.getPackageInfoAsUser(packageName, flags , userId)); + } + + static void enforceUsesCompanionDeviceFeature(@NonNull Context context, + @UserIdInt int userId, @NonNull String packageName) { + final boolean requested = ArrayUtils.contains( + getPackageInfo(context, userId, packageName).reqFeatures, + FEATURE_COMPANION_DEVICE_SETUP); + + if (requested) { + throw new IllegalStateException("Must declare uses-feature " + + FEATURE_COMPANION_DEVICE_SETUP + + " 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. + */ + static @NonNull Map<String, List<ComponentName>> getCompanionServicesForUser( + @NonNull Context context, @UserIdInt int userId) { + final PackageManager pm = context.getPackageManager(); + final ResolveInfoFlags flags = ResolveInfoFlags.of(GET_META_DATA); + final List<ResolveInfo> companionServices = + pm.queryIntentServicesAsUser(COMPANION_SERVICE_INTENT, flags, userId); + + final Map<String, List<ComponentName>> packageNameToServiceInfoList = new HashMap<>(); + + 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(LOG_TAG, "CompanionDeviceService " + + service.getComponentName().flattenToShortString() + " must require " + + "android.permission.BIND_COMPANION_DEVICE_SERVICE"); + continue; + } + + // Use LinkedList, because we'll need to prepend "primary" services, while appending the + // other (non-primary) services to the list. + final LinkedList<ComponentName> services = + (LinkedList<ComponentName>) packageNameToServiceInfoList.computeIfAbsent( + service.packageName, it -> new LinkedList<>()); + + final ComponentName componentName = service.getComponentName(); + if (isPrimaryCompanionDeviceService(service)) { + // "Primary" service should be at the head of the list. + services.addFirst(componentName); + } else { + services.addLast(componentName); + } + } + + return packageNameToServiceInfoList; + } + + private static boolean isPrimaryCompanionDeviceService(ServiceInfo service) { + return service.metaData != null && service.metaData.getBoolean(META_DATA_KEY_PRIMARY); + } +} |