From 57999bac7893330e3c828b67e5870631f621028d Mon Sep 17 00:00:00 2001 From: Kathy Chen Date: Thu, 21 Apr 2022 13:16:40 -0700 Subject: [PATCH] Stabalize AmbientContext detection by restoring client requests. If the underlying detection service restarts or package updates, resume detection for cached requests. Move cached client data from PerUserService to MasterService, since the former can be re-instantiated when the underlyig detection service gets destroyed. If the AiAi component is not available, return STATUS_NOT_SUPPORTED. Bug: 229028987 Test: Flash device and restart/re-install AiAi service to verify detection is restored. Install AiAi apk without detection service, and verify STATUS_NOT_SUPPORTED is returned. Change-Id: I0b583da07fa0d406c32f0c44a0270f3f81977624 --- .../AmbientContextManagerPerUserService.java | 87 +++++-------- .../AmbientContextManagerService.java | 122 ++++++++++++++++++ 2 files changed, 156 insertions(+), 53 deletions(-) diff --git a/services/core/java/com/android/server/ambientcontext/AmbientContextManagerPerUserService.java b/services/core/java/com/android/server/ambientcontext/AmbientContextManagerPerUserService.java index 766283b92d917..028288f73564a 100644 --- a/services/core/java/com/android/server/ambientcontext/AmbientContextManagerPerUserService.java +++ b/services/core/java/com/android/server/ambientcontext/AmbientContextManagerPerUserService.java @@ -43,7 +43,6 @@ import android.os.RemoteException; import android.service.ambientcontext.AmbientContextDetectionResult; import android.service.ambientcontext.AmbientContextDetectionServiceStatus; import android.text.TextUtils; -import android.util.ArraySet; import android.util.IndentingPrintWriter; import android.util.Slog; @@ -54,7 +53,6 @@ import com.android.server.infra.AbstractPerUserSystemService; import java.io.PrintWriter; import java.util.ArrayList; import java.util.Arrays; -import java.util.Set; /** * Per-user manager service for {@link AmbientContextEvent}s. @@ -69,19 +67,13 @@ final class AmbientContextManagerPerUserService extends RemoteAmbientContextDetectionService mRemoteService; private ComponentName mComponentName; - private Set mExistingPendingIntents; AmbientContextManagerPerUserService( @NonNull AmbientContextManagerService master, Object lock, @UserIdInt int userId) { super(master, lock, userId); - mExistingPendingIntents = new ArraySet<>(); } void destroyLocked() { - if (isVerbose()) { - Slog.v(TAG, "destroyLocked()"); - } - Slog.d(TAG, "Trying to cancel the remote request. Reason: Service destroyed."); if (mRemoteService != null) { synchronized (mLock) { @@ -118,7 +110,19 @@ final class AmbientContextManagerPerUserService extends if (mComponentName == null) { mComponentName = updateServiceInfoLocked(); } - return mComponentName != null; + if (mComponentName == null) { + return false; + } + + ServiceInfo serviceInfo; + try { + serviceInfo = AppGlobals.getPackageManager().getServiceInfo( + mComponentName, 0, mUserId); + } catch (RemoteException e) { + Slog.w(TAG, "RemoteException while setting up service"); + return false; + } + return serviceInfo != null; } @Override @@ -171,18 +175,10 @@ final class AmbientContextManagerPerUserService extends return; } - // Remove any existing PendingIntent for this package. - String callingPackage = pendingIntent.getCreatorPackage(); - PendingIntent duplicatePendingIntent = findExistingRequestByPackage(callingPackage); - if (duplicatePendingIntent != null) { - Slog.d(TAG, "Replace duplicate request from " + callingPackage); - mExistingPendingIntents.remove(duplicatePendingIntent); - } - - // Register package and add pendingIntent to mExistingPendingIntents - startDetection(request, callingPackage, createDetectionResultRemoteCallback(), - getServerStatusCallback(clientStatusCallback)); - mExistingPendingIntents.add(pendingIntent); + // Register package and add to existing ClientRequests cache + startDetection(request, pendingIntent.getCreatorPackage(), + createDetectionResultRemoteCallback(), clientStatusCallback); + mMaster.newClientAdded(mUserId, request, pendingIntent, clientStatusCallback); } } @@ -214,15 +210,17 @@ final class AmbientContextManagerPerUserService extends @VisibleForTesting void startDetection(AmbientContextEventRequest request, String callingPackage, - RemoteCallback detectionResultCallback, RemoteCallback statusCallback) { + RemoteCallback detectionResultCallback, RemoteCallback clientStatusCallback) { Slog.d(TAG, "Requested detection of " + request.getEventTypes()); synchronized (mLock) { if (setUpServiceIfNeeded()) { ensureRemoteServiceInitiated(); mRemoteService.startDetection(request, callingPackage, detectionResultCallback, - statusCallback); + getServerStatusCallback(clientStatusCallback)); } else { Slog.w(TAG, "No valid component found for AmbientContextDetectionService"); + sendStatusToCallback(clientStatusCallback, + AmbientContextManager.STATUS_NOT_SUPPORTED); } } } @@ -245,15 +243,8 @@ final class AmbientContextManagerPerUserService extends */ public void onUnregisterObserver(String callingPackage) { synchronized (mLock) { - PendingIntent pendingIntent = findExistingRequestByPackage(callingPackage); - if (pendingIntent == null) { - Slog.d(TAG, "No registration found for " + callingPackage); - return; - } - - // Remove from existing requests - mExistingPendingIntents.remove(pendingIntent); - stopDetection(pendingIntent.getCreatorPackage()); + stopDetection(callingPackage); + mMaster.clientRemoved(mUserId, callingPackage); } } @@ -265,7 +256,7 @@ final class AmbientContextManagerPerUserService extends if (!setUpServiceIfNeeded()) { Slog.w(TAG, "Detection service is not available at this moment."); sendStatusToCallback(statusCallback, - AmbientContextManager.STATUS_SERVICE_UNAVAILABLE); + AmbientContextManager.STATUS_NOT_SUPPORTED); return; } ensureRemoteServiceInitiated(); @@ -382,16 +373,6 @@ final class AmbientContextManagerPerUserService extends } } - @Nullable - private PendingIntent findExistingRequestByPackage(String callingPackage) { - for (PendingIntent pendingIntent : mExistingPendingIntents) { - if (pendingIntent.getCreatorPackage().equals(callingPackage)) { - return pendingIntent; - } - } - return null; - } - /** * Sends out the Intent to the client after the event is detected. * @@ -418,22 +399,22 @@ final class AmbientContextManagerPerUserService extends } @NonNull - private RemoteCallback createDetectionResultRemoteCallback() { + RemoteCallback createDetectionResultRemoteCallback() { return new RemoteCallback(result -> { AmbientContextDetectionResult detectionResult = (AmbientContextDetectionResult) result.get( AmbientContextDetectionResult.RESULT_RESPONSE_BUNDLE_KEY); + String packageName = detectionResult.getPackageName(); + PendingIntent pendingIntent = mMaster.getPendingIntent(mUserId, packageName); + if (pendingIntent == null) { + return; + } + final long token = Binder.clearCallingIdentity(); try { - for (PendingIntent pendingIntent : mExistingPendingIntents) { - // Send PendingIntent to requesting packages - String creatorPackage = pendingIntent.getCreatorPackage(); - if (detectionResult.getPackageName().equals(creatorPackage)) { - sendDetectionResultIntent(pendingIntent, detectionResult); - Slog.i(TAG, "Got detection result of " + detectionResult.getEvents() - + " for " + creatorPackage); - } - } + sendDetectionResultIntent(pendingIntent, detectionResult); + Slog.i(TAG, "Got detection result of " + detectionResult.getEvents() + + " for " + packageName); } finally { Binder.restoreCallingIdentity(token); } diff --git a/services/core/java/com/android/server/ambientcontext/AmbientContextManagerService.java b/services/core/java/com/android/server/ambientcontext/AmbientContextManagerService.java index cfca7ec6f568f..4206262398c7d 100644 --- a/services/core/java/com/android/server/ambientcontext/AmbientContextManagerService.java +++ b/services/core/java/com/android/server/ambientcontext/AmbientContextManagerService.java @@ -20,6 +20,7 @@ import static android.provider.DeviceConfig.NAMESPACE_AMBIENT_CONTEXT_MANAGER_SE import android.Manifest; import android.annotation.NonNull; +import android.annotation.Nullable; import android.annotation.UserIdInt; import android.app.PendingIntent; import android.app.ambientcontext.AmbientContextEvent; @@ -34,6 +35,7 @@ import android.os.ResultReceiver; import android.os.ShellCallback; import android.os.UserHandle; import android.provider.DeviceConfig; +import android.util.ArraySet; import android.util.Slog; import com.android.internal.R; @@ -60,9 +62,50 @@ public class AmbientContextManagerService extends /** Default value in absence of {@link DeviceConfig} override. */ private static final boolean DEFAULT_SERVICE_ENABLED = true; + public static final int MAX_TEMPORARY_SERVICE_DURATION_MS = 30000; + + static class ClientRequest { + private final int mUserId; + private final AmbientContextEventRequest mRequest; + private final PendingIntent mPendingIntent; + private final RemoteCallback mClientStatusCallback; + + ClientRequest(int userId, AmbientContextEventRequest request, + PendingIntent pendingIntent, RemoteCallback clientStatusCallback) { + this.mUserId = userId; + this.mRequest = request; + this.mPendingIntent = pendingIntent; + this.mClientStatusCallback = clientStatusCallback; + } + + String getPackageName() { + return mPendingIntent.getCreatorPackage(); + } + + AmbientContextEventRequest getRequest() { + return mRequest; + } + + PendingIntent getPendingIntent() { + return mPendingIntent; + } + + RemoteCallback getClientStatusCallback() { + return mClientStatusCallback; + } + + boolean hasUserId(int userId) { + return mUserId == userId; + } + + boolean hasUserIdAndPackageName(int userId, String packageName) { + return (userId == mUserId) && packageName.equals(getPackageName()); + } + } private final Context mContext; boolean mIsServiceEnabled; + private Set mExistingClientRequests; public AmbientContextManagerService(Context context) { super(context, @@ -73,6 +116,7 @@ public class AmbientContextManagerService extends PACKAGE_UPDATE_POLICY_REFRESH_EAGER | /*To avoid high latency*/ PACKAGE_RESTART_POLICY_REFRESH_EAGER); mContext = context; + mExistingClientRequests = new ArraySet<>(); } @Override @@ -94,6 +138,44 @@ public class AmbientContextManagerService extends } } + void newClientAdded(int userId, AmbientContextEventRequest request, + PendingIntent pendingIntent, RemoteCallback clientStatusCallback) { + Slog.d(TAG, "New client added: " + pendingIntent.getCreatorPackage()); + + // Remove any existing ClientRequest for this user and package. + mExistingClientRequests.removeAll( + findExistingRequests(userId, pendingIntent.getCreatorPackage())); + + // Add to existing ClientRequests + mExistingClientRequests.add( + new ClientRequest(userId, request, pendingIntent, clientStatusCallback)); + } + + void clientRemoved(int userId, String packageName) { + Slog.d(TAG, "Remove client: " + packageName); + mExistingClientRequests.removeAll(findExistingRequests(userId, packageName)); + } + + private Set findExistingRequests(int userId, String packageName) { + Set existingRequests = new ArraySet<>(); + for (ClientRequest clientRequest : mExistingClientRequests) { + if (clientRequest.hasUserIdAndPackageName(userId, packageName)) { + existingRequests.add(clientRequest); + } + } + return existingRequests; + } + + @Nullable + PendingIntent getPendingIntent(int userId, String packageName) { + for (ClientRequest clientRequest : mExistingClientRequests) { + if (clientRequest.hasUserIdAndPackageName(userId, packageName)) { + return clientRequest.getPendingIntent(); + } + } + return null; + } + private void onDeviceConfigChange(@NonNull Set keys) { if (keys.contains(KEY_SERVICE_ENABLED)) { mIsServiceEnabled = DeviceConfig.getBoolean( @@ -111,9 +193,33 @@ public class AmbientContextManagerService extends @Override protected void onServiceRemoved( AmbientContextManagerPerUserService service, @UserIdInt int userId) { + Slog.d(TAG, "onServiceRemoved"); service.destroyLocked(); } + @Override + protected void onServicePackageRestartedLocked(@UserIdInt int userId) { + Slog.d(TAG, "Restoring remote request. Reason: Service package restarted."); + restorePreviouslyEnabledClients(userId); + } + + @Override + protected void onServicePackageUpdatedLocked(@UserIdInt int userId) { + Slog.d(TAG, "Restoring remote request. Reason: Service package updated."); + restorePreviouslyEnabledClients(userId); + } + + @Override + protected void enforceCallingPermissionForManagement() { + getContext().enforceCallingPermission( + Manifest.permission.ACCESS_AMBIENT_CONTEXT_EVENT, TAG); + } + + @Override + protected int getMaximumTemporaryServiceDurationMs() { + return MAX_TEMPORARY_SERVICE_DURATION_MS; + } + /** Returns {@code true} if the detection service is configured on this device. */ public static boolean isDetectionServiceConfigured() { final PackageManagerInternal pmi = LocalServices.getService(PackageManagerInternal.class); @@ -182,6 +288,22 @@ public class AmbientContextManagerService extends } } + private void restorePreviouslyEnabledClients(int userId) { + synchronized (mLock) { + final AmbientContextManagerPerUserService service = getServiceForUserLocked(userId); + for (ClientRequest clientRequest : mExistingClientRequests) { + // Start detection for previously enabled clients + if (clientRequest.hasUserId(userId)) { + Slog.d(TAG, "Restoring detection for " + clientRequest.getPackageName()); + service.startDetection(clientRequest.getRequest(), + clientRequest.getPackageName(), + service.createDetectionResultRemoteCallback(), + clientRequest.getClientStatusCallback()); + } + } + } + } + /** * Returns the AmbientContextManagerPerUserService component for this user. */