Loading core/java/android/companion/CompanionDeviceManager.java +4 −4 Original line number Diff line number Diff line Loading @@ -1086,7 +1086,7 @@ public final class CompanionDeviceManager { } Objects.requireNonNull(deviceAddress, "address cannot be null"); try { mService.legacyStartObservingDevicePresence(deviceAddress, mService.registerDevicePresenceListenerService(deviceAddress, mContext.getOpPackageName(), mContext.getUserId()); } catch (RemoteException e) { ExceptionUtils.propagateIfInstanceOf(e.getCause(), DeviceNotAssociatedException.class); Loading Loading @@ -1128,7 +1128,7 @@ public final class CompanionDeviceManager { } Objects.requireNonNull(deviceAddress, "address cannot be null"); try { mService.legacyStopObservingDevicePresence(deviceAddress, mService.unregisterDevicePresenceListenerService(deviceAddress, mContext.getPackageName(), mContext.getUserId()); } catch (RemoteException e) { ExceptionUtils.propagateIfInstanceOf(e.getCause(), DeviceNotAssociatedException.class); Loading Loading @@ -1328,7 +1328,7 @@ public final class CompanionDeviceManager { @RequiresPermission(android.Manifest.permission.REQUEST_COMPANION_SELF_MANAGED) public void notifyDeviceAppeared(int associationId) { try { mService.notifySelfManagedDeviceAppeared(associationId); mService.notifyDeviceAppeared(associationId); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } Loading @@ -1350,7 +1350,7 @@ public final class CompanionDeviceManager { @RequiresPermission(android.Manifest.permission.REQUEST_COMPANION_SELF_MANAGED) public void notifyDeviceDisappeared(int associationId) { try { mService.notifySelfManagedDeviceDisappeared(associationId); mService.notifyDeviceDisappeared(associationId); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } Loading core/java/android/companion/ICompanionDeviceManager.aidl +12 −12 Original line number Diff line number Diff line Loading @@ -59,16 +59,12 @@ interface ICompanionDeviceManager { int userId); @EnforcePermission("REQUEST_OBSERVE_COMPANION_DEVICE_PRESENCE") void legacyStartObservingDevicePresence(in String deviceAddress, in String callingPackage, int userId); @EnforcePermission("REQUEST_OBSERVE_COMPANION_DEVICE_PRESENCE") void legacyStopObservingDevicePresence(in String deviceAddress, in String callingPackage, int userId); @EnforcePermission("REQUEST_OBSERVE_COMPANION_DEVICE_PRESENCE") void startObservingDevicePresence(in ObservingDevicePresenceRequest request, in String packageName, int userId); void registerDevicePresenceListenerService(in String deviceAddress, in String callingPackage, int userId); @EnforcePermission("REQUEST_OBSERVE_COMPANION_DEVICE_PRESENCE") void stopObservingDevicePresence(in ObservingDevicePresenceRequest request, in String packageName, int userId); void unregisterDevicePresenceListenerService(in String deviceAddress, in String callingPackage, int userId); boolean canPairWithoutPrompt(in String packageName, in String deviceMacAddress, int userId); Loading Loading @@ -97,11 +93,9 @@ interface ICompanionDeviceManager { @EnforcePermission("USE_COMPANION_TRANSPORTS") void removeOnMessageReceivedListener(int messageType, IOnMessageReceivedListener listener); @EnforcePermission("REQUEST_COMPANION_SELF_MANAGED") void notifySelfManagedDeviceAppeared(int associationId); void notifyDeviceAppeared(int associationId); @EnforcePermission("REQUEST_COMPANION_SELF_MANAGED") void notifySelfManagedDeviceDisappeared(int associationId); void notifyDeviceDisappeared(int associationId); PendingIntent buildPermissionTransferUserConsentIntent(String callingPackage, int userId, int associationId); Loading Loading @@ -141,4 +135,10 @@ interface ICompanionDeviceManager { byte[] getBackupPayload(int userId); void applyRestoredPayload(in byte[] payload, int userId); @EnforcePermission("REQUEST_OBSERVE_COMPANION_DEVICE_PRESENCE") void startObservingDevicePresence(in ObservingDevicePresenceRequest request, in String packageName, int userId); @EnforcePermission("REQUEST_OBSERVE_COMPANION_DEVICE_PRESENCE") void stopObservingDevicePresence(in ObservingDevicePresenceRequest request, in String packageName, int userId); } services/companion/java/com/android/server/companion/presence/CompanionAppBinder.java→services/companion/java/com/android/server/companion/CompanionApplicationController.java +567 −0 Original line number Diff line number Diff line Loading @@ -14,7 +14,9 @@ * limitations under the License. */ package com.android.server.companion.presence; package com.android.server.companion; import static android.companion.AssociationRequest.DEVICE_PROFILE_AUTOMOTIVE_PROJECTION; import android.annotation.NonNull; import android.annotation.Nullable; Loading @@ -25,7 +27,9 @@ import android.companion.CompanionDeviceService; import android.companion.DevicePresenceEvent; import android.content.ComponentName; import android.content.Context; import android.hardware.power.Mode; import android.os.Handler; import android.os.ParcelUuid; import android.os.PowerManagerInternal; import android.util.Log; import android.util.Slog; Loading @@ -33,8 +37,10 @@ import android.util.SparseArray; import com.android.internal.annotations.GuardedBy; import com.android.internal.infra.PerUser; import com.android.server.companion.CompanionDeviceManagerService; import com.android.server.companion.association.AssociationStore; import com.android.server.companion.presence.CompanionDevicePresenceMonitor; import com.android.server.companion.presence.ObservableUuid; import com.android.server.companion.presence.ObservableUuidStore; import com.android.server.companion.utils.PackageUtils; import java.io.PrintWriter; Loading @@ -54,61 +60,56 @@ import java.util.Map; * application process. * * <p> * The following is the list of the APIs provided by {@link CompanionAppBinder} (to be * The following is the list of the APIs provided by {@link CompanionApplicationController} (to be * utilized by {@link CompanionDeviceManagerService}): * <ul> * <li> {@link #bindCompanionApplication(int, String, boolean, CompanionServiceConnector.Listener)} * <li> {@link #bindCompanionApplication(int, String, boolean)} * <li> {@link #unbindCompanionApplication(int, String)} * <li> {@link #notifyCompanionDevicePresenceEvent(AssociationInfo, int)} * <li> {@link #isCompanionApplicationBound(int, String)} * <li> {@link #isRebindingCompanionApplicationScheduled(int, String)} * </ul> * * @see CompanionDeviceService * @see android.companion.ICompanionDeviceService * @see CompanionServiceConnector * @see CompanionDeviceServiceConnector */ @SuppressLint("LongLogTag") public class CompanionAppBinder { private static final String TAG = "CDM_CompanionAppBinder"; public class CompanionApplicationController { static final boolean DEBUG = false; private static final String TAG = "CDM_CompanionApplicationController"; private static final long REBIND_TIMEOUT = 10 * 1000; // 10 sec @NonNull private final Context mContext; @NonNull private final AssociationStore mAssociationStore; @NonNull private final ObservableUuidStore mObservableUuidStore; @NonNull private final CompanionServicesRegister mCompanionServicesRegister; private final @NonNull Context mContext; private final @NonNull AssociationStore mAssociationStore; private final @NonNull ObservableUuidStore mObservableUuidStore; private final @NonNull CompanionDevicePresenceMonitor mDevicePresenceMonitor; private final @NonNull CompanionServicesRegister mCompanionServicesRegister; private final PowerManagerInternal mPowerManagerInternal; @NonNull @GuardedBy("mBoundCompanionApplications") private final AndroidPackageMap<List<CompanionServiceConnector>> private final @NonNull AndroidPackageMap<List<CompanionDeviceServiceConnector>> mBoundCompanionApplications; @NonNull @GuardedBy("mScheduledForRebindingCompanionApplications") private final AndroidPackageMap<Boolean> mScheduledForRebindingCompanionApplications; private final @NonNull AndroidPackageMap<Boolean> mScheduledForRebindingCompanionApplications; public CompanionAppBinder(@NonNull Context context, @NonNull AssociationStore associationStore, @NonNull ObservableUuidStore observableUuidStore, @NonNull PowerManagerInternal powerManagerInternal) { CompanionApplicationController(Context context, AssociationStore associationStore, ObservableUuidStore observableUuidStore, CompanionDevicePresenceMonitor companionDevicePresenceMonitor, PowerManagerInternal powerManagerInternal) { mContext = context; mAssociationStore = associationStore; mObservableUuidStore = observableUuidStore; mDevicePresenceMonitor = companionDevicePresenceMonitor; mPowerManagerInternal = powerManagerInternal; mCompanionServicesRegister = new CompanionServicesRegister(); mBoundCompanionApplications = new AndroidPackageMap<>(); mScheduledForRebindingCompanionApplications = new AndroidPackageMap<>(); } /** * On package changed. */ public void onPackagesChanged(@UserIdInt int userId) { void onPackagesChanged(@UserIdInt int userId) { mCompanionServicesRegister.invalidate(userId); } Loading @@ -116,14 +117,16 @@ public class CompanionAppBinder { * CDM binds to the companion app. */ public void bindCompanionApplication(@UserIdInt int userId, @NonNull String packageName, boolean isSelfManaged, CompanionServiceConnector.Listener listener) { Slog.i(TAG, "Binding user=[" + userId + "], package=[" + packageName + "], isSelfManaged=[" + isSelfManaged + "]..."); boolean isSelfManaged) { if (DEBUG) { Log.i(TAG, "bind() u" + userId + "/" + packageName + " isSelfManaged=" + isSelfManaged); } final List<ComponentName> companionServices = mCompanionServicesRegister.forPackage(userId, packageName); if (companionServices.isEmpty()) { Slog.e(TAG, "Can not bind companion applications u" + userId + "/" + packageName + ": " Slog.w(TAG, "Can not bind companion applications u" + userId + "/" + packageName + ": " + "eligible CompanionDeviceService not found.\n" + "A CompanionDeviceService should declare an intent-filter for " + "\"android.companion.CompanionDeviceService\" action and require " Loading @@ -131,16 +134,16 @@ public class CompanionAppBinder { return; } final List<CompanionServiceConnector> serviceConnectors = new ArrayList<>(); final List<CompanionDeviceServiceConnector> serviceConnectors = new ArrayList<>(); synchronized (mBoundCompanionApplications) { if (mBoundCompanionApplications.containsValueForPackage(userId, packageName)) { Slog.w(TAG, "The package is ALREADY bound."); if (DEBUG) Log.e(TAG, "u" + userId + "/" + packageName + " is ALREADY bound."); return; } for (int i = 0; i < companionServices.size(); i++) { boolean isPrimary = i == 0; serviceConnectors.add(CompanionServiceConnector.newInstance(mContext, userId, serviceConnectors.add(CompanionDeviceServiceConnector.newInstance(mContext, userId, companionServices.get(i), isSelfManaged, isPrimary)); } Loading @@ -148,12 +151,12 @@ public class CompanionAppBinder { } // Set listeners for both Primary and Secondary connectors. for (CompanionServiceConnector serviceConnector : serviceConnectors) { serviceConnector.setListener(listener); for (CompanionDeviceServiceConnector serviceConnector : serviceConnectors) { serviceConnector.setListener(this::onBinderDied); } // Now "bind" all the connectors: the primary one and the rest of them. for (CompanionServiceConnector serviceConnector : serviceConnectors) { for (CompanionDeviceServiceConnector serviceConnector : serviceConnectors) { serviceConnector.connect(); } } Loading @@ -162,9 +165,9 @@ public class CompanionAppBinder { * CDM unbinds the companion app. */ public void unbindCompanionApplication(@UserIdInt int userId, @NonNull String packageName) { Slog.i(TAG, "Unbinding user=[" + userId + "], package=[" + packageName + "]..."); if (DEBUG) Log.i(TAG, "unbind() u" + userId + "/" + packageName); final List<CompanionServiceConnector> serviceConnectors; final List<CompanionDeviceServiceConnector> serviceConnectors; synchronized (mBoundCompanionApplications) { serviceConnectors = mBoundCompanionApplications.removePackage(userId, packageName); Loading @@ -175,11 +178,15 @@ public class CompanionAppBinder { } if (serviceConnectors == null) { Slog.e(TAG, "The package is not bound."); if (DEBUG) { Log.e(TAG, "unbindCompanionApplication(): " + "u" + userId + "/" + packageName + " is NOT bound"); Log.d(TAG, "Stacktrace", new Throwable()); } return; } for (CompanionServiceConnector serviceConnector : serviceConnectors) { for (CompanionDeviceServiceConnector serviceConnector : serviceConnectors) { serviceConnector.postUnbind(); } } Loading @@ -193,25 +200,15 @@ public class CompanionAppBinder { } } /** * Remove bound apps for package. */ public void removePackage(int userId, String packageName) { synchronized (mBoundCompanionApplications) { mBoundCompanionApplications.removePackage(userId, packageName); } } /** * Schedule rebinding for the package. */ public void scheduleRebinding(@UserIdInt int userId, @NonNull String packageName, CompanionServiceConnector serviceConnector) { private void scheduleRebinding(@UserIdInt int userId, @NonNull String packageName, CompanionDeviceServiceConnector serviceConnector) { Slog.i(TAG, "scheduleRebinding() " + userId + "/" + packageName); if (isRebindingCompanionApplicationScheduled(userId, packageName)) { Slog.i(TAG, "CompanionApplication rebinding has been scheduled, skipping " if (DEBUG) { Log.i(TAG, "CompanionApplication rebinding has been scheduled, skipping " + serviceConnector.getComponentName()); } return; } Loading @@ -224,8 +221,7 @@ public class CompanionAppBinder { // Rebinding in 10 seconds. Handler.getMain().postDelayed(() -> onRebindingCompanionApplicationTimeout(userId, packageName, serviceConnector), onRebindingCompanionApplicationTimeout(userId, packageName, serviceConnector), REBIND_TIMEOUT); } Loading @@ -239,12 +235,12 @@ public class CompanionAppBinder { private void onRebindingCompanionApplicationTimeout( @UserIdInt int userId, @NonNull String packageName, @NonNull CompanionServiceConnector serviceConnector) { @NonNull CompanionDeviceServiceConnector serviceConnector) { // Re-mark the application is bound. if (serviceConnector.isPrimary()) { synchronized (mBoundCompanionApplications) { if (!mBoundCompanionApplications.containsValueForPackage(userId, packageName)) { List<CompanionServiceConnector> serviceConnectors = List<CompanionDeviceServiceConnector> serviceConnectors = Collections.singletonList(serviceConnector); mBoundCompanionApplications.setValueForPackage(userId, packageName, serviceConnectors); Loading @@ -260,9 +256,114 @@ public class CompanionAppBinder { } /** * Dump bound apps. * Notify the app that the device appeared. * * @deprecated use {@link #notifyCompanionDevicePresenceEvent(AssociationInfo, int)} instead */ @Deprecated public void notifyCompanionApplicationDeviceAppeared(AssociationInfo association) { final int userId = association.getUserId(); final String packageName = association.getPackageName(); Slog.i(TAG, "notifyDevice_Appeared() id=" + association.getId() + " u" + userId + "/" + packageName); final CompanionDeviceServiceConnector primaryServiceConnector = getPrimaryServiceConnector(userId, packageName); if (primaryServiceConnector == null) { Slog.e(TAG, "notify_CompanionApplicationDevice_Appeared(): " + "u" + userId + "/" + packageName + " is NOT bound."); Slog.e(TAG, "Stacktrace", new Throwable()); return; } Log.i(TAG, "Calling onDeviceAppeared to userId=[" + userId + "] package=[" + packageName + "] associationId=[" + association.getId() + "]"); primaryServiceConnector.postOnDeviceAppeared(association); } /** * Notify the app that the device disappeared. * * @deprecated use {@link #notifyCompanionDevicePresenceEvent(AssociationInfo, int)} instead */ @Deprecated public void notifyCompanionApplicationDeviceDisappeared(AssociationInfo association) { final int userId = association.getUserId(); final String packageName = association.getPackageName(); Slog.i(TAG, "notifyDevice_Disappeared() id=" + association.getId() + " u" + userId + "/" + packageName); final CompanionDeviceServiceConnector primaryServiceConnector = getPrimaryServiceConnector(userId, packageName); if (primaryServiceConnector == null) { Slog.e(TAG, "notify_CompanionApplicationDevice_Disappeared(): " + "u" + userId + "/" + packageName + " is NOT bound."); Slog.e(TAG, "Stacktrace", new Throwable()); return; } Log.i(TAG, "Calling onDeviceDisappeared to userId=[" + userId + "] package=[" + packageName + "] associationId=[" + association.getId() + "]"); primaryServiceConnector.postOnDeviceDisappeared(association); } /** * Notify the app that the device appeared. */ public void notifyCompanionDevicePresenceEvent(AssociationInfo association, int event) { final int userId = association.getUserId(); final String packageName = association.getPackageName(); final CompanionDeviceServiceConnector primaryServiceConnector = getPrimaryServiceConnector(userId, packageName); final DevicePresenceEvent devicePresenceEvent = new DevicePresenceEvent(association.getId(), event, null); if (primaryServiceConnector == null) { Slog.e(TAG, "notifyCompanionApplicationDevicePresenceEvent(): " + "u" + userId + "/" + packageName + " event=[ " + event + " ] is NOT bound."); Slog.e(TAG, "Stacktrace", new Throwable()); return; } Slog.i(TAG, "Calling onDevicePresenceEvent() to userId=[" + userId + "] package=[" + packageName + "] associationId=[" + association.getId() + "] event=[" + event + "]"); primaryServiceConnector.postOnDevicePresenceEvent(devicePresenceEvent); } /** * Notify the app that the device disappeared. */ public void dump(@NonNull PrintWriter out) { public void notifyUuidDevicePresenceEvent(ObservableUuid uuid, int event) { final int userId = uuid.getUserId(); final ParcelUuid parcelUuid = uuid.getUuid(); final String packageName = uuid.getPackageName(); final CompanionDeviceServiceConnector primaryServiceConnector = getPrimaryServiceConnector(userId, packageName); final DevicePresenceEvent devicePresenceEvent = new DevicePresenceEvent(DevicePresenceEvent.NO_ASSOCIATION, event, parcelUuid); if (primaryServiceConnector == null) { Slog.e(TAG, "notifyApplicationDevicePresenceChanged(): " + "u" + userId + "/" + packageName + " event=[ " + event + " ] is NOT bound."); Slog.e(TAG, "Stacktrace", new Throwable()); return; } Slog.i(TAG, "Calling onDevicePresenceEvent() to userId=[" + userId + "] package=[" + packageName + "]" + "event= [" + event + "]"); primaryServiceConnector.postOnDevicePresenceEvent(devicePresenceEvent); } void dump(@NonNull PrintWriter out) { out.append("Companion Device Application Controller: \n"); synchronized (mBoundCompanionApplications) { Loading @@ -276,7 +377,6 @@ public class CompanionAppBinder { } out.append(" Companion Applications Scheduled For Rebinding: "); synchronized (mScheduledForRebindingCompanionApplications) { if (mScheduledForRebindingCompanionApplications.size() == 0) { out.append("<empty>\n"); } else { Loading @@ -284,18 +384,93 @@ public class CompanionAppBinder { mScheduledForRebindingCompanionApplications.dump(out); } } /** * Rebinding for Self-Managed secondary services OR Non-Self-Managed services. */ private void onBinderDied(@UserIdInt int userId, @NonNull String packageName, @NonNull CompanionDeviceServiceConnector serviceConnector) { boolean isPrimary = serviceConnector.isPrimary(); Slog.i(TAG, "onBinderDied() u" + userId + "/" + packageName + " isPrimary: " + isPrimary); // First, disable hint mode for Auto profile and mark not BOUND for primary service ONLY. if (isPrimary) { final List<AssociationInfo> associations = mAssociationStore.getActiveAssociationsByPackage(userId, packageName); for (AssociationInfo association : associations) { final String deviceProfile = association.getDeviceProfile(); if (DEVICE_PROFILE_AUTOMOTIVE_PROJECTION.equals(deviceProfile)) { Slog.i(TAG, "Disable hint mode for device profile: " + deviceProfile); mPowerManagerInternal.setPowerMode(Mode.AUTOMOTIVE_PROJECTION, false); break; } } @Nullable CompanionServiceConnector getPrimaryServiceConnector( synchronized (mBoundCompanionApplications) { mBoundCompanionApplications.removePackage(userId, packageName); } } // Second: schedule rebinding if needed. final boolean shouldScheduleRebind = shouldScheduleRebind(userId, packageName, isPrimary); if (shouldScheduleRebind) { scheduleRebinding(userId, packageName, serviceConnector); } } private @Nullable CompanionDeviceServiceConnector getPrimaryServiceConnector( @UserIdInt int userId, @NonNull String packageName) { final List<CompanionServiceConnector> connectors; final List<CompanionDeviceServiceConnector> connectors; synchronized (mBoundCompanionApplications) { connectors = mBoundCompanionApplications.getValueForPackage(userId, packageName); } return connectors != null ? connectors.get(0) : null; } private boolean shouldScheduleRebind(int userId, String packageName, boolean isPrimary) { // Make sure do not schedule rebind for the case ServiceConnector still gets callback after // app is uninstalled. boolean stillAssociated = false; // Make sure to clean up the state for all the associations // that associate with this package. boolean shouldScheduleRebind = false; boolean shouldScheduleRebindForUuid = false; final List<ObservableUuid> uuids = mObservableUuidStore.getObservableUuidsForPackage(userId, packageName); for (AssociationInfo ai : mAssociationStore.getActiveAssociationsByPackage(userId, packageName)) { final int associationId = ai.getId(); stillAssociated = true; if (ai.isSelfManaged()) { // Do not rebind if primary one is died for selfManaged application. if (isPrimary && mDevicePresenceMonitor.isDevicePresent(associationId)) { mDevicePresenceMonitor.onSelfManagedDeviceReporterBinderDied(associationId); shouldScheduleRebind = false; } // Do not rebind if both primary and secondary services are died for // selfManaged application. shouldScheduleRebind = isCompanionApplicationBound(userId, packageName); } else if (ai.isNotifyOnDeviceNearby()) { // Always rebind for non-selfManaged devices. shouldScheduleRebind = true; } } for (ObservableUuid uuid : uuids) { if (mDevicePresenceMonitor.isDeviceUuidPresent(uuid.getUuid())) { shouldScheduleRebindForUuid = true; break; } } return (stillAssociated && shouldScheduleRebind) || shouldScheduleRebindForUuid; } private class CompanionServicesRegister extends PerUser<Map<String, List<ComponentName>>> { @Override public synchronized @NonNull Map<String, List<ComponentName>> forUser( Loading Loading
core/java/android/companion/CompanionDeviceManager.java +4 −4 Original line number Diff line number Diff line Loading @@ -1086,7 +1086,7 @@ public final class CompanionDeviceManager { } Objects.requireNonNull(deviceAddress, "address cannot be null"); try { mService.legacyStartObservingDevicePresence(deviceAddress, mService.registerDevicePresenceListenerService(deviceAddress, mContext.getOpPackageName(), mContext.getUserId()); } catch (RemoteException e) { ExceptionUtils.propagateIfInstanceOf(e.getCause(), DeviceNotAssociatedException.class); Loading Loading @@ -1128,7 +1128,7 @@ public final class CompanionDeviceManager { } Objects.requireNonNull(deviceAddress, "address cannot be null"); try { mService.legacyStopObservingDevicePresence(deviceAddress, mService.unregisterDevicePresenceListenerService(deviceAddress, mContext.getPackageName(), mContext.getUserId()); } catch (RemoteException e) { ExceptionUtils.propagateIfInstanceOf(e.getCause(), DeviceNotAssociatedException.class); Loading Loading @@ -1328,7 +1328,7 @@ public final class CompanionDeviceManager { @RequiresPermission(android.Manifest.permission.REQUEST_COMPANION_SELF_MANAGED) public void notifyDeviceAppeared(int associationId) { try { mService.notifySelfManagedDeviceAppeared(associationId); mService.notifyDeviceAppeared(associationId); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } Loading @@ -1350,7 +1350,7 @@ public final class CompanionDeviceManager { @RequiresPermission(android.Manifest.permission.REQUEST_COMPANION_SELF_MANAGED) public void notifyDeviceDisappeared(int associationId) { try { mService.notifySelfManagedDeviceDisappeared(associationId); mService.notifyDeviceDisappeared(associationId); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } Loading
core/java/android/companion/ICompanionDeviceManager.aidl +12 −12 Original line number Diff line number Diff line Loading @@ -59,16 +59,12 @@ interface ICompanionDeviceManager { int userId); @EnforcePermission("REQUEST_OBSERVE_COMPANION_DEVICE_PRESENCE") void legacyStartObservingDevicePresence(in String deviceAddress, in String callingPackage, int userId); @EnforcePermission("REQUEST_OBSERVE_COMPANION_DEVICE_PRESENCE") void legacyStopObservingDevicePresence(in String deviceAddress, in String callingPackage, int userId); @EnforcePermission("REQUEST_OBSERVE_COMPANION_DEVICE_PRESENCE") void startObservingDevicePresence(in ObservingDevicePresenceRequest request, in String packageName, int userId); void registerDevicePresenceListenerService(in String deviceAddress, in String callingPackage, int userId); @EnforcePermission("REQUEST_OBSERVE_COMPANION_DEVICE_PRESENCE") void stopObservingDevicePresence(in ObservingDevicePresenceRequest request, in String packageName, int userId); void unregisterDevicePresenceListenerService(in String deviceAddress, in String callingPackage, int userId); boolean canPairWithoutPrompt(in String packageName, in String deviceMacAddress, int userId); Loading Loading @@ -97,11 +93,9 @@ interface ICompanionDeviceManager { @EnforcePermission("USE_COMPANION_TRANSPORTS") void removeOnMessageReceivedListener(int messageType, IOnMessageReceivedListener listener); @EnforcePermission("REQUEST_COMPANION_SELF_MANAGED") void notifySelfManagedDeviceAppeared(int associationId); void notifyDeviceAppeared(int associationId); @EnforcePermission("REQUEST_COMPANION_SELF_MANAGED") void notifySelfManagedDeviceDisappeared(int associationId); void notifyDeviceDisappeared(int associationId); PendingIntent buildPermissionTransferUserConsentIntent(String callingPackage, int userId, int associationId); Loading Loading @@ -141,4 +135,10 @@ interface ICompanionDeviceManager { byte[] getBackupPayload(int userId); void applyRestoredPayload(in byte[] payload, int userId); @EnforcePermission("REQUEST_OBSERVE_COMPANION_DEVICE_PRESENCE") void startObservingDevicePresence(in ObservingDevicePresenceRequest request, in String packageName, int userId); @EnforcePermission("REQUEST_OBSERVE_COMPANION_DEVICE_PRESENCE") void stopObservingDevicePresence(in ObservingDevicePresenceRequest request, in String packageName, int userId); }
services/companion/java/com/android/server/companion/presence/CompanionAppBinder.java→services/companion/java/com/android/server/companion/CompanionApplicationController.java +567 −0 Original line number Diff line number Diff line Loading @@ -14,7 +14,9 @@ * limitations under the License. */ package com.android.server.companion.presence; package com.android.server.companion; import static android.companion.AssociationRequest.DEVICE_PROFILE_AUTOMOTIVE_PROJECTION; import android.annotation.NonNull; import android.annotation.Nullable; Loading @@ -25,7 +27,9 @@ import android.companion.CompanionDeviceService; import android.companion.DevicePresenceEvent; import android.content.ComponentName; import android.content.Context; import android.hardware.power.Mode; import android.os.Handler; import android.os.ParcelUuid; import android.os.PowerManagerInternal; import android.util.Log; import android.util.Slog; Loading @@ -33,8 +37,10 @@ import android.util.SparseArray; import com.android.internal.annotations.GuardedBy; import com.android.internal.infra.PerUser; import com.android.server.companion.CompanionDeviceManagerService; import com.android.server.companion.association.AssociationStore; import com.android.server.companion.presence.CompanionDevicePresenceMonitor; import com.android.server.companion.presence.ObservableUuid; import com.android.server.companion.presence.ObservableUuidStore; import com.android.server.companion.utils.PackageUtils; import java.io.PrintWriter; Loading @@ -54,61 +60,56 @@ import java.util.Map; * application process. * * <p> * The following is the list of the APIs provided by {@link CompanionAppBinder} (to be * The following is the list of the APIs provided by {@link CompanionApplicationController} (to be * utilized by {@link CompanionDeviceManagerService}): * <ul> * <li> {@link #bindCompanionApplication(int, String, boolean, CompanionServiceConnector.Listener)} * <li> {@link #bindCompanionApplication(int, String, boolean)} * <li> {@link #unbindCompanionApplication(int, String)} * <li> {@link #notifyCompanionDevicePresenceEvent(AssociationInfo, int)} * <li> {@link #isCompanionApplicationBound(int, String)} * <li> {@link #isRebindingCompanionApplicationScheduled(int, String)} * </ul> * * @see CompanionDeviceService * @see android.companion.ICompanionDeviceService * @see CompanionServiceConnector * @see CompanionDeviceServiceConnector */ @SuppressLint("LongLogTag") public class CompanionAppBinder { private static final String TAG = "CDM_CompanionAppBinder"; public class CompanionApplicationController { static final boolean DEBUG = false; private static final String TAG = "CDM_CompanionApplicationController"; private static final long REBIND_TIMEOUT = 10 * 1000; // 10 sec @NonNull private final Context mContext; @NonNull private final AssociationStore mAssociationStore; @NonNull private final ObservableUuidStore mObservableUuidStore; @NonNull private final CompanionServicesRegister mCompanionServicesRegister; private final @NonNull Context mContext; private final @NonNull AssociationStore mAssociationStore; private final @NonNull ObservableUuidStore mObservableUuidStore; private final @NonNull CompanionDevicePresenceMonitor mDevicePresenceMonitor; private final @NonNull CompanionServicesRegister mCompanionServicesRegister; private final PowerManagerInternal mPowerManagerInternal; @NonNull @GuardedBy("mBoundCompanionApplications") private final AndroidPackageMap<List<CompanionServiceConnector>> private final @NonNull AndroidPackageMap<List<CompanionDeviceServiceConnector>> mBoundCompanionApplications; @NonNull @GuardedBy("mScheduledForRebindingCompanionApplications") private final AndroidPackageMap<Boolean> mScheduledForRebindingCompanionApplications; private final @NonNull AndroidPackageMap<Boolean> mScheduledForRebindingCompanionApplications; public CompanionAppBinder(@NonNull Context context, @NonNull AssociationStore associationStore, @NonNull ObservableUuidStore observableUuidStore, @NonNull PowerManagerInternal powerManagerInternal) { CompanionApplicationController(Context context, AssociationStore associationStore, ObservableUuidStore observableUuidStore, CompanionDevicePresenceMonitor companionDevicePresenceMonitor, PowerManagerInternal powerManagerInternal) { mContext = context; mAssociationStore = associationStore; mObservableUuidStore = observableUuidStore; mDevicePresenceMonitor = companionDevicePresenceMonitor; mPowerManagerInternal = powerManagerInternal; mCompanionServicesRegister = new CompanionServicesRegister(); mBoundCompanionApplications = new AndroidPackageMap<>(); mScheduledForRebindingCompanionApplications = new AndroidPackageMap<>(); } /** * On package changed. */ public void onPackagesChanged(@UserIdInt int userId) { void onPackagesChanged(@UserIdInt int userId) { mCompanionServicesRegister.invalidate(userId); } Loading @@ -116,14 +117,16 @@ public class CompanionAppBinder { * CDM binds to the companion app. */ public void bindCompanionApplication(@UserIdInt int userId, @NonNull String packageName, boolean isSelfManaged, CompanionServiceConnector.Listener listener) { Slog.i(TAG, "Binding user=[" + userId + "], package=[" + packageName + "], isSelfManaged=[" + isSelfManaged + "]..."); boolean isSelfManaged) { if (DEBUG) { Log.i(TAG, "bind() u" + userId + "/" + packageName + " isSelfManaged=" + isSelfManaged); } final List<ComponentName> companionServices = mCompanionServicesRegister.forPackage(userId, packageName); if (companionServices.isEmpty()) { Slog.e(TAG, "Can not bind companion applications u" + userId + "/" + packageName + ": " Slog.w(TAG, "Can not bind companion applications u" + userId + "/" + packageName + ": " + "eligible CompanionDeviceService not found.\n" + "A CompanionDeviceService should declare an intent-filter for " + "\"android.companion.CompanionDeviceService\" action and require " Loading @@ -131,16 +134,16 @@ public class CompanionAppBinder { return; } final List<CompanionServiceConnector> serviceConnectors = new ArrayList<>(); final List<CompanionDeviceServiceConnector> serviceConnectors = new ArrayList<>(); synchronized (mBoundCompanionApplications) { if (mBoundCompanionApplications.containsValueForPackage(userId, packageName)) { Slog.w(TAG, "The package is ALREADY bound."); if (DEBUG) Log.e(TAG, "u" + userId + "/" + packageName + " is ALREADY bound."); return; } for (int i = 0; i < companionServices.size(); i++) { boolean isPrimary = i == 0; serviceConnectors.add(CompanionServiceConnector.newInstance(mContext, userId, serviceConnectors.add(CompanionDeviceServiceConnector.newInstance(mContext, userId, companionServices.get(i), isSelfManaged, isPrimary)); } Loading @@ -148,12 +151,12 @@ public class CompanionAppBinder { } // Set listeners for both Primary and Secondary connectors. for (CompanionServiceConnector serviceConnector : serviceConnectors) { serviceConnector.setListener(listener); for (CompanionDeviceServiceConnector serviceConnector : serviceConnectors) { serviceConnector.setListener(this::onBinderDied); } // Now "bind" all the connectors: the primary one and the rest of them. for (CompanionServiceConnector serviceConnector : serviceConnectors) { for (CompanionDeviceServiceConnector serviceConnector : serviceConnectors) { serviceConnector.connect(); } } Loading @@ -162,9 +165,9 @@ public class CompanionAppBinder { * CDM unbinds the companion app. */ public void unbindCompanionApplication(@UserIdInt int userId, @NonNull String packageName) { Slog.i(TAG, "Unbinding user=[" + userId + "], package=[" + packageName + "]..."); if (DEBUG) Log.i(TAG, "unbind() u" + userId + "/" + packageName); final List<CompanionServiceConnector> serviceConnectors; final List<CompanionDeviceServiceConnector> serviceConnectors; synchronized (mBoundCompanionApplications) { serviceConnectors = mBoundCompanionApplications.removePackage(userId, packageName); Loading @@ -175,11 +178,15 @@ public class CompanionAppBinder { } if (serviceConnectors == null) { Slog.e(TAG, "The package is not bound."); if (DEBUG) { Log.e(TAG, "unbindCompanionApplication(): " + "u" + userId + "/" + packageName + " is NOT bound"); Log.d(TAG, "Stacktrace", new Throwable()); } return; } for (CompanionServiceConnector serviceConnector : serviceConnectors) { for (CompanionDeviceServiceConnector serviceConnector : serviceConnectors) { serviceConnector.postUnbind(); } } Loading @@ -193,25 +200,15 @@ public class CompanionAppBinder { } } /** * Remove bound apps for package. */ public void removePackage(int userId, String packageName) { synchronized (mBoundCompanionApplications) { mBoundCompanionApplications.removePackage(userId, packageName); } } /** * Schedule rebinding for the package. */ public void scheduleRebinding(@UserIdInt int userId, @NonNull String packageName, CompanionServiceConnector serviceConnector) { private void scheduleRebinding(@UserIdInt int userId, @NonNull String packageName, CompanionDeviceServiceConnector serviceConnector) { Slog.i(TAG, "scheduleRebinding() " + userId + "/" + packageName); if (isRebindingCompanionApplicationScheduled(userId, packageName)) { Slog.i(TAG, "CompanionApplication rebinding has been scheduled, skipping " if (DEBUG) { Log.i(TAG, "CompanionApplication rebinding has been scheduled, skipping " + serviceConnector.getComponentName()); } return; } Loading @@ -224,8 +221,7 @@ public class CompanionAppBinder { // Rebinding in 10 seconds. Handler.getMain().postDelayed(() -> onRebindingCompanionApplicationTimeout(userId, packageName, serviceConnector), onRebindingCompanionApplicationTimeout(userId, packageName, serviceConnector), REBIND_TIMEOUT); } Loading @@ -239,12 +235,12 @@ public class CompanionAppBinder { private void onRebindingCompanionApplicationTimeout( @UserIdInt int userId, @NonNull String packageName, @NonNull CompanionServiceConnector serviceConnector) { @NonNull CompanionDeviceServiceConnector serviceConnector) { // Re-mark the application is bound. if (serviceConnector.isPrimary()) { synchronized (mBoundCompanionApplications) { if (!mBoundCompanionApplications.containsValueForPackage(userId, packageName)) { List<CompanionServiceConnector> serviceConnectors = List<CompanionDeviceServiceConnector> serviceConnectors = Collections.singletonList(serviceConnector); mBoundCompanionApplications.setValueForPackage(userId, packageName, serviceConnectors); Loading @@ -260,9 +256,114 @@ public class CompanionAppBinder { } /** * Dump bound apps. * Notify the app that the device appeared. * * @deprecated use {@link #notifyCompanionDevicePresenceEvent(AssociationInfo, int)} instead */ @Deprecated public void notifyCompanionApplicationDeviceAppeared(AssociationInfo association) { final int userId = association.getUserId(); final String packageName = association.getPackageName(); Slog.i(TAG, "notifyDevice_Appeared() id=" + association.getId() + " u" + userId + "/" + packageName); final CompanionDeviceServiceConnector primaryServiceConnector = getPrimaryServiceConnector(userId, packageName); if (primaryServiceConnector == null) { Slog.e(TAG, "notify_CompanionApplicationDevice_Appeared(): " + "u" + userId + "/" + packageName + " is NOT bound."); Slog.e(TAG, "Stacktrace", new Throwable()); return; } Log.i(TAG, "Calling onDeviceAppeared to userId=[" + userId + "] package=[" + packageName + "] associationId=[" + association.getId() + "]"); primaryServiceConnector.postOnDeviceAppeared(association); } /** * Notify the app that the device disappeared. * * @deprecated use {@link #notifyCompanionDevicePresenceEvent(AssociationInfo, int)} instead */ @Deprecated public void notifyCompanionApplicationDeviceDisappeared(AssociationInfo association) { final int userId = association.getUserId(); final String packageName = association.getPackageName(); Slog.i(TAG, "notifyDevice_Disappeared() id=" + association.getId() + " u" + userId + "/" + packageName); final CompanionDeviceServiceConnector primaryServiceConnector = getPrimaryServiceConnector(userId, packageName); if (primaryServiceConnector == null) { Slog.e(TAG, "notify_CompanionApplicationDevice_Disappeared(): " + "u" + userId + "/" + packageName + " is NOT bound."); Slog.e(TAG, "Stacktrace", new Throwable()); return; } Log.i(TAG, "Calling onDeviceDisappeared to userId=[" + userId + "] package=[" + packageName + "] associationId=[" + association.getId() + "]"); primaryServiceConnector.postOnDeviceDisappeared(association); } /** * Notify the app that the device appeared. */ public void notifyCompanionDevicePresenceEvent(AssociationInfo association, int event) { final int userId = association.getUserId(); final String packageName = association.getPackageName(); final CompanionDeviceServiceConnector primaryServiceConnector = getPrimaryServiceConnector(userId, packageName); final DevicePresenceEvent devicePresenceEvent = new DevicePresenceEvent(association.getId(), event, null); if (primaryServiceConnector == null) { Slog.e(TAG, "notifyCompanionApplicationDevicePresenceEvent(): " + "u" + userId + "/" + packageName + " event=[ " + event + " ] is NOT bound."); Slog.e(TAG, "Stacktrace", new Throwable()); return; } Slog.i(TAG, "Calling onDevicePresenceEvent() to userId=[" + userId + "] package=[" + packageName + "] associationId=[" + association.getId() + "] event=[" + event + "]"); primaryServiceConnector.postOnDevicePresenceEvent(devicePresenceEvent); } /** * Notify the app that the device disappeared. */ public void dump(@NonNull PrintWriter out) { public void notifyUuidDevicePresenceEvent(ObservableUuid uuid, int event) { final int userId = uuid.getUserId(); final ParcelUuid parcelUuid = uuid.getUuid(); final String packageName = uuid.getPackageName(); final CompanionDeviceServiceConnector primaryServiceConnector = getPrimaryServiceConnector(userId, packageName); final DevicePresenceEvent devicePresenceEvent = new DevicePresenceEvent(DevicePresenceEvent.NO_ASSOCIATION, event, parcelUuid); if (primaryServiceConnector == null) { Slog.e(TAG, "notifyApplicationDevicePresenceChanged(): " + "u" + userId + "/" + packageName + " event=[ " + event + " ] is NOT bound."); Slog.e(TAG, "Stacktrace", new Throwable()); return; } Slog.i(TAG, "Calling onDevicePresenceEvent() to userId=[" + userId + "] package=[" + packageName + "]" + "event= [" + event + "]"); primaryServiceConnector.postOnDevicePresenceEvent(devicePresenceEvent); } void dump(@NonNull PrintWriter out) { out.append("Companion Device Application Controller: \n"); synchronized (mBoundCompanionApplications) { Loading @@ -276,7 +377,6 @@ public class CompanionAppBinder { } out.append(" Companion Applications Scheduled For Rebinding: "); synchronized (mScheduledForRebindingCompanionApplications) { if (mScheduledForRebindingCompanionApplications.size() == 0) { out.append("<empty>\n"); } else { Loading @@ -284,18 +384,93 @@ public class CompanionAppBinder { mScheduledForRebindingCompanionApplications.dump(out); } } /** * Rebinding for Self-Managed secondary services OR Non-Self-Managed services. */ private void onBinderDied(@UserIdInt int userId, @NonNull String packageName, @NonNull CompanionDeviceServiceConnector serviceConnector) { boolean isPrimary = serviceConnector.isPrimary(); Slog.i(TAG, "onBinderDied() u" + userId + "/" + packageName + " isPrimary: " + isPrimary); // First, disable hint mode for Auto profile and mark not BOUND for primary service ONLY. if (isPrimary) { final List<AssociationInfo> associations = mAssociationStore.getActiveAssociationsByPackage(userId, packageName); for (AssociationInfo association : associations) { final String deviceProfile = association.getDeviceProfile(); if (DEVICE_PROFILE_AUTOMOTIVE_PROJECTION.equals(deviceProfile)) { Slog.i(TAG, "Disable hint mode for device profile: " + deviceProfile); mPowerManagerInternal.setPowerMode(Mode.AUTOMOTIVE_PROJECTION, false); break; } } @Nullable CompanionServiceConnector getPrimaryServiceConnector( synchronized (mBoundCompanionApplications) { mBoundCompanionApplications.removePackage(userId, packageName); } } // Second: schedule rebinding if needed. final boolean shouldScheduleRebind = shouldScheduleRebind(userId, packageName, isPrimary); if (shouldScheduleRebind) { scheduleRebinding(userId, packageName, serviceConnector); } } private @Nullable CompanionDeviceServiceConnector getPrimaryServiceConnector( @UserIdInt int userId, @NonNull String packageName) { final List<CompanionServiceConnector> connectors; final List<CompanionDeviceServiceConnector> connectors; synchronized (mBoundCompanionApplications) { connectors = mBoundCompanionApplications.getValueForPackage(userId, packageName); } return connectors != null ? connectors.get(0) : null; } private boolean shouldScheduleRebind(int userId, String packageName, boolean isPrimary) { // Make sure do not schedule rebind for the case ServiceConnector still gets callback after // app is uninstalled. boolean stillAssociated = false; // Make sure to clean up the state for all the associations // that associate with this package. boolean shouldScheduleRebind = false; boolean shouldScheduleRebindForUuid = false; final List<ObservableUuid> uuids = mObservableUuidStore.getObservableUuidsForPackage(userId, packageName); for (AssociationInfo ai : mAssociationStore.getActiveAssociationsByPackage(userId, packageName)) { final int associationId = ai.getId(); stillAssociated = true; if (ai.isSelfManaged()) { // Do not rebind if primary one is died for selfManaged application. if (isPrimary && mDevicePresenceMonitor.isDevicePresent(associationId)) { mDevicePresenceMonitor.onSelfManagedDeviceReporterBinderDied(associationId); shouldScheduleRebind = false; } // Do not rebind if both primary and secondary services are died for // selfManaged application. shouldScheduleRebind = isCompanionApplicationBound(userId, packageName); } else if (ai.isNotifyOnDeviceNearby()) { // Always rebind for non-selfManaged devices. shouldScheduleRebind = true; } } for (ObservableUuid uuid : uuids) { if (mDevicePresenceMonitor.isDeviceUuidPresent(uuid.getUuid())) { shouldScheduleRebindForUuid = true; break; } } return (stillAssociated && shouldScheduleRebind) || shouldScheduleRebindForUuid; } private class CompanionServicesRegister extends PerUser<Map<String, List<ComponentName>>> { @Override public synchronized @NonNull Map<String, List<ComponentName>> forUser( Loading