[8/8] Use PresenceController and AppController in CdmService
Use CompanionDevicePresenceMonitor and CompanionApplicationController in CompanionDeviceManagerService. Remove CompanionDevicePresenceController. Bug: 211398735 Test: atest CtsCompanionDeviceManagerCoreTestCases Test: atest CtsCompanionDeviceManagerUiAutomationTestCases Test: atest CtsOsTestCases:CompanionDeviceManagerTest Change-Id: I542c6d8157b51fc9c0a2e55055792b6951f292d9
This commit is contained in:
@@ -207,6 +207,18 @@ public final class AssociationInfo implements Parcelable {
|
||||
return macAddress.equals(mDeviceMacAddress);
|
||||
}
|
||||
|
||||
/**
|
||||
* Utility method to be used by CdmService only.
|
||||
*
|
||||
* @return whether CdmService should bind the companion application that "owns" this association
|
||||
* when the device is present.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
public boolean shouldBindWhenPresent() {
|
||||
return mNotifyOnDeviceNearby || mSelfManaged;
|
||||
}
|
||||
|
||||
/** @hide */
|
||||
public @NonNull String toShortString() {
|
||||
final StringBuilder sb = new StringBuilder();
|
||||
|
||||
@@ -16,7 +16,9 @@
|
||||
|
||||
package com.android.server.companion;
|
||||
|
||||
import static com.android.server.companion.CompanionDeviceManagerService.LOG_TAG;
|
||||
import static com.android.server.companion.CompanionDeviceManagerService.TAG;
|
||||
|
||||
import static java.util.concurrent.TimeUnit.DAYS;
|
||||
|
||||
import android.app.job.JobInfo;
|
||||
import android.app.job.JobParameters;
|
||||
@@ -37,17 +39,16 @@ import com.android.server.LocalServices;
|
||||
*/
|
||||
public class AssociationCleanUpService extends JobService {
|
||||
private static final int JOB_ID = AssociationCleanUpService.class.hashCode();
|
||||
private static final long ONE_DAY_INTERVAL = 3 * 24 * 60 * 60 * 1000; // 1 Day
|
||||
private CompanionDeviceManagerServiceInternal mCdmServiceInternal = LocalServices.getService(
|
||||
CompanionDeviceManagerServiceInternal.class);
|
||||
private static final long ONE_DAY_INTERVAL = DAYS.toMillis(1);
|
||||
|
||||
@Override
|
||||
public boolean onStartJob(final JobParameters params) {
|
||||
Slog.i(LOG_TAG, "Execute the Association CleanUp job");
|
||||
Slog.i(TAG, "Execute the Association CleanUp job");
|
||||
// Special policy for APP_STREAMING role that need to revoke associations if the device
|
||||
// does not connect for 3 months.
|
||||
AsyncTask.execute(() -> {
|
||||
mCdmServiceInternal.associationCleanUp(AssociationRequest.DEVICE_PROFILE_APP_STREAMING);
|
||||
LocalServices.getService(CompanionDeviceManagerServiceInternal.class)
|
||||
.associationCleanUp(AssociationRequest.DEVICE_PROFILE_APP_STREAMING);
|
||||
jobFinished(params, false);
|
||||
});
|
||||
return true;
|
||||
@@ -55,7 +56,7 @@ public class AssociationCleanUpService extends JobService {
|
||||
|
||||
@Override
|
||||
public boolean onStopJob(final JobParameters params) {
|
||||
Slog.i(LOG_TAG, "Association cleanup job stopped; id=" + params.getJobId()
|
||||
Slog.i(TAG, "Association cleanup job stopped; id=" + params.getJobId()
|
||||
+ ", reason="
|
||||
+ JobParameters.getInternalReasonCodeDescription(
|
||||
params.getInternalStopReasonCode()));
|
||||
@@ -63,7 +64,7 @@ public class AssociationCleanUpService extends JobService {
|
||||
}
|
||||
|
||||
static void schedule(Context context) {
|
||||
Slog.i(LOG_TAG, "Scheduling the Association Cleanup job");
|
||||
Slog.i(TAG, "Scheduling the Association Cleanup job");
|
||||
final JobScheduler jobScheduler = context.getSystemService(JobScheduler.class);
|
||||
final JobInfo job = new JobInfo.Builder(JOB_ID,
|
||||
new ComponentName(context, AssociationCleanUpService.class))
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,238 +0,0 @@
|
||||
/*
|
||||
* 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.server.companion;
|
||||
|
||||
import static android.Manifest.permission.BIND_COMPANION_DEVICE_SERVICE;
|
||||
import static android.content.Context.BIND_IMPORTANT;
|
||||
|
||||
import static com.android.internal.util.CollectionUtils.filter;
|
||||
|
||||
import android.annotation.NonNull;
|
||||
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.content.pm.PackageManager;
|
||||
import android.content.pm.ResolveInfo;
|
||||
import android.os.Handler;
|
||||
import android.util.ArrayMap;
|
||||
import android.util.Slog;
|
||||
|
||||
import com.android.internal.infra.PerUser;
|
||||
import com.android.internal.infra.ServiceConnector;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* This class creates/removes {@link ServiceConnector}s between {@link CompanionDeviceService} and
|
||||
* the companion apps. The controller also will notify the companion apps with device status.
|
||||
*/
|
||||
public class CompanionDevicePresenceController {
|
||||
private static final String LOG_TAG = "CompanionDevicePresenceController";
|
||||
PerUser<ArrayMap<String, List<BoundService>>> mBoundServices;
|
||||
private static final String META_DATA_KEY_PRIMARY = "android.companion.primary";
|
||||
private final CompanionDeviceManagerService mService;
|
||||
|
||||
public CompanionDevicePresenceController(CompanionDeviceManagerService service) {
|
||||
mService = service;
|
||||
mBoundServices = new PerUser<ArrayMap<String, List<BoundService>>>() {
|
||||
@NonNull
|
||||
@Override
|
||||
protected ArrayMap<String, List<BoundService>> create(int userId) {
|
||||
return new ArrayMap<>();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
void onDeviceNotifyAppeared(AssociationInfo association, Context context, Handler handler) {
|
||||
for (BoundService boundService : getDeviceListenerServiceConnector(
|
||||
association, context, handler)) {
|
||||
if (boundService.mIsPrimary) {
|
||||
Slog.i(LOG_TAG,
|
||||
"Sending onDeviceAppeared to " + association.getPackageName() + ")");
|
||||
boundService.mServiceConnector.run(
|
||||
service -> service.onDeviceAppeared(association));
|
||||
} else {
|
||||
Slog.i(LOG_TAG, "Connecting to " + boundService.mComponentName);
|
||||
boundService.mServiceConnector.connect();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void onDeviceNotifyDisappeared(AssociationInfo association, Context context, Handler handler) {
|
||||
for (BoundService boundService : getDeviceListenerServiceConnector(
|
||||
association, context, handler)) {
|
||||
if (boundService.mIsPrimary) {
|
||||
Slog.i(LOG_TAG,
|
||||
"Sending onDeviceDisappeared to " + association.getPackageName() + ")");
|
||||
boundService.mServiceConnector.run(service ->
|
||||
service.onDeviceDisappeared(association));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void onDeviceNotifyDisappearedAndUnbind(AssociationInfo association,
|
||||
Context context, Handler handler) {
|
||||
for (BoundService boundService : getDeviceListenerServiceConnector(
|
||||
association, context, handler)) {
|
||||
if (boundService.mIsPrimary) {
|
||||
Slog.i(LOG_TAG,
|
||||
"Sending onDeviceDisappeared to " + association.getPackageName() + ")");
|
||||
boundService.mServiceConnector.post(
|
||||
service -> {
|
||||
service.onDeviceDisappeared(association);
|
||||
}).thenRun(() -> unbindDevicePresenceListener(
|
||||
association.getPackageName(), association.getUserId()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void unbindDevicePresenceListener(String packageName, int userId) {
|
||||
List<BoundService> boundServices = mBoundServices.forUser(userId)
|
||||
.remove(packageName);
|
||||
if (boundServices != null) {
|
||||
for (BoundService boundService: boundServices) {
|
||||
Slog.d(LOG_TAG, "Unbinding the serviceConnector: " + boundService.mComponentName);
|
||||
boundService.mServiceConnector.unbind();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private List<BoundService> getDeviceListenerServiceConnector(AssociationInfo a, Context context,
|
||||
Handler handler) {
|
||||
return mBoundServices.forUser(a.getUserId()).computeIfAbsent(
|
||||
a.getPackageName(),
|
||||
pkg -> createDeviceListenerServiceConnector(a, context, handler));
|
||||
}
|
||||
|
||||
private List<BoundService> createDeviceListenerServiceConnector(AssociationInfo a,
|
||||
Context context, Handler handler) {
|
||||
List<ResolveInfo> resolveInfos = context
|
||||
.getPackageManager()
|
||||
.queryIntentServicesAsUser(new Intent(CompanionDeviceService.SERVICE_INTERFACE),
|
||||
PackageManager.GET_META_DATA, a.getUserId());
|
||||
List<ResolveInfo> packageResolveInfos = filter(resolveInfos,
|
||||
info -> Objects.equals(info.serviceInfo.packageName, a.getPackageName()));
|
||||
List<BoundService> serviceConnectors = new ArrayList<>();
|
||||
if (!validatePackageInfo(packageResolveInfos, a)) {
|
||||
return serviceConnectors;
|
||||
}
|
||||
for (ResolveInfo packageResolveInfo : packageResolveInfos) {
|
||||
boolean isPrimary = (packageResolveInfo.serviceInfo.metaData != null
|
||||
&& packageResolveInfo.serviceInfo.metaData.getBoolean(META_DATA_KEY_PRIMARY))
|
||||
|| packageResolveInfos.size() == 1;
|
||||
ComponentName componentName = packageResolveInfo.serviceInfo.getComponentName();
|
||||
|
||||
Slog.i(LOG_TAG, "Initializing CompanionDeviceService binding for " + componentName);
|
||||
|
||||
ServiceConnector<ICompanionDeviceService> serviceConnector =
|
||||
new ServiceConnector.Impl<ICompanionDeviceService>(context,
|
||||
new Intent(CompanionDeviceService.SERVICE_INTERFACE).setComponent(
|
||||
componentName), BIND_IMPORTANT, a.getUserId(),
|
||||
ICompanionDeviceService.Stub::asInterface) {
|
||||
@Override
|
||||
protected long getAutoDisconnectTimeoutMs() {
|
||||
// Service binding is managed manually based on corresponding device
|
||||
// being nearby
|
||||
return -1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void binderDied() {
|
||||
super.binderDied();
|
||||
if (a.isSelfManaged()) {
|
||||
mBoundServices.forUser(a.getUserId()).remove(a.getPackageName());
|
||||
mService.mPresentSelfManagedDevices.remove(a.getId());
|
||||
} else {
|
||||
// Re-connect to the service if process gets killed
|
||||
handler.postDelayed(
|
||||
this::connect,
|
||||
CompanionDeviceManagerService
|
||||
.DEVICE_LISTENER_DIED_REBIND_TIMEOUT_MS);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
serviceConnectors.add(new BoundService(componentName, isPrimary, serviceConnector));
|
||||
}
|
||||
return serviceConnectors;
|
||||
}
|
||||
|
||||
private boolean validatePackageInfo(List<ResolveInfo> packageResolveInfos,
|
||||
AssociationInfo association) {
|
||||
if (packageResolveInfos.size() == 0 || packageResolveInfos.size() > 5) {
|
||||
Slog.e(LOG_TAG, "Device presence listener package must have at least one and not "
|
||||
+ "more than five CompanionDeviceService(s) declared. But "
|
||||
+ association.getPackageName()
|
||||
+ " has " + packageResolveInfos.size());
|
||||
return false;
|
||||
}
|
||||
|
||||
int primaryCount = 0;
|
||||
for (ResolveInfo packageResolveInfo : packageResolveInfos) {
|
||||
String servicePermission = packageResolveInfo.serviceInfo.permission;
|
||||
if (!BIND_COMPANION_DEVICE_SERVICE.equals(servicePermission)) {
|
||||
Slog.e(LOG_TAG, "Binding CompanionDeviceService must have "
|
||||
+ BIND_COMPANION_DEVICE_SERVICE + " permission.");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (packageResolveInfo.serviceInfo.metaData != null
|
||||
&& packageResolveInfo.serviceInfo.metaData.getBoolean(META_DATA_KEY_PRIMARY)) {
|
||||
primaryCount++;
|
||||
if (primaryCount > 1) {
|
||||
Slog.e(LOG_TAG, "Must have exactly one primary CompanionDeviceService "
|
||||
+ "to be bound but "
|
||||
+ association.getPackageName() + "has " + primaryCount);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (packageResolveInfos.size() > 1 && primaryCount == 0) {
|
||||
Slog.e(LOG_TAG, "Must have exactly one primary CompanionDeviceService "
|
||||
+ "to be bound when declare more than one CompanionDeviceService but "
|
||||
+ association.getPackageName() + " has " + primaryCount);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (packageResolveInfos.size() == 1 && primaryCount != 0) {
|
||||
Slog.w(LOG_TAG, "Do not need the primary metadata if there's only one"
|
||||
+ " CompanionDeviceService " + "but " + association.getPackageName()
|
||||
+ " has " + primaryCount);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private static class BoundService {
|
||||
private final ComponentName mComponentName;
|
||||
private final boolean mIsPrimary;
|
||||
private final ServiceConnector<ICompanionDeviceService> mServiceConnector;
|
||||
|
||||
BoundService(ComponentName componentName,
|
||||
boolean isPrimary, ServiceConnector<ICompanionDeviceService> serviceConnector) {
|
||||
this.mComponentName = componentName;
|
||||
this.mIsPrimary = isPrimary;
|
||||
this.mServiceConnector = serviceConnector;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -21,7 +21,7 @@ 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 static com.android.server.companion.CompanionDeviceManagerService.TAG;
|
||||
|
||||
import android.Manifest;
|
||||
import android.annotation.NonNull;
|
||||
@@ -96,7 +96,7 @@ final class PackageUtils {
|
||||
final boolean requiresPermission = Manifest.permission.BIND_COMPANION_DEVICE_SERVICE
|
||||
.equals(resolveInfo.serviceInfo.permission);
|
||||
if (!requiresPermission) {
|
||||
Slog.w(LOG_TAG, "CompanionDeviceService "
|
||||
Slog.w(TAG, "CompanionDeviceService "
|
||||
+ service.getComponentName().flattenToShortString() + " must require "
|
||||
+ "android.permission.BIND_COMPANION_DEVICE_SERVICE");
|
||||
continue;
|
||||
|
||||
@@ -36,6 +36,7 @@ import android.Manifest;
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.Nullable;
|
||||
import android.annotation.UserIdInt;
|
||||
import android.companion.AssociationInfo;
|
||||
import android.companion.AssociationRequest;
|
||||
import android.companion.CompanionDeviceManager;
|
||||
import android.content.Context;
|
||||
@@ -190,6 +191,19 @@ final class PermissionsUtils {
|
||||
return checkCallerCanManageCompanionDevice(context);
|
||||
}
|
||||
|
||||
static @Nullable AssociationInfo sanitizeWithCallerChecks(@NonNull Context context,
|
||||
@Nullable AssociationInfo association) {
|
||||
if (association == null) return null;
|
||||
|
||||
final int userId = association.getUserId();
|
||||
final String packageName = association.getPackageName();
|
||||
if (!checkCallerCanManageAssociationsForPackage(context, userId, packageName)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return association;
|
||||
}
|
||||
|
||||
private static boolean checkPackage(@UserIdInt int uid, @NonNull String packageName) {
|
||||
try {
|
||||
return getAppOpsService().checkPackage(uid, packageName) == MODE_ALLOWED;
|
||||
|
||||
@@ -19,6 +19,7 @@ package com.android.server.companion;
|
||||
import static android.app.role.RoleManager.MANAGE_HOLDERS_FLAG_DONT_KILL_APP;
|
||||
|
||||
import static com.android.server.companion.CompanionDeviceManagerService.DEBUG;
|
||||
import static com.android.server.companion.CompanionDeviceManagerService.TAG;
|
||||
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.SuppressLint;
|
||||
@@ -35,7 +36,6 @@ import java.util.List;
|
||||
/** Utility methods for accessing {@link RoleManager} APIs. */
|
||||
@SuppressLint("LongLogTag")
|
||||
final class RolesUtils {
|
||||
private static final String TAG = CompanionDeviceManagerService.LOG_TAG;
|
||||
|
||||
static boolean isRoleHolder(@NonNull Context context, @UserIdInt int userId,
|
||||
@NonNull String packageName, @NonNull String role) {
|
||||
|
||||
Reference in New Issue
Block a user