Merge "AmbientContextManager API changes to support quick-tap detection" into tm-qpr-dev

This commit is contained in:
Lili Zou
2022-08-31 13:41:34 +00:00
committed by Android (Google) Code Review
9 changed files with 295 additions and 96 deletions

View File

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

View File

@@ -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() {}

View File

@@ -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<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.

View File

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

View File

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

View File

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

View File

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

View File

@@ -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<AmbientContextEvent> mLastEvents;
private int mLastStatus;
public AmbientContextDetectionResult getLastResult() {
return mLastResult;
public List<AmbientContextEvent> 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<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 class AmbientContextShellCommand extends ShellCommand {
final String packageName = getNextArgRequired();
mService.startDetection(
userId, REQUEST, packageName,
sTestableCallbackInternal.createRemoteDetectionResultCallback(),
sTestableCallbackInternal.createRemoteStatusCallback());
sTestableCallbackInternal.createAmbientContextObserver());
return 0;
}

View File

@@ -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<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();