Merge "AmbientContextManager API changes to support quick-tap detection" into tm-qpr-dev
diff --git a/core/java/android/app/ambientcontext/AmbientContextCallback.java b/core/java/android/app/ambientcontext/AmbientContextCallback.java
new file mode 100644
index 0000000..9133d7f
--- /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<AmbientContextEvent> 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 11e695ad..af48fde 100644
--- a/core/java/android/app/ambientcontext/AmbientContextEvent.java
+++ b/core/java/android/app/ambientcontext/AmbientContextEvent.java
@@ -59,11 +59,21 @@
*/
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 @@
@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 @@
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 @@
}
@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 dd1dd0c..e45dfdf 100644
--- a/core/java/android/app/ambientcontext/AmbientContextManager.java
+++ b/core/java/android/app/ambientcontext/AmbientContextManager.java
@@ -282,7 +282,7 @@
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));
@@ -297,6 +297,72 @@
}
/**
+ * 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<AmbientContextEvent> 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 0d9ecfd..8f06e76 100644
--- a/core/java/android/app/ambientcontext/IAmbientContextManager.aidl
+++ b/core/java/android/app/ambientcontext/IAmbientContextManager.aidl
@@ -18,6 +18,7 @@
import android.app.PendingIntent;
import android.app.ambientcontext.AmbientContextEventRequest;
+import android.app.ambientcontext.IAmbientContextObserver;
import android.os.RemoteCallback;
/**
@@ -29,6 +30,11 @@
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 0000000..529e2fe
--- /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<AmbientContextEvent> 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 028288f..dcadd5f 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.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 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 @@
* 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 @@
* 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<Integer> 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 @@
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 @@
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 @@
mRemoteService.queryServiceStatus(
eventTypes,
callingPackage,
- getServerStatusCallback(statusCallback));
+ getServerStatusCallback(
+ statusCode -> sendStatusCallback(statusCallback, statusCode)));
}
}
@@ -350,18 +357,6 @@
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 @@
* 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<AmbientContextEvent> 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 @@
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 @@
(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 4206262..e205e84 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.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 java.io.FileDescriptor;
import java.io.PrintWriter;
+import java.util.List;
import java.util.Objects;
import java.util.Set;
@@ -67,31 +70,27 @@
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 @@
}
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 @@
}
@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 @@
* 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 @@
Slog.d(TAG, "Restoring detection for " + clientRequest.getPackageName());
service.startDetection(clientRequest.getRequest(),
clientRequest.getPackageName(),
- service.createDetectionResultRemoteCallback(),
- clientRequest.getClientStatusCallback());
+ clientRequest.getObserver());
}
}
}
@@ -328,16 +324,45 @@
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<AmbientContextEvent> 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 @@
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 ec6c2f0..a3ffcde8 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.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 @@
new AmbientContextEventRequest.Builder()
.addEventType(AmbientContextEvent.EVENT_COUGH)
.addEventType(AmbientContextEvent.EVENT_SNORE)
+ .addEventType(AmbientContextEvent.EVENT_BACK_DOUBLE_TAP)
.build();
@NonNull
@@ -50,11 +53,11 @@
/** Callbacks for AmbientContextEventService results used internally for testing. */
static class TestableCallbackInternal {
- private AmbientContextDetectionResult mLastResult;
+ private List<AmbientContextEvent> mLastEvents;
private int mLastStatus;
- public AmbientContextDetectionResult getLastResult() {
- return mLastResult;
+ public List<AmbientContextEvent> getLastEvents() {
+ return mLastEvents;
}
public int getLastStatus() {
@@ -62,19 +65,19 @@
}
@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<AmbientContextEvent> 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 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 6bb494d..c75abf8 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 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 org.junit.Test;
+import java.util.List;
+
/**
* Unit test for {@link AmbientContextManagerService}.
* atest FrameworksServicesTests:AmbientContextManagerServiceTest
@@ -48,12 +50,22 @@
PendingIntent pendingIntent = PendingIntent.getBroadcast(
InstrumentationRegistry.getTargetContext(), 0,
intent, PendingIntent.FLAG_IMMUTABLE);
+ IAmbientContextObserver observer = new IAmbientContextObserver.Stub() {
+ @Override
+ public void onEvents(List<AmbientContextEvent> 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();