Merge changes Ie0400e06,Icac9c6b3
* changes: [2/X] Introduce CDM PackageUtils [1/X] Introduce CompanionDeviceServiceConnector
This commit is contained in:
committed by
Android (Google) Code Review
commit
d9966186a2
@@ -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).
|
||||
|
||||
@@ -0,0 +1,119 @@
|
||||
/*
|
||||
* 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.Context.BIND_IMPORTANT;
|
||||
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.Nullable;
|
||||
import android.annotation.SuppressLint;
|
||||
import android.annotation.UserIdInt;
|
||||
import android.companion.AssociationInfo;
|
||||
import android.companion.CompanionDeviceService;
|
||||
import android.companion.ICompanionDeviceService;
|
||||
import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.os.IBinder;
|
||||
import android.util.Log;
|
||||
|
||||
import com.android.internal.infra.ServiceConnector;
|
||||
|
||||
/**
|
||||
* Manages a connection (binding) to an instance of {@link CompanionDeviceService} running in the
|
||||
* application process.
|
||||
*/
|
||||
@SuppressLint("LongLogTag")
|
||||
class CompanionDeviceServiceConnector extends ServiceConnector.Impl<ICompanionDeviceService> {
|
||||
private static final String TAG = "CompanionDevice_ServiceConnector";
|
||||
private static final boolean DEBUG = false;
|
||||
private static final int BINDING_FLAGS = BIND_IMPORTANT;
|
||||
|
||||
/** Listener for changes to the state of the {@link CompanionDeviceServiceConnector} */
|
||||
interface Listener {
|
||||
void onBindingDied(@UserIdInt int userId, @NonNull String packageName);
|
||||
}
|
||||
|
||||
private final @UserIdInt int mUserId;
|
||||
private final @NonNull ComponentName mComponentName;
|
||||
private @Nullable Listener mListener;
|
||||
|
||||
CompanionDeviceServiceConnector(@NonNull Context context, @UserIdInt int userId,
|
||||
@NonNull ComponentName componentName) {
|
||||
super(context, buildIntent(componentName), BINDING_FLAGS, userId, null);
|
||||
mUserId = userId;
|
||||
mComponentName = componentName;
|
||||
}
|
||||
|
||||
void setListener(@Nullable Listener listener) {
|
||||
mListener = listener;
|
||||
}
|
||||
|
||||
void postOnDeviceAppeared(@NonNull AssociationInfo associationInfo) {
|
||||
post(companionService -> companionService.onDeviceAppeared(associationInfo));
|
||||
}
|
||||
|
||||
void postOnDeviceDisappeared(@NonNull AssociationInfo associationInfo) {
|
||||
post(companionService -> companionService.onDeviceDisappeared(associationInfo));
|
||||
}
|
||||
|
||||
/**
|
||||
* Post "unbind" job, which will run *after* all previously posted jobs complete.
|
||||
*
|
||||
* IMPORTANT: use this method instead of invoking {@link ServiceConnector#unbind()} directly,
|
||||
* because the latter may cause previously posted callback, such as
|
||||
* {@link ICompanionDeviceService#onDeviceDisappeared(AssociationInfo)} to be dropped.
|
||||
*/
|
||||
void postUnbind() {
|
||||
post(it -> unbind());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onServiceConnectionStatusChanged(
|
||||
@NonNull ICompanionDeviceService service, boolean isConnected) {
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, "onServiceConnection_StatusChanged() " + mComponentName.toShortString()
|
||||
+ " connected=" + isConnected);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBindingDied(@NonNull ComponentName name) {
|
||||
// IMPORTANT: call super!
|
||||
super.onBindingDied(name);
|
||||
|
||||
if (DEBUG) Log.d(TAG, "onBindingDied() " + mComponentName.toShortString());
|
||||
|
||||
mListener.onBindingDied(mUserId, mComponentName.getPackageName());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ICompanionDeviceService binderAsInterface(@NonNull IBinder service) {
|
||||
return ICompanionDeviceService.Stub.asInterface(service);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected long getAutoDisconnectTimeoutMs() {
|
||||
// Do NOT auto-disconnect.
|
||||
return -1;
|
||||
}
|
||||
|
||||
private static @NonNull Intent buildIntent(@NonNull ComponentName componentName) {
|
||||
return new Intent(CompanionDeviceService.SERVICE_INTERFACE)
|
||||
.setComponent(componentName);
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user