summaryrefslogtreecommitdiff
path: root/core/java
diff options
context:
space:
mode:
Diffstat (limited to 'core/java')
-rw-r--r--core/java/android/app/SystemServiceRegistry.java6
-rw-r--r--core/java/android/app/ambientcontext/AmbientContextEventRequest.java25
-rw-r--r--core/java/android/app/ambientcontext/AmbientContextEventResponse.java293
-rw-r--r--core/java/android/app/ambientcontext/AmbientContextManager.java282
-rw-r--r--core/java/android/app/ambientcontext/IAmbientContextManager.aidl (renamed from core/java/android/app/ambientcontext/IAmbientContextEventObserver.aidl)12
-rw-r--r--core/java/android/provider/Settings.java28
-rw-r--r--core/java/android/service/ambientcontext/AmbientContextDetectionResult.aidl (renamed from core/java/android/app/ambientcontext/AmbientContextEventResponse.aidl)6
-rw-r--r--core/java/android/service/ambientcontext/AmbientContextDetectionResult.java180
-rw-r--r--core/java/android/service/ambientcontext/AmbientContextDetectionService.java94
-rw-r--r--core/java/android/service/ambientcontext/AmbientContextDetectionServiceStatus.aidl19
-rw-r--r--core/java/android/service/ambientcontext/AmbientContextDetectionServiceStatus.java171
-rw-r--r--core/java/android/service/ambientcontext/IAmbientContextDetectionService.aidl4
12 files changed, 723 insertions, 397 deletions
diff --git a/core/java/android/app/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java
index f5f2fe0d0292..eeb4705fc7d3 100644
--- a/core/java/android/app/SystemServiceRegistry.java
+++ b/core/java/android/app/SystemServiceRegistry.java
@@ -25,7 +25,7 @@ import android.app.ContextImpl.ServiceInitializationState;
import android.app.admin.DevicePolicyManager;
import android.app.admin.IDevicePolicyManager;
import android.app.ambientcontext.AmbientContextManager;
-import android.app.ambientcontext.IAmbientContextEventObserver;
+import android.app.ambientcontext.IAmbientContextManager;
import android.app.appsearch.AppSearchManagerFrameworkInitializer;
import android.app.blob.BlobStoreManagerFrameworkInitializer;
import android.app.cloudsearch.CloudSearchManager;
@@ -1542,8 +1542,8 @@ public final class SystemServiceRegistry {
throws ServiceNotFoundException {
IBinder iBinder = ServiceManager.getServiceOrThrow(
Context.AMBIENT_CONTEXT_SERVICE);
- IAmbientContextEventObserver manager =
- IAmbientContextEventObserver.Stub.asInterface(iBinder);
+ IAmbientContextManager manager =
+ IAmbientContextManager.Stub.asInterface(iBinder);
return new AmbientContextManager(ctx.getOuterContext(), manager);
}});
diff --git a/core/java/android/app/ambientcontext/AmbientContextEventRequest.java b/core/java/android/app/ambientcontext/AmbientContextEventRequest.java
index 82b16a2db0ce..0557acb1130d 100644
--- a/core/java/android/app/ambientcontext/AmbientContextEventRequest.java
+++ b/core/java/android/app/ambientcontext/AmbientContextEventRequest.java
@@ -23,6 +23,9 @@ import android.os.Parcelable;
import android.os.PersistableBundle;
import android.util.ArraySet;
+import com.android.internal.util.AnnotationValidations;
+import com.android.internal.util.Preconditions;
+
import java.util.HashSet;
import java.util.Set;
@@ -36,15 +39,17 @@ public final class AmbientContextEventRequest implements Parcelable {
@NonNull private final Set<Integer> mEventTypes;
@NonNull private final PersistableBundle mOptions;
- AmbientContextEventRequest(
+ private AmbientContextEventRequest(
@NonNull Set<Integer> eventTypes,
@NonNull PersistableBundle options) {
this.mEventTypes = eventTypes;
- com.android.internal.util.AnnotationValidations.validate(
- NonNull.class, null, mEventTypes);
+ AnnotationValidations.validate(NonNull.class, null, mEventTypes);
+ Preconditions.checkArgument(!eventTypes.isEmpty(), "eventTypes cannot be empty");
+ for (int eventType : eventTypes) {
+ AnnotationValidations.validate(AmbientContextEvent.EventCode.class, null, eventType);
+ }
this.mOptions = options;
- com.android.internal.util.AnnotationValidations.validate(
- NonNull.class, null, mOptions);
+ AnnotationValidations.validate(NonNull.class, null, mOptions);
}
/**
@@ -80,16 +85,20 @@ public final class AmbientContextEventRequest implements Parcelable {
/** @hide */
@SuppressWarnings({"unchecked", "RedundantCast"})
- AmbientContextEventRequest(@NonNull Parcel in) {
+ private AmbientContextEventRequest(@NonNull Parcel in) {
Set<Integer> eventTypes = (Set<Integer>) in.readArraySet(Integer.class.getClassLoader());
PersistableBundle options = (PersistableBundle) in.readTypedObject(
PersistableBundle.CREATOR);
this.mEventTypes = eventTypes;
- com.android.internal.util.AnnotationValidations.validate(
+ AnnotationValidations.validate(
NonNull.class, null, mEventTypes);
+ Preconditions.checkArgument(!eventTypes.isEmpty(), "eventTypes cannot be empty");
+ for (int eventType : eventTypes) {
+ AnnotationValidations.validate(AmbientContextEvent.EventCode.class, null, eventType);
+ }
this.mOptions = options;
- com.android.internal.util.AnnotationValidations.validate(
+ AnnotationValidations.validate(
NonNull.class, null, mOptions);
}
diff --git a/core/java/android/app/ambientcontext/AmbientContextEventResponse.java b/core/java/android/app/ambientcontext/AmbientContextEventResponse.java
deleted file mode 100644
index 472a78b177c9..000000000000
--- a/core/java/android/app/ambientcontext/AmbientContextEventResponse.java
+++ /dev/null
@@ -1,293 +0,0 @@
-/*
- * 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.IntDef;
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.annotation.SystemApi;
-import android.app.PendingIntent;
-import android.os.Parcelable;
-
-import com.android.internal.util.AnnotationValidations;
-
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * Represents a response from the {@code AmbientContextEvent} service.
- *
- * @hide
- */
-@SystemApi
-public final class AmbientContextEventResponse implements Parcelable {
- /**
- * An unknown status.
- */
- public static final int STATUS_UNKNOWN = 0;
- /**
- * The value of the status code that indicates success.
- */
- public static final int STATUS_SUCCESS = 1;
- /**
- * The value of the status code that indicates one or more of the
- * requested events are not supported.
- */
- public static final int STATUS_NOT_SUPPORTED = 2;
- /**
- * The value of the status code that indicates service not available.
- */
- public static final int STATUS_SERVICE_UNAVAILABLE = 3;
- /**
- * The value of the status code that microphone is disabled.
- */
- public static final int STATUS_MICROPHONE_DISABLED = 4;
- /**
- * The value of the status code that the app is not granted access.
- */
- public static final int STATUS_ACCESS_DENIED = 5;
-
- /** @hide */
- @IntDef(prefix = { "STATUS_" }, value = {
- STATUS_UNKNOWN,
- STATUS_SUCCESS,
- STATUS_NOT_SUPPORTED,
- STATUS_SERVICE_UNAVAILABLE,
- STATUS_MICROPHONE_DISABLED,
- STATUS_ACCESS_DENIED
- }) public @interface StatusCode {}
-
- @StatusCode private final int mStatusCode;
- @NonNull private final List<AmbientContextEvent> mEvents;
- @NonNull private final String mPackageName;
- @Nullable private final PendingIntent mActionPendingIntent;
-
- /** @hide */
- public static String statusToString(@StatusCode int value) {
- switch (value) {
- case STATUS_UNKNOWN:
- return "STATUS_UNKNOWN";
- case STATUS_SUCCESS:
- return "STATUS_SUCCESS";
- case STATUS_NOT_SUPPORTED:
- return "STATUS_NOT_SUPPORTED";
- case STATUS_SERVICE_UNAVAILABLE:
- return "STATUS_SERVICE_UNAVAILABLE";
- case STATUS_MICROPHONE_DISABLED:
- return "STATUS_MICROPHONE_DISABLED";
- case STATUS_ACCESS_DENIED:
- return "STATUS_ACCESS_DENIED";
- default: return Integer.toHexString(value);
- }
- }
-
- AmbientContextEventResponse(
- @StatusCode int statusCode,
- @NonNull List<AmbientContextEvent> events,
- @NonNull String packageName,
- @Nullable PendingIntent actionPendingIntent) {
- this.mStatusCode = statusCode;
- AnnotationValidations.validate(StatusCode.class, null, mStatusCode);
- this.mEvents = events;
- AnnotationValidations.validate(NonNull.class, null, mEvents);
- this.mPackageName = packageName;
- AnnotationValidations.validate(NonNull.class, null, mPackageName);
- this.mActionPendingIntent = actionPendingIntent;
- }
-
- /**
- * The status of the response.
- */
- public @StatusCode int getStatusCode() {
- return mStatusCode;
- }
-
- /**
- * The detected event.
- */
- public @NonNull List<AmbientContextEvent> getEvents() {
- return mEvents;
- }
-
- /**
- * The package to deliver the response to.
- */
- public @NonNull String getPackageName() {
- return mPackageName;
- }
-
- /**
- * A {@link PendingIntent} that the client should call to allow further actions by user.
- * For example, with {@link STATUS_ACCESS_DENIED}, the PendingIntent can redirect users to the
- * grant access activity.
- */
- public @Nullable PendingIntent getActionPendingIntent() {
- return mActionPendingIntent;
- }
-
- @Override
- public String toString() {
- return "AmbientContextEventResponse { " + "statusCode = " + mStatusCode + ", "
- + "events = " + mEvents + ", " + "packageName = " + mPackageName + ", "
- + "callbackPendingIntent = " + mActionPendingIntent + " }";
- }
-
- @Override
- public void writeToParcel(@NonNull android.os.Parcel dest, int flags) {
- byte flg = 0;
- if (mActionPendingIntent != null) flg |= 0x8;
- dest.writeByte(flg);
- dest.writeInt(mStatusCode);
- dest.writeParcelableList(mEvents, flags);
- dest.writeString(mPackageName);
- if (mActionPendingIntent != null) dest.writeTypedObject(mActionPendingIntent, flags);
- }
-
- @Override
- public int describeContents() {
- return 0;
- }
-
- /** @hide */
- @SuppressWarnings({"unchecked", "RedundantCast"})
- AmbientContextEventResponse(@NonNull android.os.Parcel in) {
- byte flg = in.readByte();
- int statusCode = in.readInt();
- List<AmbientContextEvent> events = new ArrayList<>();
- in.readParcelableList(events, AmbientContextEvent.class.getClassLoader(),
- AmbientContextEvent.class);
- String packageName = in.readString();
- PendingIntent callbackPendingIntent = (flg & 0x8) == 0 ? null
- : (PendingIntent) in.readTypedObject(PendingIntent.CREATOR);
-
- this.mStatusCode = statusCode;
- AnnotationValidations.validate(
- StatusCode.class, null, mStatusCode);
- this.mEvents = events;
- AnnotationValidations.validate(
- NonNull.class, null, mEvents);
- this.mPackageName = packageName;
- AnnotationValidations.validate(
- NonNull.class, null, mPackageName);
- this.mActionPendingIntent = callbackPendingIntent;
- }
-
- public static final @NonNull Parcelable.Creator<AmbientContextEventResponse> CREATOR =
- new Parcelable.Creator<AmbientContextEventResponse>() {
- @Override
- public AmbientContextEventResponse[] newArray(int size) {
- return new AmbientContextEventResponse[size];
- }
-
- @Override
- public AmbientContextEventResponse createFromParcel(@NonNull android.os.Parcel in) {
- return new AmbientContextEventResponse(in);
- }
- };
-
- /**
- * A builder for {@link AmbientContextEventResponse}
- */
- @SuppressWarnings("WeakerAccess")
- public static final class Builder {
- private @StatusCode int mStatusCode;
- private @NonNull List<AmbientContextEvent> mEvents;
- private @NonNull String mPackageName;
- private @Nullable PendingIntent mCallbackPendingIntent;
- private long mBuilderFieldsSet = 0L;
-
- public Builder() {
- }
-
- /**
- * The status of the response.
- */
- public @NonNull Builder setStatusCode(@StatusCode int value) {
- checkNotUsed();
- mBuilderFieldsSet |= 0x1;
- mStatusCode = value;
- return this;
- }
-
- /**
- * Adds an event to the builder.
- */
- public @NonNull Builder addEvent(@NonNull AmbientContextEvent value) {
- checkNotUsed();
- if (mEvents == null) {
- mBuilderFieldsSet |= 0x2;
- mEvents = new ArrayList<>();
- }
- mEvents.add(value);
- return this;
- }
-
- /**
- * The package to deliver the response to.
- */
- public @NonNull Builder setPackageName(@NonNull String value) {
- checkNotUsed();
- mBuilderFieldsSet |= 0x4;
- mPackageName = value;
- return this;
- }
-
- /**
- * A {@link PendingIntent} that the client should call to allow further actions by user.
- * For example, with {@link STATUS_ACCESS_DENIED}, the PendingIntent can redirect users to
- * the grant access activity.
- */
- public @NonNull Builder setActionPendingIntent(@NonNull PendingIntent value) {
- checkNotUsed();
- mBuilderFieldsSet |= 0x8;
- mCallbackPendingIntent = value;
- return this;
- }
-
- /** Builds the instance. This builder should not be touched after calling this! */
- public @NonNull AmbientContextEventResponse build() {
- checkNotUsed();
- mBuilderFieldsSet |= 0x10; // Mark builder used
-
- if ((mBuilderFieldsSet & 0x1) == 0) {
- mStatusCode = STATUS_UNKNOWN;
- }
- if ((mBuilderFieldsSet & 0x2) == 0) {
- mEvents = new ArrayList<>();
- }
- if ((mBuilderFieldsSet & 0x4) == 0) {
- mPackageName = "";
- }
- if ((mBuilderFieldsSet & 0x8) == 0) {
- mCallbackPendingIntent = null;
- }
- AmbientContextEventResponse o = new AmbientContextEventResponse(
- mStatusCode,
- mEvents,
- mPackageName,
- mCallbackPendingIntent);
- return o;
- }
-
- private void checkNotUsed() {
- if ((mBuilderFieldsSet & 0x10) != 0) {
- throw new IllegalStateException(
- "This Builder should not be reused. Use a new Builder instance instead");
- }
- }
- }
-}
diff --git a/core/java/android/app/ambientcontext/AmbientContextManager.java b/core/java/android/app/ambientcontext/AmbientContextManager.java
index 6841d1bbfc1f..7f913e798bc9 100644
--- a/core/java/android/app/ambientcontext/AmbientContextManager.java
+++ b/core/java/android/app/ambientcontext/AmbientContextManager.java
@@ -17,116 +17,280 @@
package android.app.ambientcontext;
import android.Manifest;
+import android.annotation.CallbackExecutor;
+import android.annotation.IntDef;
import android.annotation.NonNull;
-import android.annotation.Nullable;
import android.annotation.RequiresPermission;
import android.annotation.SystemApi;
import android.annotation.SystemService;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
+import android.os.Binder;
+import android.os.RemoteCallback;
import android.os.RemoteException;
import com.android.internal.util.Preconditions;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Set;
+import java.util.concurrent.Executor;
+import java.util.function.Consumer;
+
/**
- * Allows granted apps to register for particular pre-defined {@link AmbientContextEvent}s.
- * After successful registration, the app receives a callback on the provided {@link PendingIntent}
- * when the requested event is detected.
- * <p />
- *
- * Example:
- *
- * <pre><code>
- * // Create request
- * AmbientContextEventRequest request = new AmbientContextEventRequest.Builder()
- * .addEventType(AmbientContextEvent.EVENT_COUGH)
- * .addEventTYpe(AmbientContextEvent.EVENT_SNORE)
- * .build();
- * // Create PendingIntent
- * Intent intent = new Intent(actionString, null, context, MyBroadcastReceiver.class)
- * .addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
- * PendingIntent pendingIntent = PendingIntents.getBroadcastMutable(context, 0, intent, 0);
- * // Register for events
- * AmbientContextManager ambientContextManager =
- * context.getSystemService(AmbientContextManager.class);
- * ambientContextManager.registerObserver(request, pendingIntent);
- *
- * // Handle the callback intent in your receiver
- * {@literal @}Override
- * protected void onReceive(Context context, Intent intent) {
- * AmbientContextEventResponse response =
- * AmbientContextManager.getResponseFromIntent(intent);
- * if (response != null) {
- * if (response.getStatusCode() == AmbientContextEventResponse.STATUS_SUCCESS) {
- * // Do something useful with response.getEvent()
- * } else if (response.getStatusCode() == AmbientContextEventResponse.STATUS_ACCESS_DENIED) {
- * // Redirect users to grant access
- * PendingIntent callbackPendingIntent = response.getCallbackPendingIntent();
- * if (callbackPendingIntent != null) {
- * callbackPendingIntent.send();
- * }
- * } else ...
- * }
- * }
- * </code></pre>
+ * Allows granted apps to register for event types defined in {@link AmbientContextEvent}.
+ * After registration, the app receives a Consumer callback of the service status.
+ * If it is {@link STATUS_SUCCESSFUL}, when the requested events are detected, the provided
+ * {@link PendingIntent} callback will receive the list of detected {@link AmbientContextEvent}s.
+ * If it is {@link STATUS_ACCESS_DENIED}, the app can call {@link #startConsentActivity}
+ * to load the consent screen.
*
* @hide
*/
@SystemApi
@SystemService(Context.AMBIENT_CONTEXT_SERVICE)
public final class AmbientContextManager {
+ /**
+ * The bundle key for the service status query result, used in
+ * {@code RemoteCallback#sendResult}.
+ *
+ * @hide
+ */
+ public static final String STATUS_RESPONSE_BUNDLE_KEY =
+ "android.app.ambientcontext.AmbientContextStatusBundleKey";
+
+ /**
+ * The key of an intent extra indicating a list of detected {@link AmbientContextEvent}s.
+ * The intent is sent to the app in the app's registered {@link PendingIntent}.
+ */
+ public static final String EXTRA_AMBIENT_CONTEXT_EVENTS =
+ "android.app.ambientcontext.extra.AMBIENT_CONTEXT_EVENTS";
+
+ /**
+ * An unknown status.
+ */
+ public static final int STATUS_UNKNOWN = 0;
+
+ /**
+ * The value of the status code that indicates success.
+ */
+ public static final int STATUS_SUCCESS = 1;
+
+ /**
+ * The value of the status code that indicates one or more of the
+ * requested events are not supported.
+ */
+ public static final int STATUS_NOT_SUPPORTED = 2;
/**
- * The key of an Intent extra indicating the response.
+ * The value of the status code that indicates service not available.
*/
- public static final String EXTRA_AMBIENT_CONTEXT_EVENT_RESPONSE =
- "android.app.ambientcontext.extra.AMBIENT_CONTEXT_EVENT_RESPONSE";
+ public static final int STATUS_SERVICE_UNAVAILABLE = 3;
/**
- * Allows clients to retrieve the response from the intent.
+ * The value of the status code that microphone is disabled.
+ */
+ public static final int STATUS_MICROPHONE_DISABLED = 4;
+
+ /**
+ * The value of the status code that the app is not granted access.
+ */
+ public static final int STATUS_ACCESS_DENIED = 5;
+
+ /** @hide */
+ @IntDef(prefix = { "STATUS_" }, value = {
+ STATUS_UNKNOWN,
+ STATUS_SUCCESS,
+ STATUS_NOT_SUPPORTED,
+ STATUS_SERVICE_UNAVAILABLE,
+ STATUS_MICROPHONE_DISABLED,
+ STATUS_ACCESS_DENIED
+ }) public @interface StatusCode {}
+
+ /**
+ * Allows clients to retrieve the list of {@link AmbientContextEvent}s from the intent.
+ *
* @param intent received from the PendingIntent callback
*
- * @return the AmbientContextEventResponse, or null if not present
+ * @return the list of events, or an empty list if the intent doesn't have such events.
*/
- @Nullable
- public static AmbientContextEventResponse getResponseFromIntent(
- @NonNull Intent intent) {
- if (intent.hasExtra(AmbientContextManager.EXTRA_AMBIENT_CONTEXT_EVENT_RESPONSE)) {
- return intent.getParcelableExtra(EXTRA_AMBIENT_CONTEXT_EVENT_RESPONSE);
+ @NonNull public static List<AmbientContextEvent> getEventsFromIntent(@NonNull Intent intent) {
+ if (intent.hasExtra(AmbientContextManager.EXTRA_AMBIENT_CONTEXT_EVENTS)) {
+ return intent.getParcelableArrayListExtra(EXTRA_AMBIENT_CONTEXT_EVENTS);
} else {
- return null;
+ return new ArrayList<>();
}
}
private final Context mContext;
- private final IAmbientContextEventObserver mService;
+ private final IAmbientContextManager mService;
/**
* {@hide}
*/
- public AmbientContextManager(Context context, IAmbientContextEventObserver service) {
+ public AmbientContextManager(Context context, IAmbientContextManager service) {
mContext = context;
mService = service;
}
/**
+ * Queries the {@link AmbientContextEvent} service status for the calling package, and
+ * sends the result to the {@link Consumer} right after the call. This is used by foreground
+ * apps to check whether the requested events are enabled for detection on the device.
+ * If all events are enabled for detection, the response has
+ * {@link AmbientContextManager#STATUS_SUCCESS}.
+ * If any of the events are not consented by user, the response has
+ * {@link AmbientContextManager#STATUS_ACCESS_DENIED}, and the app can
+ * call {@link #startConsentActivity} to redirect the user to the consent screen.
+ * <p />
+ *
+ * Example:
+ *
+ * <pre><code>
+ * Set<Integer> eventTypes = new HashSet<>();
+ * eventTypes.add(AmbientContextEvent.EVENT_COUGH);
+ * eventTypes.add(AmbientContextEvent.EVENT_SNORE);
+ *
+ * // Create Consumer
+ * Consumer<Integer> statusConsumer = response -> {
+ * int status = status.getStatusCode();
+ * if (status == AmbientContextManager.STATUS_SUCCESS) {
+ * // Show user it's enabled
+ * } else if (status == AmbientContextManager.STATUS_ACCESS_DENIED) {
+ * // Send user to grant access
+ * startConsentActivity(eventTypes);
+ * }
+ * };
+ *
+ * // Query status
+ * AmbientContextManager ambientContextManager =
+ * context.getSystemService(AmbientContextManager.class);
+ * ambientContextManager.queryAmbientContextStatus(eventTypes, executor, statusConsumer);
+ * </code></pre>
+ *
+ * @param eventTypes The set of event codes to check status on.
+ * @param executor Executor on which to run the consumer callback.
+ * @param consumer The consumer that handles the status code.
+ */
+ @RequiresPermission(Manifest.permission.ACCESS_AMBIENT_CONTEXT_EVENT)
+ public void queryAmbientContextServiceStatus(
+ @NonNull @AmbientContextEvent.EventCode Set<Integer> eventTypes,
+ @NonNull @CallbackExecutor Executor executor,
+ @NonNull @StatusCode Consumer<Integer> consumer) {
+ try {
+ RemoteCallback callback = new RemoteCallback(result -> {
+ int status = result.getInt(STATUS_RESPONSE_BUNDLE_KEY);
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ executor.execute(() -> consumer.accept(status));
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ });
+ mService.queryServiceStatus(integerSetToIntArray(eventTypes),
+ mContext.getOpPackageName(), callback);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Requests the consent data host to open an activity that allows users to modify consent.
+ *
+ * @param eventTypes The set of event codes to be consented.
+ */
+ @RequiresPermission(Manifest.permission.ACCESS_AMBIENT_CONTEXT_EVENT)
+ public void startConsentActivity(
+ @NonNull @AmbientContextEvent.EventCode Set<Integer> eventTypes) {
+ try {
+ mService.startConsentActivity(
+ integerSetToIntArray(eventTypes), mContext.getOpPackageName());
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ @NonNull
+ private static int[] integerSetToIntArray(@NonNull Set<Integer> integerSet) {
+ int[] intArray = new int[integerSet.size()];
+ int i = 0;
+ for (Integer type : integerSet) {
+ intArray[i++] = type;
+ }
+ return intArray;
+ }
+
+ /**
* Allows app to register as a {@link AmbientContextEvent} observer. The
* observer receives a callback on the provided {@link PendingIntent} when the requested
* event is detected. Registering another observer from the same package that has already been
* registered will override the previous observer.
+ * <p />
+ *
+ * Example:
+ *
+ * <pre><code>
+ * // Create request
+ * AmbientContextEventRequest request = new AmbientContextEventRequest.Builder()
+ * .addEventType(AmbientContextEvent.EVENT_COUGH)
+ * .addEventType(AmbientContextEvent.EVENT_SNORE)
+ * .build();
+ *
+ * // Create PendingIntent for delivering detection results to my receiver
+ * Intent intent = new Intent(actionString, null, context, MyBroadcastReceiver.class)
+ * .addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
+ * PendingIntent pendingIntent =
+ * PendingIntent.getBroadcast(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
+ *
+ * // Create Consumer of service status
+ * Consumer<Integer> statusConsumer = status -> {
+ * if (status == AmbientContextManager.STATUS_ACCESS_DENIED) {
+ * // User did not consent event detection. See #queryAmbientContextServiceStatus and
+ * // #startConsentActivity
+ * }
+ * };
+ *
+ * // Register as observer
+ * AmbientContextManager ambientContextManager =
+ * context.getSystemService(AmbientContextManager.class);
+ * ambientContextManager.registerObserver(request, pendingIntent, executor, statusConsumer);
+ *
+ * // Handle the list of {@link AmbientContextEvent}s in your receiver
+ * {@literal @}Override
+ * protected void onReceive(Context context, Intent intent) {
+ * List<AmbientContextEvent> events = AmbientContextManager.getEventsFromIntent(intent);
+ * if (!events.isEmpty()) {
+ * // Do something useful with the events.
+ * }
+ * }
+ * </code></pre>
*
* @param request The request with events to observe.
- * @param pendingIntent A mutable {@link PendingIntent} that will be dispatched when any
- * requested event is detected.
+ * @param resultPendingIntent A mutable {@link PendingIntent} that will be dispatched after the
+ * requested events are detected.
+ * @param executor Executor on which to run the consumer callback.
+ * @param statusConsumer A consumer that handles the status code, which is returned
+ * right after the call.
*/
@RequiresPermission(Manifest.permission.ACCESS_AMBIENT_CONTEXT_EVENT)
public void registerObserver(
@NonNull AmbientContextEventRequest request,
- @NonNull PendingIntent pendingIntent) {
- Preconditions.checkArgument(!pendingIntent.isImmutable());
+ @NonNull PendingIntent resultPendingIntent,
+ @NonNull @CallbackExecutor Executor executor,
+ @NonNull @StatusCode Consumer<Integer> statusConsumer) {
+ Preconditions.checkArgument(!resultPendingIntent.isImmutable());
try {
- mService.registerObserver(request, pendingIntent);
+ RemoteCallback callback = new RemoteCallback(result -> {
+ int statusCode = result.getInt(STATUS_RESPONSE_BUNDLE_KEY);
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ executor.execute(() -> statusConsumer.accept(statusCode));
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ });
+ mService.registerObserver(request, resultPendingIntent, callback);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
diff --git a/core/java/android/app/ambientcontext/IAmbientContextEventObserver.aidl b/core/java/android/app/ambientcontext/IAmbientContextManager.aidl
index 9032fe1ee045..3b15bcb9846d 100644
--- a/core/java/android/app/ambientcontext/IAmbientContextEventObserver.aidl
+++ b/core/java/android/app/ambientcontext/IAmbientContextManager.aidl
@@ -18,13 +18,19 @@ package android.app.ambientcontext;
import android.app.PendingIntent;
import android.app.ambientcontext.AmbientContextEventRequest;
+import android.os.RemoteCallback;
/**
- * Interface for an AmbientContextEventManager that provides access to AmbientContextEvents.
+ * Interface for an AmbientContextManager that provides access to AmbientContextEvents.
*
* @hide
*/
-oneway interface IAmbientContextEventObserver {
- void registerObserver(in AmbientContextEventRequest request, in PendingIntent pendingIntent);
+oneway interface IAmbientContextManager {
+ void registerObserver(in AmbientContextEventRequest request,
+ in PendingIntent resultPendingIntent,
+ in RemoteCallback statusCallback);
void unregisterObserver(in String callingPackage);
+ void queryServiceStatus(in int[] eventTypes, in String callingPackage,
+ in RemoteCallback statusCallback);
+ void startConsentActivity(in int[] eventTypes, in String callingPackage);
} \ No newline at end of file
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 9323d1797f0e..464567b85270 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -10321,6 +10321,34 @@ public final class Settings {
"nearby_fast_pair_settings_devices_component";
/**
+ * Current provider of the component for requesting ambient context consent.
+ * Default value in @string/config_defaultAmbientContextConsentComponent.
+ * No VALIDATOR as this setting will not be backed up.
+ * @hide
+ */
+ public static final String AMBIENT_CONTEXT_CONSENT_COMPONENT =
+ "ambient_context_consent_component";
+
+ /**
+ * Current provider of the intent extra key for the caller's package name while
+ * requesting ambient context consent.
+ * No VALIDATOR as this setting will not be backed up.
+ * @hide
+ */
+ public static final String AMBIENT_CONTEXT_PACKAGE_NAME_EXTRA_KEY =
+ "ambient_context_package_name_key";
+
+ /**
+ * Current provider of the intent extra key for the event code int array while
+ * requesting ambient context consent.
+ * Default value in @string/config_ambientContextEventArrayExtraKey.
+ * No VALIDATOR as this setting will not be backed up.
+ * @hide
+ */
+ public static final String AMBIENT_CONTEXT_EVENT_ARRAY_EXTRA_KEY =
+ "ambient_context_event_array_key";
+
+ /**
* Controls whether aware is enabled.
* @hide
*/
diff --git a/core/java/android/app/ambientcontext/AmbientContextEventResponse.aidl b/core/java/android/service/ambientcontext/AmbientContextDetectionResult.aidl
index 4dc6466c7365..4bb29b2ef16d 100644
--- a/core/java/android/app/ambientcontext/AmbientContextEventResponse.aidl
+++ b/core/java/android/service/ambientcontext/AmbientContextDetectionResult.aidl
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2021 The Android Open Source Project
+ * 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.
@@ -14,6 +14,6 @@
* limitations under the License.
*/
-package android.app.ambientcontext;
+package android.service.ambientcontext;
-parcelable AmbientContextEventResponse; \ No newline at end of file
+parcelable AmbientContextDetectionResult; \ No newline at end of file
diff --git a/core/java/android/service/ambientcontext/AmbientContextDetectionResult.java b/core/java/android/service/ambientcontext/AmbientContextDetectionResult.java
new file mode 100644
index 000000000000..227194e60a7e
--- /dev/null
+++ b/core/java/android/service/ambientcontext/AmbientContextDetectionResult.java
@@ -0,0 +1,180 @@
+/*
+ * 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.service.ambientcontext;
+
+import android.annotation.NonNull;
+import android.annotation.SuppressLint;
+import android.annotation.SystemApi;
+import android.app.ambientcontext.AmbientContextEvent;
+import android.os.Parcelable;
+
+import com.android.internal.util.AnnotationValidations;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Represents a {@code AmbientContextEvent} detection result reported by the detection service.
+ *
+ * @hide
+ */
+@SystemApi
+public final class AmbientContextDetectionResult implements Parcelable {
+
+ /**
+ * The bundle key for this class of object, used in {@code RemoteCallback#sendResult}.
+ *
+ * @hide
+ */
+ public static final String RESULT_RESPONSE_BUNDLE_KEY =
+ "android.app.ambientcontext.AmbientContextDetectionResultBundleKey";
+ @NonNull private final List<AmbientContextEvent> mEvents;
+ @NonNull private final String mPackageName;
+
+ AmbientContextDetectionResult(
+ @NonNull List<AmbientContextEvent> events,
+ @NonNull String packageName) {
+ this.mEvents = events;
+ AnnotationValidations.validate(NonNull.class, null, mEvents);
+ this.mPackageName = packageName;
+ AnnotationValidations.validate(NonNull.class, null, mPackageName);
+ }
+
+ /**
+ * A list of detected event.
+ */
+ @SuppressLint("ConcreteCollection")
+ public @NonNull List<AmbientContextEvent> getEvents() {
+ return mEvents;
+ }
+
+ /**
+ * The package to deliver the response to.
+ */
+ public @NonNull String getPackageName() {
+ return mPackageName;
+ }
+
+ @Override
+ public String toString() {
+ return "AmbientContextEventResponse { "
+ + "events = " + mEvents + ", " + "packageName = " + mPackageName + " }";
+ }
+
+ @Override
+ public void writeToParcel(@NonNull android.os.Parcel dest, int flags) {
+ byte flg = 0;
+ dest.writeByte(flg);
+ dest.writeParcelableList(mEvents, flags);
+ dest.writeString(mPackageName);
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ /** @hide */
+ @SuppressWarnings({"unchecked", "RedundantCast"})
+ AmbientContextDetectionResult(@NonNull android.os.Parcel in) {
+ byte flg = in.readByte();
+ ArrayList<AmbientContextEvent> events = new ArrayList<>();
+ in.readParcelableList(events, AmbientContextEvent.class.getClassLoader(),
+ AmbientContextEvent.class);
+ String packageName = in.readString();
+
+ this.mEvents = events;
+ AnnotationValidations.validate(
+ NonNull.class, null, mEvents);
+ this.mPackageName = packageName;
+ AnnotationValidations.validate(
+ NonNull.class, null, mPackageName);
+ }
+
+ public static final @NonNull Creator<AmbientContextDetectionResult> CREATOR =
+ new Creator<AmbientContextDetectionResult>() {
+ @Override
+ public AmbientContextDetectionResult[] newArray(int size) {
+ return new AmbientContextDetectionResult[size];
+ }
+
+ @Override
+ public AmbientContextDetectionResult createFromParcel(@NonNull android.os.Parcel in) {
+ return new AmbientContextDetectionResult(in);
+ }
+ };
+
+ /**
+ * A builder for {@link AmbientContextDetectionResult}
+ */
+ @SuppressWarnings("WeakerAccess")
+ public static final class Builder {
+ private @NonNull ArrayList<AmbientContextEvent> mEvents;
+ private @NonNull String mPackageName;
+ private long mBuilderFieldsSet = 0L;
+
+ public Builder() {
+ }
+
+ /**
+ * Adds an event to the builder.
+ */
+ public @NonNull Builder addEvent(@NonNull AmbientContextEvent value) {
+ checkNotUsed();
+ if (mEvents == null) {
+ mBuilderFieldsSet |= 0x1;
+ mEvents = new ArrayList<>();
+ }
+ mEvents.add(value);
+ return this;
+ }
+
+ /**
+ * The package to deliver the response to.
+ */
+ public @NonNull Builder setPackageName(@NonNull String value) {
+ checkNotUsed();
+ mBuilderFieldsSet |= 0x2;
+ mPackageName = value;
+ return this;
+ }
+
+ /** Builds the instance. This builder should not be touched after calling this! */
+ public @NonNull AmbientContextDetectionResult build() {
+ checkNotUsed();
+ mBuilderFieldsSet |= 0x4; // Mark builder used
+
+ if ((mBuilderFieldsSet & 0x1) == 0) {
+ mEvents = new ArrayList<>();
+ }
+ if ((mBuilderFieldsSet & 0x2) == 0) {
+ mPackageName = "";
+ }
+ AmbientContextDetectionResult o = new AmbientContextDetectionResult(
+ mEvents,
+ mPackageName);
+ return o;
+ }
+
+ private void checkNotUsed() {
+ if ((mBuilderFieldsSet & 0x4) != 0) {
+ throw new IllegalStateException(
+ "This Builder should not be reused. Use a new Builder instance instead");
+ }
+ }
+ }
+}
diff --git a/core/java/android/service/ambientcontext/AmbientContextDetectionService.java b/core/java/android/service/ambientcontext/AmbientContextDetectionService.java
index dccfe3693b35..6224aa1d102e 100644
--- a/core/java/android/service/ambientcontext/AmbientContextDetectionService.java
+++ b/core/java/android/service/ambientcontext/AmbientContextDetectionService.java
@@ -16,13 +16,13 @@
package android.service.ambientcontext;
+import android.annotation.BinderThread;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SystemApi;
import android.app.Service;
import android.app.ambientcontext.AmbientContextEvent;
import android.app.ambientcontext.AmbientContextEventRequest;
-import android.app.ambientcontext.AmbientContextEventResponse;
import android.content.Intent;
import android.os.Bundle;
import android.os.IBinder;
@@ -64,15 +64,6 @@ public abstract class AmbientContextDetectionService extends Service {
public static final String SERVICE_INTERFACE =
"android.service.ambientcontext.AmbientContextDetectionService";
- /**
- * The key for the bundle the parameter of {@code RemoteCallback#sendResult}. Implementation
- * should set bundle result with this key.
- *
- * @hide
- */
- public static final String RESPONSE_BUNDLE_KEY =
- "android.service.ambientcontext.EventResponseKey";
-
@Nullable
@Override
public final IBinder onBind(@NonNull Intent intent) {
@@ -82,19 +73,30 @@ public abstract class AmbientContextDetectionService extends Service {
@Override
public void startDetection(
@NonNull AmbientContextEventRequest request, String packageName,
- RemoteCallback callback) {
+ RemoteCallback detectionResultCallback, RemoteCallback statusCallback) {
Objects.requireNonNull(request);
- Objects.requireNonNull(callback);
- Consumer<AmbientContextEventResponse> consumer =
- response -> {
+ Objects.requireNonNull(packageName);
+ Objects.requireNonNull(detectionResultCallback);
+ Objects.requireNonNull(statusCallback);
+ Consumer<AmbientContextDetectionResult> detectionResultConsumer =
+ result -> {
Bundle bundle = new Bundle();
bundle.putParcelable(
- AmbientContextDetectionService.RESPONSE_BUNDLE_KEY,
- response);
- callback.sendResult(bundle);
+ AmbientContextDetectionResult.RESULT_RESPONSE_BUNDLE_KEY,
+ result);
+ detectionResultCallback.sendResult(bundle);
+ };
+ Consumer<AmbientContextDetectionServiceStatus> statusConsumer =
+ status -> {
+ Bundle bundle = new Bundle();
+ bundle.putParcelable(
+ AmbientContextDetectionServiceStatus
+ .STATUS_RESPONSE_BUNDLE_KEY,
+ status);
+ statusCallback.sendResult(bundle);
};
AmbientContextDetectionService.this.onStartDetection(
- request, packageName, consumer);
+ request, packageName, detectionResultConsumer, statusConsumer);
Slog.d(TAG, "startDetection " + request);
}
@@ -104,29 +106,52 @@ public abstract class AmbientContextDetectionService extends Service {
Objects.requireNonNull(packageName);
AmbientContextDetectionService.this.onStopDetection(packageName);
}
+
+ /** {@inheritDoc} */
+ @Override
+ public void queryServiceStatus(
+ @AmbientContextEvent.EventCode int[] eventTypes,
+ String packageName,
+ RemoteCallback callback) {
+ Objects.requireNonNull(eventTypes);
+ Objects.requireNonNull(packageName);
+ Objects.requireNonNull(callback);
+ Consumer<AmbientContextDetectionServiceStatus> consumer =
+ response -> {
+ Bundle bundle = new Bundle();
+ bundle.putParcelable(
+ AmbientContextDetectionServiceStatus
+ .STATUS_RESPONSE_BUNDLE_KEY,
+ response);
+ callback.sendResult(bundle);
+ };
+ AmbientContextDetectionService.this.onQueryServiceStatus(
+ eventTypes, packageName, consumer);
+ }
};
}
return null;
}
/**
- * Starts detection and provides detected events to the consumer. The ongoing detection will
- * keep running, until onStopDetection is called. If there were previously requested
+ * Starts detection and provides detected events to the statusConsumer. The ongoing detection
+ * will keep running, until onStopDetection is called. If there were previously requested
* detection from the same package, the previous request will be replaced with the new request.
* The implementation should keep track of whether the user consented each requested
- * AmbientContextEvent for the app. If not consented, the response should set status
- * STATUS_ACCESS_DENIED and include an action PendingIntent for the app to redirect the user
- * to the consent screen.
+ * AmbientContextEvent for the app. If not consented, the statusConsumer should get a response
+ * with STATUS_ACCESS_DENIED.
*
- * @param request The request with events to detect, optional detection window and other
- * options.
+ * @param request The request with events to detect.
* @param packageName the requesting app's package name
- * @param consumer the consumer for the detected event
+ * @param detectionResultConsumer the consumer for the detected event
+ * @param statusConsumer the consumer for the service status.
*/
+ @BinderThread
public abstract void onStartDetection(
@NonNull AmbientContextEventRequest request,
@NonNull String packageName,
- @NonNull Consumer<AmbientContextEventResponse> consumer);
+ @NonNull Consumer<AmbientContextDetectionResult> detectionResultConsumer,
+ @NonNull Consumer<AmbientContextDetectionServiceStatus> statusConsumer);
/**
* Stops detection of the events. Events that are not being detected will be ignored.
@@ -134,4 +159,19 @@ public abstract class AmbientContextDetectionService extends Service {
* @param packageName stops detection for the given package.
*/
public abstract void onStopDetection(@NonNull String packageName);
+
+ /**
+ * Called when a query for the detection status occurs. The implementation should check
+ * the detection status of the requested events for the package, and provide results in a
+ * {@link AmbientContextDetectionServiceStatus} for the consumer.
+ *
+ * @param eventTypes The events to check for status.
+ * @param packageName the requesting app's package name
+ * @param consumer the consumer for the query results
+ */
+ @BinderThread
+ public abstract void onQueryServiceStatus(
+ @NonNull int[] eventTypes,
+ @NonNull String packageName,
+ @NonNull Consumer<AmbientContextDetectionServiceStatus> consumer);
}
diff --git a/core/java/android/service/ambientcontext/AmbientContextDetectionServiceStatus.aidl b/core/java/android/service/ambientcontext/AmbientContextDetectionServiceStatus.aidl
new file mode 100644
index 000000000000..979cf7b69ddf
--- /dev/null
+++ b/core/java/android/service/ambientcontext/AmbientContextDetectionServiceStatus.aidl
@@ -0,0 +1,19 @@
+/*
+ * 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.service.ambientcontext;
+
+parcelable AmbientContextDetectionServiceStatus; \ No newline at end of file
diff --git a/core/java/android/service/ambientcontext/AmbientContextDetectionServiceStatus.java b/core/java/android/service/ambientcontext/AmbientContextDetectionServiceStatus.java
new file mode 100644
index 000000000000..3e92f39893de
--- /dev/null
+++ b/core/java/android/service/ambientcontext/AmbientContextDetectionServiceStatus.java
@@ -0,0 +1,171 @@
+/*
+ * 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.service.ambientcontext;
+
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.app.ambientcontext.AmbientContextManager;
+import android.app.ambientcontext.AmbientContextManager.StatusCode;
+import android.os.Parcelable;
+
+import com.android.internal.util.AnnotationValidations;
+
+/**
+ * Represents a status for the {@code AmbientContextDetectionService}.
+ *
+ * @hide
+ */
+@SystemApi
+public final class AmbientContextDetectionServiceStatus implements Parcelable {
+ /**
+ * The bundle key for this class of object, used in {@code RemoteCallback#sendResult}.
+ *
+ * @hide
+ */
+ public static final String STATUS_RESPONSE_BUNDLE_KEY =
+ "android.app.ambientcontext.AmbientContextServiceStatusBundleKey";
+
+ @StatusCode private final int mStatusCode;
+ @NonNull private final String mPackageName;
+
+ AmbientContextDetectionServiceStatus(
+ @StatusCode int statusCode,
+ @NonNull String packageName) {
+ this.mStatusCode = statusCode;
+ AnnotationValidations.validate(StatusCode.class, null, mStatusCode);
+ this.mPackageName = packageName;
+ }
+
+ /**
+ * The status of the service.
+ */
+ public @StatusCode int getStatusCode() {
+ return mStatusCode;
+ }
+
+ /**
+ * The package to deliver the response to.
+ */
+ public @NonNull String getPackageName() {
+ return mPackageName;
+ }
+
+ @Override
+ public String toString() {
+ return "AmbientContextDetectionServiceStatus { " + "statusCode = " + mStatusCode + ", "
+ + "packageName = " + mPackageName + " }";
+ }
+
+ @Override
+ public void writeToParcel(@NonNull android.os.Parcel dest, int flags) {
+ byte flg = 0;
+ dest.writeByte(flg);
+ dest.writeInt(mStatusCode);
+ dest.writeString(mPackageName);
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ /** @hide */
+ @SuppressWarnings({"unchecked", "RedundantCast"})
+ AmbientContextDetectionServiceStatus(@NonNull android.os.Parcel in) {
+ byte flg = in.readByte();
+ int statusCode = in.readInt();
+ String packageName = in.readString();
+
+ this.mStatusCode = statusCode;
+ AnnotationValidations.validate(
+ StatusCode.class, null, mStatusCode);
+ this.mPackageName = packageName;
+ AnnotationValidations.validate(
+ NonNull.class, null, mPackageName);
+ }
+
+ public static final @NonNull Creator<AmbientContextDetectionServiceStatus> CREATOR =
+ new Creator<AmbientContextDetectionServiceStatus>() {
+ @Override
+ public AmbientContextDetectionServiceStatus[] newArray(int size) {
+ return new AmbientContextDetectionServiceStatus[size];
+ }
+
+ @Override
+ public AmbientContextDetectionServiceStatus createFromParcel(
+ @NonNull android.os.Parcel in) {
+ return new AmbientContextDetectionServiceStatus(in);
+ }
+ };
+
+ /**
+ * A builder for {@link AmbientContextDetectionServiceStatus}
+ */
+ @SuppressWarnings("WeakerAccess")
+ public static final class Builder {
+ private @StatusCode int mStatusCode;
+ private @NonNull String mPackageName;
+ private long mBuilderFieldsSet = 0L;
+
+ public Builder() {
+ }
+
+ /**
+ * Sets the status of the service.
+ */
+ public @NonNull Builder setStatusCode(@StatusCode int value) {
+ checkNotUsed();
+ mBuilderFieldsSet |= 0x1;
+ mStatusCode = value;
+ return this;
+ }
+
+ /**
+ * The package to deliver the response to.
+ */
+ public @NonNull Builder setPackageName(@NonNull String value) {
+ checkNotUsed();
+ mBuilderFieldsSet |= 0x2;
+ mPackageName = value;
+ return this;
+ }
+
+ /** Builds the instance. This builder should not be touched after calling this! */
+ public @NonNull AmbientContextDetectionServiceStatus build() {
+ checkNotUsed();
+ mBuilderFieldsSet |= 0x4; // Mark builder used
+
+ if ((mBuilderFieldsSet & 0x1) == 0) {
+ mStatusCode = AmbientContextManager.STATUS_UNKNOWN;
+ }
+ if ((mBuilderFieldsSet & 0x2) == 0) {
+ mPackageName = "";
+ }
+ AmbientContextDetectionServiceStatus o = new AmbientContextDetectionServiceStatus(
+ mStatusCode,
+ mPackageName);
+ return o;
+ }
+
+ private void checkNotUsed() {
+ if ((mBuilderFieldsSet & 0x4) != 0) {
+ throw new IllegalStateException(
+ "This Builder should not be reused. Use a new Builder instance instead");
+ }
+ }
+ }
+}
diff --git a/core/java/android/service/ambientcontext/IAmbientContextDetectionService.aidl b/core/java/android/service/ambientcontext/IAmbientContextDetectionService.aidl
index 1c6e25efeabe..50c89c0832eb 100644
--- a/core/java/android/service/ambientcontext/IAmbientContextDetectionService.aidl
+++ b/core/java/android/service/ambientcontext/IAmbientContextDetectionService.aidl
@@ -26,6 +26,8 @@ import android.os.RemoteCallback;
*/
oneway interface IAmbientContextDetectionService {
void startDetection(in AmbientContextEventRequest request, in String packageName,
- in RemoteCallback callback);
+ in RemoteCallback detectionResultCallback, in RemoteCallback statusCallback);
void stopDetection(in String packageName);
+ void queryServiceStatus(in int[] eventTypes, in String packageName,
+ in RemoteCallback callback);
} \ No newline at end of file