From 81f52b053da67df6332a3177c7c7f9e266718517 Mon Sep 17 00:00:00 2001 From: Lili Zou Date: Mon, 8 Aug 2022 17:19:46 +0800 Subject: [PATCH] AmbientContextManager API changes to support quick-tap detection Test: "atest CtsAmbientContextServiceTestCases", "atest FrameworksServicesTests:AmbientContextManagerServiceTest" BUG: 242480433 Ignore-AOSP-First: to prevent new feature leak. Change-Id: If445894f0d68fbb336156d2817e0dea884182a10 --- .../AmbientContextCallback.java | 47 ++++++++++ .../ambientcontext/AmbientContextEvent.java | 19 +++- .../ambientcontext/AmbientContextManager.java | 68 ++++++++++++++- .../IAmbientContextManager.aidl | 6 ++ .../IAmbientContextObserver.aidl | 30 +++++++ .../AmbientContextManagerPerUserService.java | 86 +++++++++---------- .../AmbientContextManagerService.java | 81 +++++++++++------ .../AmbientContextShellCommand.java | 38 ++++---- .../AmbientContextManagerServiceTest.java | 16 +++- 9 files changed, 295 insertions(+), 96 deletions(-) create mode 100644 core/java/android/app/ambientcontext/AmbientContextCallback.java create mode 100644 core/java/android/app/ambientcontext/IAmbientContextObserver.aidl diff --git a/core/java/android/app/ambientcontext/AmbientContextCallback.java b/core/java/android/app/ambientcontext/AmbientContextCallback.java new file mode 100644 index 0000000000000..9133d7fe6634b --- /dev/null +++ b/core/java/android/app/ambientcontext/AmbientContextCallback.java @@ -0,0 +1,47 @@ +/* + * 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 android.app.ambientcontext; + +import android.annotation.NonNull; + +import java.util.List; +import java.util.concurrent.Executor; + +/** + * Callback for listening to Ambient Context events and status changes. See {@link + * AmbientContextManager#registerObserver(AmbientContextEventRequest, AmbientContextCallback, + * Executor)} + * + * @hide + */ +public interface AmbientContextCallback { + /** + * Called when AmbientContextManager service detects events. + * + * @param events a list of detected events. + */ + void onEvents(@NonNull List events); + + /** + * Called with a statusCode when + * {@link AmbientContextManager#registerObserver(AmbientContextEventRequest, + * Executor, AmbientContextCallback)} completes, to indicate if the registration is successful + * + * @param statusCode the status of the service. + */ + void onRegistrationComplete(@NonNull @AmbientContextManager.StatusCode int statusCode); +} diff --git a/core/java/android/app/ambientcontext/AmbientContextEvent.java b/core/java/android/app/ambientcontext/AmbientContextEvent.java index 11e695ad7fad8..af48fde6423a7 100644 --- a/core/java/android/app/ambientcontext/AmbientContextEvent.java +++ b/core/java/android/app/ambientcontext/AmbientContextEvent.java @@ -59,11 +59,21 @@ public final class AmbientContextEvent implements Parcelable { */ public static final int EVENT_SNORE = 2; + /** + * The integer indicating a double-tap event was detected. + * For detecting this event type, there's no specific consent activity to request access, but + * the consent is implied through the double tap toggle in the Settings app. + * + * @hide + */ + public static final int EVENT_BACK_DOUBLE_TAP = 3; + /** @hide */ @IntDef(prefix = { "EVENT_" }, value = { EVENT_UNKNOWN, EVENT_COUGH, EVENT_SNORE, + EVENT_BACK_DOUBLE_TAP, }) public @interface EventCode {} /** The integer indicating an unknown level. */ @@ -150,7 +160,8 @@ public final class AmbientContextEvent implements Parcelable { @IntDef(prefix = "EVENT_", value = { EVENT_UNKNOWN, EVENT_COUGH, - EVENT_SNORE + EVENT_SNORE, + EVENT_BACK_DOUBLE_TAP }) @Retention(RetentionPolicy.SOURCE) @DataClass.Generated.Member @@ -166,6 +177,8 @@ public final class AmbientContextEvent implements Parcelable { return "EVENT_COUGH"; case EVENT_SNORE: return "EVENT_SNORE"; + case EVENT_BACK_DOUBLE_TAP: + return "EVENT_BACK_DOUBLE_TAP"; default: return Integer.toHexString(value); } } @@ -478,10 +491,10 @@ public final class AmbientContextEvent implements Parcelable { } @DataClass.Generated( - time = 1642040319323L, + time = 1659950304931L, codegenVersion = "1.0.23", sourceFile = "frameworks/base/core/java/android/app/ambientcontext/AmbientContextEvent.java", - inputSignatures = "public static final int EVENT_UNKNOWN\npublic static final int EVENT_COUGH\npublic static final int EVENT_SNORE\npublic static final int LEVEL_UNKNOWN\npublic static final int LEVEL_LOW\npublic static final int LEVEL_MEDIUM_LOW\npublic static final int LEVEL_MEDIUM\npublic static final int LEVEL_MEDIUM_HIGH\npublic static final int LEVEL_HIGH\nprivate final @android.app.ambientcontext.AmbientContextEvent.EventCode int mEventType\nprivate final @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInstant.class) @android.annotation.NonNull java.time.Instant mStartTime\nprivate final @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInstant.class) @android.annotation.NonNull java.time.Instant mEndTime\nprivate final @android.app.ambientcontext.AmbientContextEvent.LevelValue int mConfidenceLevel\nprivate final @android.app.ambientcontext.AmbientContextEvent.LevelValue int mDensityLevel\nprivate static int defaultEventType()\nprivate static @android.annotation.NonNull java.time.Instant defaultStartTime()\nprivate static @android.annotation.NonNull java.time.Instant defaultEndTime()\nprivate static int defaultConfidenceLevel()\nprivate static int defaultDensityLevel()\nclass AmbientContextEvent extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genBuilder=true, genConstructor=false, genHiddenConstDefs=true, genParcelable=true, genToString=true)") + inputSignatures = "public static final int EVENT_UNKNOWN\npublic static final int EVENT_COUGH\npublic static final int EVENT_SNORE\npublic static final int EVENT_BACK_DOUBLE_TAP\npublic static final int LEVEL_UNKNOWN\npublic static final int LEVEL_LOW\npublic static final int LEVEL_MEDIUM_LOW\npublic static final int LEVEL_MEDIUM\npublic static final int LEVEL_MEDIUM_HIGH\npublic static final int LEVEL_HIGH\nprivate final @android.app.ambientcontext.AmbientContextEvent.EventCode int mEventType\nprivate final @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInstant.class) @android.annotation.NonNull java.time.Instant mStartTime\nprivate final @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInstant.class) @android.annotation.NonNull java.time.Instant mEndTime\nprivate final @android.app.ambientcontext.AmbientContextEvent.LevelValue int mConfidenceLevel\nprivate final @android.app.ambientcontext.AmbientContextEvent.LevelValue int mDensityLevel\nprivate static int defaultEventType()\nprivate static @android.annotation.NonNull java.time.Instant defaultStartTime()\nprivate static @android.annotation.NonNull java.time.Instant defaultEndTime()\nprivate static int defaultConfidenceLevel()\nprivate static int defaultDensityLevel()\nclass AmbientContextEvent extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genBuilder=true, genConstructor=false, genHiddenConstDefs=true, genParcelable=true, genToString=true)") @Deprecated private void __metadata() {} diff --git a/core/java/android/app/ambientcontext/AmbientContextManager.java b/core/java/android/app/ambientcontext/AmbientContextManager.java index dd1dd0c75f76a..e45dfdf3dc493 100644 --- a/core/java/android/app/ambientcontext/AmbientContextManager.java +++ b/core/java/android/app/ambientcontext/AmbientContextManager.java @@ -282,7 +282,7 @@ public final class AmbientContextManager { Preconditions.checkArgument(!resultPendingIntent.isImmutable()); try { RemoteCallback callback = new RemoteCallback(result -> { - int statusCode = result.getInt(STATUS_RESPONSE_BUNDLE_KEY); + int statusCode = result.getInt(STATUS_RESPONSE_BUNDLE_KEY); final long identity = Binder.clearCallingIdentity(); try { executor.execute(() -> statusConsumer.accept(statusCode)); @@ -296,6 +296,72 @@ public final class AmbientContextManager { } } + /** + * Allows app to register as a {@link AmbientContextEvent} observer. Same as {@link + * #registerObserver(AmbientContextEventRequest, PendingIntent, Executor, Consumer)}, + * but use {@link AmbientContextCallback} instead of {@link PendingIntent} as a callback on + * detected events. + * Registering another observer from the same package that has already been + * registered will override the previous observer. If the same app previously calls + * {@link #registerObserver(AmbientContextEventRequest, AmbientContextCallback, Executor)}, + * and now calls + * {@link #registerObserver(AmbientContextEventRequest, PendingIntent, Executor, Consumer)}, + * the previous observer will be replaced with the new observer with the PendingIntent callback. + * Or vice versa. + * + * When the registration completes, a status will be returned to client through + * {@link AmbientContextCallback#onRegistrationComplete(int)}. + * If the AmbientContextManager service is not enabled yet, or the underlying detection service + * is not running yet, {@link AmbientContextManager#STATUS_SERVICE_UNAVAILABLE} will be + * returned, and the detection won't be really started. + * If the underlying detection service feature is not enabled, or the requested event type is + * not enabled yet, {@link AmbientContextManager#STATUS_NOT_SUPPORTED} will be returned, and the + * detection won't be really started. + * If there is no user consent, {@link AmbientContextManager#STATUS_ACCESS_DENIED} will be + * returned, and the detection won't be really started. + * Otherwise, it will try to start the detection. And if it starts successfully, it will return + * {@link AmbientContextManager#STATUS_SUCCESS}. If it fails to start the detection, then + * it will return {@link AmbientContextManager#STATUS_SERVICE_UNAVAILABLE} + * After registerObserver succeeds and when the service detects an event, the service will + * trigger {@link AmbientContextCallback#onEvents(List)}. + * + * @hide + */ + @RequiresPermission(Manifest.permission.ACCESS_AMBIENT_CONTEXT_EVENT) + public void registerObserver( + @NonNull AmbientContextEventRequest request, + @NonNull @CallbackExecutor Executor executor, + @NonNull AmbientContextCallback ambientContextCallback) { + try { + IAmbientContextObserver observer = new IAmbientContextObserver.Stub() { + @Override + public void onEvents(List events) throws RemoteException { + final long identity = Binder.clearCallingIdentity(); + try { + executor.execute(() -> ambientContextCallback.onEvents(events)); + } finally { + Binder.restoreCallingIdentity(identity); + } + } + + @Override + public void onRegistrationComplete(int statusCode) throws RemoteException { + final long identity = Binder.clearCallingIdentity(); + try { + executor.execute( + () -> ambientContextCallback.onRegistrationComplete(statusCode)); + } finally { + Binder.restoreCallingIdentity(identity); + } + } + }; + + mService.registerObserverWithCallback(request, mContext.getPackageName(), observer); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + /** * Unregisters the requesting app as an {@code AmbientContextEvent} observer. Unregistering an * observer that was already unregistered or never registered will have no effect. diff --git a/core/java/android/app/ambientcontext/IAmbientContextManager.aidl b/core/java/android/app/ambientcontext/IAmbientContextManager.aidl index 0d9ecfdfe8f65..8f06e76db5595 100644 --- a/core/java/android/app/ambientcontext/IAmbientContextManager.aidl +++ b/core/java/android/app/ambientcontext/IAmbientContextManager.aidl @@ -18,6 +18,7 @@ package android.app.ambientcontext; import android.app.PendingIntent; import android.app.ambientcontext.AmbientContextEventRequest; +import android.app.ambientcontext.IAmbientContextObserver; import android.os.RemoteCallback; /** @@ -29,6 +30,11 @@ interface IAmbientContextManager { void registerObserver(in AmbientContextEventRequest request, in PendingIntent resultPendingIntent, in RemoteCallback statusCallback); + + @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.ACCESS_AMBIENT_CONTEXT_EVENT)") + void registerObserverWithCallback(in AmbientContextEventRequest request, + String packageName, + in IAmbientContextObserver observer); void unregisterObserver(in String callingPackage); void queryServiceStatus(in int[] eventTypes, in String callingPackage, in RemoteCallback statusCallback); diff --git a/core/java/android/app/ambientcontext/IAmbientContextObserver.aidl b/core/java/android/app/ambientcontext/IAmbientContextObserver.aidl new file mode 100644 index 0000000000000..529e2fea7edce --- /dev/null +++ b/core/java/android/app/ambientcontext/IAmbientContextObserver.aidl @@ -0,0 +1,30 @@ +/* + * 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 android.app.ambientcontext; + +import android.app.ambientcontext.AmbientContextEvent; + +/** + * Callback interface of AmbientContextManager. + * + * @hide + */ +oneway interface IAmbientContextObserver { + void onEvents(in List events); + void onRegistrationComplete(in int statusCode); +} + diff --git a/services/core/java/com/android/server/ambientcontext/AmbientContextManagerPerUserService.java b/services/core/java/com/android/server/ambientcontext/AmbientContextManagerPerUserService.java index 028288f73564a..dcadd5ff518c2 100644 --- a/services/core/java/com/android/server/ambientcontext/AmbientContextManagerPerUserService.java +++ b/services/core/java/com/android/server/ambientcontext/AmbientContextManagerPerUserService.java @@ -29,6 +29,7 @@ import android.app.PendingIntent; import android.app.ambientcontext.AmbientContextEvent; import android.app.ambientcontext.AmbientContextEventRequest; import android.app.ambientcontext.AmbientContextManager; +import android.app.ambientcontext.IAmbientContextObserver; import android.content.ActivityNotFoundException; import android.content.ComponentName; import android.content.Context; @@ -53,6 +54,8 @@ import com.android.server.infra.AbstractPerUserSystemService; import java.io.PrintWriter; import java.util.ArrayList; import java.util.Arrays; +import java.util.List; +import java.util.function.Consumer; /** * Per-user manager service for {@link AmbientContextEvent}s. @@ -165,20 +168,17 @@ final class AmbientContextManagerPerUserService extends * package. A new registration from the same package will overwrite the previous registration. */ public void onRegisterObserver(AmbientContextEventRequest request, - PendingIntent pendingIntent, RemoteCallback clientStatusCallback) { + String packageName, IAmbientContextObserver observer) { synchronized (mLock) { if (!setUpServiceIfNeeded()) { Slog.w(TAG, "Detection service is not available at this moment."); - sendStatusCallback( - clientStatusCallback, - AmbientContextManager.STATUS_SERVICE_UNAVAILABLE); + completeRegistration(observer, AmbientContextManager.STATUS_SERVICE_UNAVAILABLE); return; } // Register package and add to existing ClientRequests cache - startDetection(request, pendingIntent.getCreatorPackage(), - createDetectionResultRemoteCallback(), clientStatusCallback); - mMaster.newClientAdded(mUserId, request, pendingIntent, clientStatusCallback); + startDetection(request, packageName, observer); + mMaster.newClientAdded(mUserId, request, packageName, observer); } } @@ -186,49 +186,46 @@ final class AmbientContextManagerPerUserService extends * Returns a RemoteCallback that handles the status from the detection service, and * sends results to the client callback. */ - private RemoteCallback getServerStatusCallback(RemoteCallback clientStatusCallback) { + private RemoteCallback getServerStatusCallback(Consumer statusConsumer) { return new RemoteCallback(result -> { AmbientContextDetectionServiceStatus serviceStatus = (AmbientContextDetectionServiceStatus) result.get( AmbientContextDetectionServiceStatus.STATUS_RESPONSE_BUNDLE_KEY); final long token = Binder.clearCallingIdentity(); try { - String packageName = serviceStatus.getPackageName(); - Bundle bundle = new Bundle(); - bundle.putInt( - AmbientContextManager.STATUS_RESPONSE_BUNDLE_KEY, - serviceStatus.getStatusCode()); - clientStatusCallback.sendResult(bundle); int statusCode = serviceStatus.getStatusCode(); + statusConsumer.accept(statusCode); Slog.i(TAG, "Got detection status of " + statusCode - + " for " + packageName); + + " for " + serviceStatus.getPackageName()); } finally { Binder.restoreCallingIdentity(token); } }); } - @VisibleForTesting void startDetection(AmbientContextEventRequest request, String callingPackage, - RemoteCallback detectionResultCallback, RemoteCallback clientStatusCallback) { + IAmbientContextObserver observer) { Slog.d(TAG, "Requested detection of " + request.getEventTypes()); synchronized (mLock) { if (setUpServiceIfNeeded()) { ensureRemoteServiceInitiated(); - mRemoteService.startDetection(request, callingPackage, detectionResultCallback, - getServerStatusCallback(clientStatusCallback)); + mRemoteService.startDetection(request, callingPackage, + createDetectionResultRemoteCallback(), + getServerStatusCallback( + statusCode -> completeRegistration(observer, statusCode))); } else { Slog.w(TAG, "No valid component found for AmbientContextDetectionService"); - sendStatusToCallback(clientStatusCallback, + completeRegistration(observer, AmbientContextManager.STATUS_NOT_SUPPORTED); } } } /** - * Sends an intent with a status code and empty events. + * Sends the result response with the specified status to the callback. */ - void sendStatusCallback(RemoteCallback statusCallback, int statusCode) { + static void sendStatusCallback(RemoteCallback statusCallback, + @AmbientContextManager.StatusCode int statusCode) { Bundle bundle = new Bundle(); bundle.putInt( AmbientContextManager.STATUS_RESPONSE_BUNDLE_KEY, @@ -236,6 +233,15 @@ final class AmbientContextManagerPerUserService extends statusCallback.sendResult(bundle); } + static void completeRegistration(IAmbientContextObserver observer, int statusCode) { + try { + observer.onRegistrationComplete(statusCode); + } catch (RemoteException e) { + Slog.w(TAG, "Failed to call IAmbientContextObserver.onRegistrationComplete: " + + e.getMessage()); + } + } + /** * Unregisters the client from all previously registered events by removing from the * mExistingRequests map, and unregister events from the service if those events are not @@ -255,7 +261,7 @@ final class AmbientContextManagerPerUserService extends synchronized (mLock) { if (!setUpServiceIfNeeded()) { Slog.w(TAG, "Detection service is not available at this moment."); - sendStatusToCallback(statusCallback, + sendStatusCallback(statusCallback, AmbientContextManager.STATUS_NOT_SUPPORTED); return; } @@ -263,7 +269,8 @@ final class AmbientContextManagerPerUserService extends mRemoteService.queryServiceStatus( eventTypes, callingPackage, - getServerStatusCallback(statusCallback)); + getServerStatusCallback( + statusCode -> sendStatusCallback(statusCallback, statusCode))); } } @@ -350,18 +357,6 @@ final class AmbientContextManagerPerUserService extends return ComponentName.unflattenFromString(consentComponent); } - /** - * Sends the result response with the specified status to the callback. - */ - void sendStatusToCallback(RemoteCallback callback, - @AmbientContextManager.StatusCode int status) { - Bundle bundle = new Bundle(); - bundle.putInt( - AmbientContextManager.STATUS_RESPONSE_BUNDLE_KEY, - status); - callback.sendResult(bundle); - } - @VisibleForTesting void stopDetection(String packageName) { Slog.d(TAG, "Stop detection for " + packageName); @@ -377,13 +372,13 @@ final class AmbientContextManagerPerUserService extends * Sends out the Intent to the client after the event is detected. * * @param pendingIntent Client's PendingIntent for callback - * @param result result from the detection service + * @param events detected events from the detection service */ - private void sendDetectionResultIntent(PendingIntent pendingIntent, - AmbientContextDetectionResult result) { + void sendDetectionResultIntent(PendingIntent pendingIntent, + List events) { Intent intent = new Intent(); intent.putExtra(AmbientContextManager.EXTRA_AMBIENT_CONTEXT_EVENTS, - new ArrayList(result.getEvents())); + new ArrayList(events)); // Explicitly disallow the receiver from starting activities, to prevent apps from utilizing // the PendingIntent as a backdoor to do this. BroadcastOptions options = BroadcastOptions.makeBasic(); @@ -392,7 +387,7 @@ final class AmbientContextManagerPerUserService extends pendingIntent.send(getContext(), 0, intent, null, null, null, options.toBundle()); Slog.i(TAG, "Sending PendingIntent to " + pendingIntent.getCreatorPackage() + ": " - + result); + + events); } catch (PendingIntent.CanceledException e) { Slog.w(TAG, "Couldn't deliver pendingIntent:" + pendingIntent); } @@ -405,16 +400,19 @@ final class AmbientContextManagerPerUserService extends (AmbientContextDetectionResult) result.get( AmbientContextDetectionResult.RESULT_RESPONSE_BUNDLE_KEY); String packageName = detectionResult.getPackageName(); - PendingIntent pendingIntent = mMaster.getPendingIntent(mUserId, packageName); - if (pendingIntent == null) { + IAmbientContextObserver observer = mMaster.getClientRequestObserver( + mUserId, packageName); + if (observer == null) { return; } final long token = Binder.clearCallingIdentity(); try { - sendDetectionResultIntent(pendingIntent, detectionResult); + observer.onEvents(detectionResult.getEvents()); Slog.i(TAG, "Got detection result of " + detectionResult.getEvents() + " for " + packageName); + } catch (RemoteException e) { + Slog.w(TAG, "Failed to call IAmbientContextObserver.onEvents: " + e.getMessage()); } 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 4206262398c7d..e205e84cea268 100644 --- a/services/core/java/com/android/server/ambientcontext/AmbientContextManagerService.java +++ b/services/core/java/com/android/server/ambientcontext/AmbientContextManagerService.java @@ -27,10 +27,12 @@ import android.app.ambientcontext.AmbientContextEvent; import android.app.ambientcontext.AmbientContextEventRequest; import android.app.ambientcontext.AmbientContextManager; import android.app.ambientcontext.IAmbientContextManager; +import android.app.ambientcontext.IAmbientContextObserver; import android.content.ComponentName; import android.content.Context; import android.content.pm.PackageManagerInternal; import android.os.RemoteCallback; +import android.os.RemoteException; import android.os.ResultReceiver; import android.os.ShellCallback; import android.os.UserHandle; @@ -48,6 +50,7 @@ import com.android.server.pm.KnownPackages; import java.io.FileDescriptor; import java.io.PrintWriter; +import java.util.List; import java.util.Objects; import java.util.Set; @@ -67,31 +70,27 @@ public class AmbientContextManagerService extends static class ClientRequest { private final int mUserId; private final AmbientContextEventRequest mRequest; - private final PendingIntent mPendingIntent; - private final RemoteCallback mClientStatusCallback; + private final String mPackageName; + private final IAmbientContextObserver mObserver; ClientRequest(int userId, AmbientContextEventRequest request, - PendingIntent pendingIntent, RemoteCallback clientStatusCallback) { + String packageName, IAmbientContextObserver observer) { this.mUserId = userId; this.mRequest = request; - this.mPendingIntent = pendingIntent; - this.mClientStatusCallback = clientStatusCallback; + this.mPackageName = packageName; + this.mObserver = observer; } String getPackageName() { - return mPendingIntent.getCreatorPackage(); + return mPackageName; } AmbientContextEventRequest getRequest() { return mRequest; } - PendingIntent getPendingIntent() { - return mPendingIntent; - } - - RemoteCallback getClientStatusCallback() { - return mClientStatusCallback; + IAmbientContextObserver getObserver() { + return mObserver; } boolean hasUserId(int userId) { @@ -139,16 +138,16 @@ public class AmbientContextManagerService extends } void newClientAdded(int userId, AmbientContextEventRequest request, - PendingIntent pendingIntent, RemoteCallback clientStatusCallback) { - Slog.d(TAG, "New client added: " + pendingIntent.getCreatorPackage()); + String callingPackage, IAmbientContextObserver observer) { + Slog.d(TAG, "New client added: " + callingPackage); // Remove any existing ClientRequest for this user and package. mExistingClientRequests.removeAll( - findExistingRequests(userId, pendingIntent.getCreatorPackage())); + findExistingRequests(userId, callingPackage)); // Add to existing ClientRequests mExistingClientRequests.add( - new ClientRequest(userId, request, pendingIntent, clientStatusCallback)); + new ClientRequest(userId, request, callingPackage, observer)); } void clientRemoved(int userId, String packageName) { @@ -167,10 +166,10 @@ public class AmbientContextManagerService extends } @Nullable - PendingIntent getPendingIntent(int userId, String packageName) { + IAmbientContextObserver getClientRequestObserver(int userId, String packageName) { for (ClientRequest clientRequest : mExistingClientRequests) { if (clientRequest.hasUserIdAndPackageName(userId, packageName)) { - return clientRequest.getPendingIntent(); + return clientRequest.getObserver(); } } return null; @@ -236,15 +235,13 @@ public class AmbientContextManagerService extends * Requires ACCESS_AMBIENT_CONTEXT_EVENT permission. */ void startDetection(@UserIdInt int userId, AmbientContextEventRequest request, - String packageName, RemoteCallback detectionResultCallback, - RemoteCallback statusCallback) { + String packageName, IAmbientContextObserver observer) { mContext.enforceCallingOrSelfPermission( Manifest.permission.ACCESS_AMBIENT_CONTEXT_EVENT, TAG); synchronized (mLock) { final AmbientContextManagerPerUserService service = getServiceForUserLocked(userId); if (service != null) { - service.startDetection(request, packageName, detectionResultCallback, - statusCallback); + service.startDetection(request, packageName, observer); } else { Slog.i(TAG, "service not available for user_id: " + userId); } @@ -297,8 +294,7 @@ public class AmbientContextManagerService extends Slog.d(TAG, "Restoring detection for " + clientRequest.getPackageName()); service.startDetection(clientRequest.getRequest(), clientRequest.getPackageName(), - service.createDetectionResultRemoteCallback(), - clientRequest.getClientStatusCallback()); + clientRequest.getObserver()); } } } @@ -328,16 +324,45 @@ public class AmbientContextManagerService extends Objects.requireNonNull(request); Objects.requireNonNull(resultPendingIntent); Objects.requireNonNull(statusCallback); + // Wrap the PendingIntent and statusCallback in a IAmbientContextObserver to make the + // code unified + IAmbientContextObserver observer = new IAmbientContextObserver.Stub() { + @Override + public void onEvents(List events) throws RemoteException { + mService.sendDetectionResultIntent(resultPendingIntent, events); + } + + @Override + public void onRegistrationComplete(int statusCode) throws RemoteException { + AmbientContextManagerPerUserService.sendStatusCallback(statusCallback, + statusCode); + } + }; + registerObserverWithCallback(request, resultPendingIntent.getCreatorPackage(), + observer); + } + + /** + * Register an observer for Ambient Context events. + */ + @Override + public void registerObserverWithCallback(AmbientContextEventRequest request, + String packageName, + IAmbientContextObserver observer) { + Slog.i(TAG, "AmbientContextManagerService registerObserverWithCallback."); + Objects.requireNonNull(request); + Objects.requireNonNull(packageName); + Objects.requireNonNull(observer); mContext.enforceCallingOrSelfPermission( Manifest.permission.ACCESS_AMBIENT_CONTEXT_EVENT, TAG); - assertCalledByPackageOwner(resultPendingIntent.getCreatorPackage()); + assertCalledByPackageOwner(packageName); if (!mIsServiceEnabled) { Slog.w(TAG, "Service not available."); - mService.sendStatusCallback(statusCallback, + AmbientContextManagerPerUserService.completeRegistration(observer, AmbientContextManager.STATUS_SERVICE_UNAVAILABLE); return; } - mService.onRegisterObserver(request, resultPendingIntent, statusCallback); + mService.onRegisterObserver(request, packageName, observer); } @Override @@ -359,7 +384,7 @@ public class AmbientContextManagerService extends assertCalledByPackageOwner(callingPackage); if (!mIsServiceEnabled) { Slog.w(TAG, "Detection service not available."); - mService.sendStatusToCallback(statusCallback, + AmbientContextManagerPerUserService.sendStatusCallback(statusCallback, AmbientContextManager.STATUS_SERVICE_UNAVAILABLE); return; } diff --git a/services/core/java/com/android/server/ambientcontext/AmbientContextShellCommand.java b/services/core/java/com/android/server/ambientcontext/AmbientContextShellCommand.java index ec6c2f00b3b16..a3ffcde802f34 100644 --- a/services/core/java/com/android/server/ambientcontext/AmbientContextShellCommand.java +++ b/services/core/java/com/android/server/ambientcontext/AmbientContextShellCommand.java @@ -22,13 +22,15 @@ import android.annotation.NonNull; import android.app.ambientcontext.AmbientContextEvent; import android.app.ambientcontext.AmbientContextEventRequest; import android.app.ambientcontext.AmbientContextManager; +import android.app.ambientcontext.IAmbientContextObserver; import android.content.ComponentName; import android.os.Binder; import android.os.RemoteCallback; +import android.os.RemoteException; import android.os.ShellCommand; -import android.service.ambientcontext.AmbientContextDetectionResult; import java.io.PrintWriter; +import java.util.List; /** * Shell command for {@link AmbientContextManagerService}. @@ -39,6 +41,7 @@ final class AmbientContextShellCommand extends ShellCommand { new AmbientContextEventRequest.Builder() .addEventType(AmbientContextEvent.EVENT_COUGH) .addEventType(AmbientContextEvent.EVENT_SNORE) + .addEventType(AmbientContextEvent.EVENT_BACK_DOUBLE_TAP) .build(); @NonNull @@ -50,11 +53,11 @@ final class AmbientContextShellCommand extends ShellCommand { /** Callbacks for AmbientContextEventService results used internally for testing. */ static class TestableCallbackInternal { - private AmbientContextDetectionResult mLastResult; + private List mLastEvents; private int mLastStatus; - public AmbientContextDetectionResult getLastResult() { - return mLastResult; + public List getLastEvents() { + return mLastEvents; } public int getLastStatus() { @@ -62,19 +65,19 @@ final class AmbientContextShellCommand extends ShellCommand { } @NonNull - private RemoteCallback createRemoteDetectionResultCallback() { - return new RemoteCallback(result -> { - AmbientContextDetectionResult detectionResult = - (AmbientContextDetectionResult) result.get( - AmbientContextDetectionResult.RESULT_RESPONSE_BUNDLE_KEY); - final long token = Binder.clearCallingIdentity(); - try { - mLastResult = detectionResult; - out.println("Detection result available: " + detectionResult); - } finally { - Binder.restoreCallingIdentity(token); + private IAmbientContextObserver createAmbientContextObserver() { + return new IAmbientContextObserver.Stub() { + @Override + public void onEvents(List events) throws RemoteException { + mLastEvents = events; + out.println("Detection events available: " + events); } - }); + + @Override + public void onRegistrationComplete(int statusCode) throws RemoteException { + mLastStatus = statusCode; + } + }; } @NonNull @@ -123,8 +126,7 @@ final class AmbientContextShellCommand extends ShellCommand { final String packageName = getNextArgRequired(); mService.startDetection( userId, REQUEST, packageName, - sTestableCallbackInternal.createRemoteDetectionResultCallback(), - sTestableCallbackInternal.createRemoteStatusCallback()); + sTestableCallbackInternal.createAmbientContextObserver()); return 0; } diff --git a/services/tests/servicestests/src/com/android/server/ambientcontext/AmbientContextManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/ambientcontext/AmbientContextManagerServiceTest.java index 6bb494d3a92a9..c75abf84fb89a 100644 --- a/services/tests/servicestests/src/com/android/server/ambientcontext/AmbientContextManagerServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/ambientcontext/AmbientContextManagerServiceTest.java @@ -21,8 +21,8 @@ import static com.google.common.truth.Truth.assertThat; import android.app.PendingIntent; import android.app.ambientcontext.AmbientContextEvent; import android.app.ambientcontext.AmbientContextEventRequest; +import android.app.ambientcontext.IAmbientContextObserver; import android.content.Intent; -import android.os.RemoteCallback; import android.os.UserHandle; import androidx.test.InstrumentationRegistry; @@ -30,6 +30,8 @@ import androidx.test.filters.SmallTest; import org.junit.Test; +import java.util.List; + /** * Unit test for {@link AmbientContextManagerService}. * atest FrameworksServicesTests:AmbientContextManagerServiceTest @@ -48,12 +50,22 @@ public class AmbientContextManagerServiceTest { PendingIntent pendingIntent = PendingIntent.getBroadcast( InstrumentationRegistry.getTargetContext(), 0, intent, PendingIntent.FLAG_IMMUTABLE); + IAmbientContextObserver observer = new IAmbientContextObserver.Stub() { + @Override + public void onEvents(List events) { + } + + @Override + public void onRegistrationComplete(int statusCode) { + } + }; AmbientContextManagerService.ClientRequest clientRequest = new AmbientContextManagerService.ClientRequest(USER_ID, request, - pendingIntent, new RemoteCallback(result -> {})); + pendingIntent.getCreatorPackage(), observer); assertThat(clientRequest.getRequest()).isEqualTo(request); assertThat(clientRequest.getPackageName()).isEqualTo(SYSTEM_PACKAGE_NAME); + assertThat(clientRequest.getObserver()).isEqualTo(observer); assertThat(clientRequest.hasUserId(USER_ID)).isTrue(); assertThat(clientRequest.hasUserId(-1)).isFalse(); assertThat(clientRequest.hasUserIdAndPackageName(USER_ID, SYSTEM_PACKAGE_NAME)).isTrue();