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
This commit is contained in:
Kathy Chen
2022-04-21 13:16:40 -07:00
parent b45b8bdc94
commit 57999bac78
2 changed files with 156 additions and 53 deletions

View File

@@ -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<PendingIntent> 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);
}

View File

@@ -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<ClientRequest> 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<ClientRequest> findExistingRequests(int userId, String packageName) {
Set<ClientRequest> 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<String> 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.
*/