Merge "Wearable events for AmbientContextManager."

This commit is contained in:
Charlie Wang
2022-12-21 17:46:00 +00:00
committed by Android (Google) Code Review
16 changed files with 1381 additions and 267 deletions

View File

@@ -1398,12 +1398,15 @@ package android.app.ambientcontext {
method @NonNull public java.time.Instant getEndTime();
method public int getEventType();
method @NonNull public java.time.Instant getStartTime();
method @NonNull public android.os.PersistableBundle getVendorData();
method public void writeToParcel(@NonNull android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.app.ambientcontext.AmbientContextEvent> CREATOR;
field public static final int EVENT_BACK_DOUBLE_TAP = 3; // 0x3
field public static final int EVENT_COUGH = 1; // 0x1
field public static final int EVENT_SNORE = 2; // 0x2
field public static final int EVENT_UNKNOWN = 0; // 0x0
field public static final int EVENT_VENDOR_WEARABLE_START = 100000; // 0x186a0
field public static final String KEY_VENDOR_WEARABLE_EVENT_NAME = "wearable_event_name";
field public static final int LEVEL_HIGH = 5; // 0x5
field public static final int LEVEL_LOW = 1; // 0x1
field public static final int LEVEL_MEDIUM = 3; // 0x3
@@ -1420,6 +1423,7 @@ package android.app.ambientcontext {
method @NonNull public android.app.ambientcontext.AmbientContextEvent.Builder setEndTime(@NonNull java.time.Instant);
method @NonNull public android.app.ambientcontext.AmbientContextEvent.Builder setEventType(int);
method @NonNull public android.app.ambientcontext.AmbientContextEvent.Builder setStartTime(@NonNull java.time.Instant);
method @NonNull public android.app.ambientcontext.AmbientContextEvent.Builder setVendorData(@NonNull android.os.PersistableBundle);
}
public final class AmbientContextEventRequest implements android.os.Parcelable {
@@ -12532,6 +12536,9 @@ package android.service.wearable {
method @Nullable public final android.os.IBinder onBind(@NonNull android.content.Intent);
method @BinderThread public abstract void onDataProvided(@NonNull android.os.PersistableBundle, @Nullable android.os.SharedMemory, @NonNull java.util.function.Consumer<java.lang.Integer>);
method @BinderThread public abstract void onDataStreamProvided(@NonNull android.os.ParcelFileDescriptor, @NonNull java.util.function.Consumer<java.lang.Integer>);
method @BinderThread public abstract void onQueryServiceStatus(@NonNull java.util.Set<java.lang.Integer>, @NonNull String, @NonNull java.util.function.Consumer<android.service.ambientcontext.AmbientContextDetectionServiceStatus>);
method @BinderThread public abstract void onStartDetection(@NonNull android.app.ambientcontext.AmbientContextEventRequest, @NonNull String, @NonNull java.util.function.Consumer<android.service.ambientcontext.AmbientContextDetectionServiceStatus>, @NonNull java.util.function.Consumer<android.service.ambientcontext.AmbientContextDetectionResult>);
method public abstract void onStopDetection(@NonNull String);
field public static final String SERVICE_INTERFACE = "android.service.wearable.WearableSensingService";
}

View File

@@ -20,6 +20,7 @@ import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.SystemApi;
import android.os.Parcelable;
import android.os.PersistableBundle;
import com.android.internal.util.DataClass;
import com.android.internal.util.Parcelling;
@@ -66,12 +67,25 @@ public final class AmbientContextEvent implements Parcelable {
*/
public static final int EVENT_BACK_DOUBLE_TAP = 3;
/**
* Integer indicating the start of wearable vendor defined events that can be detected.
* These depend on the vendor implementation.
*/
public static final int EVENT_VENDOR_WEARABLE_START = 100000;
/**
* Name for the mVendorData object for this AmbientContextEvent. The mVendorData must be present
* in the object, or it will be rejected.
*/
public static final String KEY_VENDOR_WEARABLE_EVENT_NAME = "wearable_event_name";
/** @hide */
@IntDef(prefix = { "EVENT_" }, value = {
EVENT_UNKNOWN,
EVENT_COUGH,
EVENT_SNORE,
EVENT_BACK_DOUBLE_TAP,
EVENT_VENDOR_WEARABLE_START,
}) public @interface EventCode {}
/** The integer indicating an unknown level. */
@@ -139,6 +153,19 @@ public final class AmbientContextEvent implements Parcelable {
return LEVEL_UNKNOWN;
}
/**
* Vendor defined specific values for vendor event types.
*
* <p> The use of this vendor data is discouraged. For data defined in the range above
* {@code EVENT_VENDOR_WEARABLE_START} this bundle must include the
* {@link KEY_VENDOR_WEARABLE_EVENT_NAME} field or it will be rejected. In addition, to increase
* transparency of this data contents of this bundle will be logged to logcat.</p>
*/
private final @NonNull PersistableBundle mVendorData;
private static PersistableBundle defaultVendorData() {
return new PersistableBundle();
}
// Code below generated by codegen v1.0.23.
@@ -159,7 +186,8 @@ public final class AmbientContextEvent implements Parcelable {
EVENT_UNKNOWN,
EVENT_COUGH,
EVENT_SNORE,
EVENT_BACK_DOUBLE_TAP
EVENT_BACK_DOUBLE_TAP,
EVENT_VENDOR_WEARABLE_START
})
@Retention(RetentionPolicy.SOURCE)
@DataClass.Generated.Member
@@ -177,6 +205,8 @@ public final class AmbientContextEvent implements Parcelable {
return "EVENT_SNORE";
case EVENT_BACK_DOUBLE_TAP:
return "EVENT_BACK_DOUBLE_TAP";
case EVENT_VENDOR_WEARABLE_START:
return "EVENT_VENDOR_WEARABLE_START";
default: return Integer.toHexString(value);
}
}
@@ -220,7 +250,8 @@ public final class AmbientContextEvent implements Parcelable {
@NonNull Instant startTime,
@NonNull Instant endTime,
@LevelValue int confidenceLevel,
@LevelValue int densityLevel) {
@LevelValue int densityLevel,
@NonNull PersistableBundle vendorData) {
this.mEventType = eventType;
com.android.internal.util.AnnotationValidations.validate(
EventCode.class, null, mEventType);
@@ -236,6 +267,9 @@ public final class AmbientContextEvent implements Parcelable {
this.mDensityLevel = densityLevel;
com.android.internal.util.AnnotationValidations.validate(
LevelValue.class, null, mDensityLevel);
this.mVendorData = vendorData;
com.android.internal.util.AnnotationValidations.validate(
NonNull.class, null, mVendorData);
// onConstructed(); // You can define this method to get a callback
}
@@ -279,6 +313,19 @@ public final class AmbientContextEvent implements Parcelable {
return mDensityLevel;
}
/**
* Vendor defined specific values for vendor event types.
*
* <p> The use of this vendor data is discouraged. For data defined in the range above
* {@code EVENT_VENDOR_WEARABLE_START} this bundle must include the
* {@link KEY_VENDOR_WEARABLE_EVENT_NAME} field or it will be rejected. In addition, to increase
* transparency of this data contents of this bundle will be logged to logcat.</p>
*/
@DataClass.Generated.Member
public @NonNull PersistableBundle getVendorData() {
return mVendorData;
}
@Override
@DataClass.Generated.Member
public String toString() {
@@ -290,7 +337,8 @@ public final class AmbientContextEvent implements Parcelable {
"startTime = " + mStartTime + ", " +
"endTime = " + mEndTime + ", " +
"confidenceLevel = " + mConfidenceLevel + ", " +
"densityLevel = " + mDensityLevel +
"densityLevel = " + mDensityLevel + ", " +
"vendorData = " + mVendorData +
" }";
}
@@ -327,6 +375,7 @@ public final class AmbientContextEvent implements Parcelable {
sParcellingForEndTime.parcel(mEndTime, dest, flags);
dest.writeInt(mConfidenceLevel);
dest.writeInt(mDensityLevel);
dest.writeTypedObject(mVendorData, flags);
}
@Override
@@ -345,6 +394,7 @@ public final class AmbientContextEvent implements Parcelable {
Instant endTime = sParcellingForEndTime.unparcel(in);
int confidenceLevel = in.readInt();
int densityLevel = in.readInt();
PersistableBundle vendorData = (PersistableBundle) in.readTypedObject(PersistableBundle.CREATOR);
this.mEventType = eventType;
com.android.internal.util.AnnotationValidations.validate(
@@ -361,6 +411,9 @@ public final class AmbientContextEvent implements Parcelable {
this.mDensityLevel = densityLevel;
com.android.internal.util.AnnotationValidations.validate(
LevelValue.class, null, mDensityLevel);
this.mVendorData = vendorData;
com.android.internal.util.AnnotationValidations.validate(
NonNull.class, null, mVendorData);
// onConstructed(); // You can define this method to get a callback
}
@@ -391,6 +444,7 @@ public final class AmbientContextEvent implements Parcelable {
private @NonNull Instant mEndTime;
private @LevelValue int mConfidenceLevel;
private @LevelValue int mDensityLevel;
private @NonNull PersistableBundle mVendorData;
private long mBuilderFieldsSet = 0L;
@@ -451,10 +505,26 @@ public final class AmbientContextEvent implements Parcelable {
return this;
}
/**
* Vendor defined specific values for vendor event types.
*
* <p> The use of this vendor data is discouraged. For data defined in the range above
* {@code EVENT_VENDOR_WEARABLE_START} this bundle must include the
* {@link KEY_VENDOR_WEARABLE_EVENT_NAME} field or it will be rejected. In addition, to increase
* transparency of this data contents of this bundle will be logged to logcat.</p>
*/
@DataClass.Generated.Member
public @NonNull Builder setVendorData(@NonNull PersistableBundle value) {
checkNotUsed();
mBuilderFieldsSet |= 0x20;
mVendorData = value;
return this;
}
/** Builds the instance. This builder should not be touched after calling this! */
public @NonNull AmbientContextEvent build() {
checkNotUsed();
mBuilderFieldsSet |= 0x20; // Mark builder used
mBuilderFieldsSet |= 0x40; // Mark builder used
if ((mBuilderFieldsSet & 0x1) == 0) {
mEventType = defaultEventType();
@@ -471,17 +541,21 @@ public final class AmbientContextEvent implements Parcelable {
if ((mBuilderFieldsSet & 0x10) == 0) {
mDensityLevel = defaultDensityLevel();
}
if ((mBuilderFieldsSet & 0x20) == 0) {
mVendorData = defaultVendorData();
}
AmbientContextEvent o = new AmbientContextEvent(
mEventType,
mStartTime,
mEndTime,
mConfidenceLevel,
mDensityLevel);
mDensityLevel,
mVendorData);
return o;
}
private void checkNotUsed() {
if ((mBuilderFieldsSet & 0x20) != 0) {
if ((mBuilderFieldsSet & 0x40) != 0) {
throw new IllegalStateException(
"This Builder should not be reused. Use a new Builder instance instead");
}
@@ -489,10 +563,10 @@ public final class AmbientContextEvent implements Parcelable {
}
@DataClass.Generated(
time = 1659950304931L,
time = 1671217108067L,
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 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)")
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 EVENT_VENDOR_WEARABLE_START\npublic static final java.lang.String KEY_VENDOR_WEARABLE_EVENT_NAME\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 final @android.annotation.NonNull android.os.PersistableBundle mVendorData\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()\nprivate static android.os.PersistableBundle defaultVendorData()\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

@@ -117,7 +117,8 @@ public final class AmbientContextManager {
*/
@NonNull public static List<AmbientContextEvent> getEventsFromIntent(@NonNull Intent intent) {
if (intent.hasExtra(AmbientContextManager.EXTRA_AMBIENT_CONTEXT_EVENTS)) {
return intent.getParcelableArrayListExtra(EXTRA_AMBIENT_CONTEXT_EVENTS, android.app.ambientcontext.AmbientContextEvent.class);
return intent.getParcelableArrayListExtra(EXTRA_AMBIENT_CONTEXT_EVENTS,
android.app.ambientcontext.AmbientContextEvent.class);
} else {
return new ArrayList<>();
}
@@ -143,6 +144,9 @@ public final class AmbientContextManager {
* 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.
* If the AmbientContextRequest contains a mixed set of events containing values both greater
* than and less than {@link AmbientContextEvent.EVENT_VENDOR_WEARABLE_START}, the request
* will be rejected with {@link AmbientContextManager#STATUS_NOT_SUPPORTED}.
* <p />
*
* Example:
@@ -197,6 +201,9 @@ public final class AmbientContextManager {
/**
* Requests the consent data host to open an activity that allows users to modify consent.
* If the eventTypes contains a mixed set of events containing values both greater than and less
* than {@link AmbientContextEvent.EVENT_VENDOR_WEARABLE_START}, the request will be rejected
* with {@link AmbientContextManager#STATUS_NOT_SUPPORTED}.
*
* @param eventTypes The set of event codes to be consented.
*/
@@ -226,6 +233,9 @@ public final class AmbientContextManager {
* 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.
* If the AmbientContextRequest contains a mixed set of events containing values both greater
* than and less than {@link AmbientContextEvent.EVENT_VENDOR_WEARABLE_START}, the request
* will be rejected with {@link AmbientContextManager#STATUS_NOT_SUPPORTED}.
* <p />
*
* Example:
@@ -308,6 +318,9 @@ public final class AmbientContextManager {
* {@link #registerObserver(AmbientContextEventRequest, PendingIntent, Executor, Consumer)},
* the previous observer will be replaced with the new observer with the PendingIntent callback.
* Or vice versa.
* If the AmbientContextRequest contains a mixed set of events containing values both greater
* than and less than {@link AmbientContextEvent.EVENT_VENDOR_WEARABLE_START}, the request
* will be rejected with {@link AmbientContextManager#STATUS_NOT_SUPPORTED}.
*
* When the registration completes, a status will be returned to client through
* {@link AmbientContextCallback#onRegistrationComplete(int)}.

View File

@@ -16,6 +16,7 @@
package android.service.wearable;
import android.app.ambientcontext.AmbientContextEventRequest;
import android.os.PersistableBundle;
import android.os.RemoteCallback;
import android.os.SharedMemory;
@@ -29,4 +30,8 @@ import android.os.SharedMemory;
oneway interface IWearableSensingService {
void provideDataStream(in ParcelFileDescriptor parcelFileDescriptor, in RemoteCallback callback);
void provideData(in PersistableBundle data, in SharedMemory sharedMemory, in RemoteCallback callback);
void startDetection(in AmbientContextEventRequest request, in String packageName,
in RemoteCallback detectionResultCallback, in RemoteCallback statusCallback);
void stopDetection(in String packageName);
void queryServiceStatus(in int[] eventTypes, in String packageName, in RemoteCallback callback);
}

View File

@@ -22,6 +22,7 @@ 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.wearable.WearableSensingManager;
import android.content.Intent;
import android.os.Bundle;
@@ -30,9 +31,14 @@ import android.os.ParcelFileDescriptor;
import android.os.PersistableBundle;
import android.os.RemoteCallback;
import android.os.SharedMemory;
import android.service.ambientcontext.AmbientContextDetectionResult;
import android.service.ambientcontext.AmbientContextDetectionServiceStatus;
import android.util.Slog;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Objects;
import java.util.Set;
import java.util.function.Consumer;
/**
@@ -116,6 +122,60 @@ public abstract class WearableSensingService extends Service {
};
WearableSensingService.this.onDataProvided(data, sharedMemory, consumer);
}
/** {@inheritDoc} */
@Override
public void startDetection(@NonNull AmbientContextEventRequest request,
String packageName, RemoteCallback detectionResultCallback,
RemoteCallback statusCallback) {
Objects.requireNonNull(request);
Objects.requireNonNull(packageName);
Objects.requireNonNull(detectionResultCallback);
Objects.requireNonNull(statusCallback);
Consumer<AmbientContextDetectionResult> detectionResultConsumer = result -> {
Bundle bundle = new Bundle();
bundle.putParcelable(
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);
};
WearableSensingService.this.onStartDetection(
request, packageName, statusConsumer, detectionResultConsumer);
Slog.d(TAG, "startDetection " + request);
}
/** {@inheritDoc} */
@Override
public void stopDetection(String packageName) {
Objects.requireNonNull(packageName);
WearableSensingService.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);
};
Integer[] events = intArrayToIntegerArray(eventTypes);
WearableSensingService.this.onQueryServiceStatus(
new HashSet<>(Arrays.asList(events)), packageName, consumer);
}
};
}
Slog.w(TAG, "Incorrect service interface, returning null.");
@@ -155,4 +215,61 @@ public abstract class WearableSensingService extends Service {
@NonNull PersistableBundle data,
@Nullable SharedMemory sharedMemory,
@NonNull Consumer<Integer> statusConsumer);
/**
* Called when a client app requests starting detection of the events in the request. The
* implementation should keep track of whether the user has explicitly consented to detecting
* the events using on-going ambient sensor (e.g. microphone), and agreed to share the
* detection results with this client app. If the user has not consented, the detection
* should not start, and the statusConsumer should get a response with STATUS_ACCESS_DENIED.
* If the user has made the consent and the underlying services are available, the
* implementation should start detection and provide detected events to the
* detectionResultConsumer. If the type of event needs immediate attention, the implementation
* should send result as soon as detected. Otherwise, the implementation can batch response.
* The ongoing detection will keep running, until onStopDetection is called. If there were
* previously requested detections from the same package, regardless of the type of events in
* the request, the previous request will be replaced with the new request and pending events
* are discarded.
*
* @param request The request with events to detect.
* @param packageName the requesting app's package name
* @param statusConsumer the consumer for the service status.
* @param detectionResultConsumer the consumer for the detected event
*/
@BinderThread
public abstract void onStartDetection(@NonNull AmbientContextEventRequest request,
@NonNull String packageName,
@NonNull Consumer<AmbientContextDetectionServiceStatus> statusConsumer,
@NonNull Consumer<AmbientContextDetectionResult> detectionResultConsumer);
/**
* Stops detection of the events. Events that are not being detected will be ignored.
*
* @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 Set<Integer> eventTypes,
@NonNull String packageName,
@NonNull Consumer<AmbientContextDetectionServiceStatus> consumer);
@NonNull
private static Integer[] intArrayToIntegerArray(@NonNull int[] integerSet) {
Integer[] intArray = new Integer[integerSet.length];
int i = 0;
for (Integer type : integerSet) {
intArray[i++] = type;
}
return intArray;
}
}

View File

@@ -4154,12 +4154,34 @@
-->
<string name="config_defaultAmbientContextDetectionService" translatable="false"></string>
<!-- The component names for the system's AmbientContextEvent detection services.
These services must be trusted, as it can be activated without explicit consent of the user.
See android.service.ambientcontext.AmbientContextDetectionService and
android.service.wearable.WearableSensingService.
-->
<string-array name="config_defaultAmbientContextServices">
<item>@string/config_defaultAmbientContextDetectionService</item>
<item>@string/config_defaultWearableSensingService</item>
</string-array>
<!-- The component name for the default system wearable sensing service.
This service must be trusted, as it can be activated without explicit consent of the user.
See android.service.wearable.WearableSensingService.
-->
<string name="config_defaultWearableSensingService" translatable="false"></string>
<!-- Component name that accepts ACTION_SEND intents for requesting ambient context consent for
wearable sensing. -->
<string translatable="false" name="config_defaultWearableSensingConsentComponent"></string>
<!-- Intent extra key for the caller's package name while requesting
ambient context consent for wearable sensing. -->
<string translatable="false" name="config_wearableAmbientContextPackageNameExtraKey"></string>
<!-- Intent extra key for the event code int array while requesting
ambient context consent for wearable sensing. -->
<string translatable="false" name="config_wearableAmbientContextEventArrayExtraKey"></string>
<!-- Component name that accepts ACTION_SEND intents for requesting ambient context consent. -->
<string translatable="false" name="config_defaultAmbientContextConsentComponent"></string>

View File

@@ -3704,6 +3704,10 @@
<java-symbol type="string" name="config_defaultSystemCaptionsManagerService" />
<java-symbol type="string" name="config_defaultAmbientContextDetectionService" />
<java-symbol type="string" name="config_defaultAmbientContextConsentComponent" />
<java-symbol type="string" name="config_defaultWearableSensingConsentComponent" />
<java-symbol type="string" name="config_wearableAmbientContextPackageNameExtraKey" />
<java-symbol type="string" name="config_wearableAmbientContextEventArrayExtraKey" />
<java-symbol type="array" name="config_defaultAmbientContextServices" />
<java-symbol type="string" name="config_ambientContextPackageNameExtraKey" />
<java-symbol type="string" name="config_ambientContextEventArrayExtraKey" />
<java-symbol type="string" name="config_defaultWearableSensingService" />

View File

@@ -16,9 +16,7 @@
package com.android.server.ambientcontext;
import android.Manifest;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.UserIdInt;
import android.app.ActivityManager;
import android.app.ActivityOptions;
@@ -58,187 +56,105 @@ import java.util.List;
import java.util.function.Consumer;
/**
* Per-user manager service for {@link AmbientContextEvent}s.
* Base per-user manager service for {@link AmbientContextEvent}s.
*/
final class AmbientContextManagerPerUserService extends
abstract class AmbientContextManagerPerUserService extends
AbstractPerUserSystemService<AmbientContextManagerPerUserService,
AmbientContextManagerService> {
private static final String TAG = AmbientContextManagerPerUserService.class.getSimpleName();
private static final String TAG =
AmbientContextManagerPerUserService.class.getSimpleName();
@Nullable
@VisibleForTesting
RemoteAmbientContextDetectionService mRemoteService;
private ComponentName mComponentName;
/**
* The type of service.
*/
enum ServiceType {
DEFAULT,
WEARABLE
}
AmbientContextManagerPerUserService(
@NonNull AmbientContextManagerService master, Object lock, @UserIdInt int userId) {
super(master, lock, userId);
}
void destroyLocked() {
Slog.d(TAG, "Trying to cancel the remote request. Reason: Service destroyed.");
if (mRemoteService != null) {
synchronized (mLock) {
mRemoteService.unbind();
mRemoteService = null;
}
}
}
@GuardedBy("mLock")
private void ensureRemoteServiceInitiated() {
if (mRemoteService == null) {
mRemoteService = new RemoteAmbientContextDetectionService(
getContext(), mComponentName, getUserId());
}
}
/**
* Returns the current bound AmbientContextManagerPerUserService component for this user.
*/
abstract ComponentName getComponentName();
/**
* get the currently bound component name.
* Sets the component name for the per user service.
*/
@VisibleForTesting
ComponentName getComponentName() {
return mComponentName;
}
abstract void setComponentName(ComponentName componentName);
/**
* Resolves and sets up the service if it had not been done yet. Returns true if the service
* is available.
* Ensures that the remote service is initiated.
*/
@GuardedBy("mLock")
@VisibleForTesting
boolean setUpServiceIfNeeded() {
if (mComponentName == null) {
mComponentName = updateServiceInfoLocked();
}
if (mComponentName == null) {
return false;
}
ServiceInfo serviceInfo;
try {
serviceInfo = AppGlobals.getPackageManager().getServiceInfo(
mComponentName, 0, mUserId);
} catch (RemoteException e) {
Slog.w(TAG, "RemoteException while setting up service");
return false;
}
return serviceInfo != null;
}
@Override
protected ServiceInfo newServiceInfoLocked(@NonNull ComponentName serviceComponent)
throws PackageManager.NameNotFoundException {
ServiceInfo serviceInfo;
try {
serviceInfo = AppGlobals.getPackageManager().getServiceInfo(serviceComponent,
0, mUserId);
if (serviceInfo != null) {
final String permission = serviceInfo.permission;
if (!Manifest.permission.BIND_AMBIENT_CONTEXT_DETECTION_SERVICE.equals(
permission)) {
throw new SecurityException(String.format(
"Service %s requires %s permission. Found %s permission",
serviceInfo.getComponentName(),
Manifest.permission.BIND_AMBIENT_CONTEXT_DETECTION_SERVICE,
serviceInfo.permission));
}
}
} catch (RemoteException e) {
throw new PackageManager.NameNotFoundException(
"Could not get service for " + serviceComponent);
}
return serviceInfo;
}
@Override
protected void dumpLocked(@NonNull String prefix, @NonNull PrintWriter pw) {
synchronized (super.mLock) {
super.dumpLocked(prefix, pw);
}
if (mRemoteService != null) {
mRemoteService.dump("", new IndentingPrintWriter(pw, " "));
}
}
abstract void ensureRemoteServiceInitiated();
/**
* Handles client registering as an observer. Only one registration is supported per app
* package. A new registration from the same package will overwrite the previous registration.
* Returns the AmbientContextManagerPerUserService {@link ServiceType} for this user.
*/
public void onRegisterObserver(AmbientContextEventRequest request,
String packageName, IAmbientContextObserver observer) {
abstract ServiceType getServiceType();
/**
* Returns the int config for the consent component for the
* specific AmbientContextManagerPerUserService type
*/
abstract int getConsentComponentConfig();
/**
* Returns the int config for the intent extra key for the
* caller's package name while requesting ambient context consent.
*/
abstract int getAmbientContextPackageNameExtraKeyConfig();
/**
* Returns the int config for the Intent extra key for the event code int array while
* requesting ambient context consent.
*/
abstract int getAmbientContextEventArrayExtraKeyConfig();
/**
* Returns the permission that is required to bind to this service.
*/
abstract String getProtectedBindPermission();
/**
* Returns the remote service implementation for this user.
*/
abstract RemoteAmbientDetectionService getRemoteService();
/**
* Clears the remote service.
*/
abstract void clearRemoteService();
/**
* Called when there's an application with the callingPackage name is requesting for
* the AmbientContextDetection's service status.
*
* @param eventTypes the event types to query for
* @param callingPackage the package query for information
* @param statusCallback the callback to deliver the status on
*/
public void onQueryServiceStatus(int[] eventTypes, String callingPackage,
RemoteCallback statusCallback) {
Slog.d(TAG, "Query event status of " + Arrays.toString(eventTypes)
+ " for " + callingPackage);
synchronized (mLock) {
if (!setUpServiceIfNeeded()) {
Slog.w(TAG, "Detection service is not available at this moment.");
completeRegistration(observer, AmbientContextManager.STATUS_SERVICE_UNAVAILABLE);
sendStatusCallback(statusCallback,
AmbientContextManager.STATUS_SERVICE_UNAVAILABLE);
return;
}
// Register package and add to existing ClientRequests cache
startDetection(request, packageName, observer);
mMaster.newClientAdded(mUserId, request, packageName, observer);
}
}
/**
* Returns a RemoteCallback that handles the status from the detection service, and
* sends results to the client callback.
*/
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 {
int statusCode = serviceStatus.getStatusCode();
statusConsumer.accept(statusCode);
Slog.i(TAG, "Got detection status of " + statusCode
+ " for " + serviceStatus.getPackageName());
} finally {
Binder.restoreCallingIdentity(token);
}
});
}
void startDetection(AmbientContextEventRequest request, String callingPackage,
IAmbientContextObserver observer) {
Slog.d(TAG, "Requested detection of " + request.getEventTypes());
synchronized (mLock) {
if (setUpServiceIfNeeded()) {
ensureRemoteServiceInitiated();
mRemoteService.startDetection(request, callingPackage,
createDetectionResultRemoteCallback(),
getServerStatusCallback(
statusCode -> completeRegistration(observer, statusCode)));
} else {
Slog.w(TAG, "No valid component found for AmbientContextDetectionService");
completeRegistration(observer,
AmbientContextManager.STATUS_NOT_SUPPORTED);
}
}
}
/**
* Sends the result response with the specified status to the callback.
*/
static void sendStatusCallback(RemoteCallback statusCallback,
@AmbientContextManager.StatusCode int statusCode) {
Bundle bundle = new Bundle();
bundle.putInt(
AmbientContextManager.STATUS_RESPONSE_BUNDLE_KEY,
statusCode);
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());
ensureRemoteServiceInitiated();
getRemoteService().queryServiceStatus(
eventTypes,
callingPackage,
getServerStatusCallback(
statusCode -> sendStatusCallback(statusCallback, statusCode)));
}
}
@@ -254,26 +170,9 @@ final class AmbientContextManagerPerUserService extends
}
}
public void onQueryServiceStatus(int[] eventTypes, String callingPackage,
RemoteCallback statusCallback) {
Slog.d(TAG, "Query event status of " + Arrays.toString(eventTypes)
+ " for " + callingPackage);
synchronized (mLock) {
if (!setUpServiceIfNeeded()) {
Slog.w(TAG, "Detection service is not available at this moment.");
sendStatusCallback(statusCallback,
AmbientContextManager.STATUS_NOT_SUPPORTED);
return;
}
ensureRemoteServiceInitiated();
mRemoteService.queryServiceStatus(
eventTypes,
callingPackage,
getServerStatusCallback(
statusCode -> sendStatusCallback(statusCallback, statusCode)));
}
}
/**
* Starts the consent activity for the calling package and event types.
*/
public void onStartConsentActivity(int[] eventTypes, String callingPackage) {
Slog.d(TAG, "Opening consent activity of " + Arrays.toString(eventTypes)
+ " for " + callingPackage);
@@ -315,9 +214,9 @@ final class AmbientContextManagerPerUserService extends
try {
Context context = getContext();
String packageNameExtraKey = context.getResources().getString(
com.android.internal.R.string.config_ambientContextPackageNameExtraKey);
getAmbientContextPackageNameExtraKeyConfig());
String eventArrayExtraKey = context.getResources().getString(
com.android.internal.R.string.config_ambientContextEventArrayExtraKey);
getAmbientContextEventArrayExtraKeyConfig());
// Create consent activity intent with the calling package name and requested events
intent.setComponent(consentComponent);
@@ -344,37 +243,163 @@ final class AmbientContextManagerPerUserService extends
}
/**
* Returns the consent activity component from config lookup.
* Handles client registering as an observer. Only one registration is supported per app
* package. A new registration from the same package will overwrite the previous registration.
*/
private ComponentName getConsentComponent() {
Context context = getContext();
String consentComponent = context.getResources().getString(
com.android.internal.R.string.config_defaultAmbientContextConsentComponent);
if (TextUtils.isEmpty(consentComponent)) {
return null;
public void onRegisterObserver(AmbientContextEventRequest request,
String packageName, IAmbientContextObserver observer) {
synchronized (mLock) {
if (!setUpServiceIfNeeded()) {
Slog.w(TAG, "Detection service is not available at this moment.");
completeRegistration(observer, AmbientContextManager.STATUS_SERVICE_UNAVAILABLE);
return;
}
// Register package and add to existing ClientRequests cache
startDetection(request, packageName, observer);
mMaster.newClientAdded(mUserId, request, packageName, observer);
}
Slog.i(TAG, "Consent component name: " + consentComponent);
return ComponentName.unflattenFromString(consentComponent);
}
@Override
protected ServiceInfo newServiceInfoLocked(@NonNull ComponentName serviceComponent)
throws PackageManager.NameNotFoundException {
Slog.d(TAG, "newServiceInfoLocked with component name: "
+ serviceComponent.getClassName());
if (getComponentName() == null
|| !serviceComponent.getClassName().equals(getComponentName().getClassName())) {
Slog.d(TAG, "service name does not match this per user, returning...");
return null;
}
ServiceInfo serviceInfo;
try {
serviceInfo = AppGlobals.getPackageManager().getServiceInfo(serviceComponent,
0, mUserId);
if (serviceInfo != null) {
final String permission = serviceInfo.permission;
if (!getProtectedBindPermission().equals(
permission)) {
throw new SecurityException(String.format(
"Service %s requires %s permission. Found %s permission",
serviceInfo.getComponentName(),
getProtectedBindPermission(),
serviceInfo.permission));
}
}
} catch (RemoteException e) {
throw new PackageManager.NameNotFoundException(
"Could not get service for " + serviceComponent);
}
return serviceInfo;
}
/**
* Dumps the remote service.
*/
protected void dumpLocked(@NonNull String prefix, @NonNull PrintWriter pw) {
synchronized (super.mLock) {
super.dumpLocked(prefix, pw);
}
RemoteAmbientDetectionService remoteService = getRemoteService();
if (remoteService != null) {
remoteService.dump("", new IndentingPrintWriter(pw, " "));
}
}
/**
* Send request to the remote AmbientContextDetectionService impl to stop detecting the
* specified events. Intended for use by shell command for testing.
* Requires ACCESS_AMBIENT_CONTEXT_EVENT permission.
*/
@VisibleForTesting
void stopDetection(String packageName) {
protected void stopDetection(String packageName) {
Slog.d(TAG, "Stop detection for " + packageName);
synchronized (mLock) {
if (mComponentName != null) {
if (getComponentName() != null) {
ensureRemoteServiceInitiated();
mRemoteService.stopDetection(packageName);
RemoteAmbientDetectionService remoteService = getRemoteService();
remoteService.stopDetection(packageName);
}
}
}
/**
* Destroys this service and unbinds from the remote service.
*/
protected void destroyLocked() {
Slog.d(TAG, "Trying to cancel the remote request. Reason: Service destroyed.");
RemoteAmbientDetectionService remoteService = getRemoteService();
if (remoteService != null) {
synchronized (mLock) {
remoteService.unbind();
clearRemoteService();
}
}
}
/**
* Send request to the remote AmbientContextDetectionService impl to start detecting the
* specified events. Intended for use by shell command for testing.
* Requires ACCESS_AMBIENT_CONTEXT_EVENT permission.
*/
protected void startDetection(AmbientContextEventRequest request, String callingPackage,
IAmbientContextObserver observer) {
Slog.d(TAG, "Requested detection of " + request.getEventTypes());
synchronized (mLock) {
if (setUpServiceIfNeeded()) {
ensureRemoteServiceInitiated();
RemoteAmbientDetectionService remoteService = getRemoteService();
remoteService.startDetection(request, callingPackage,
createDetectionResultRemoteCallback(),
getServerStatusCallback(
statusCode -> completeRegistration(observer, statusCode)));
} else {
Slog.w(TAG, "No valid component found for AmbientContextDetectionService");
completeRegistration(observer,
AmbientContextManager.STATUS_NOT_SUPPORTED);
}
}
}
/**
* Notifies the observer the status of the registration.
*
* @param observer the observer to notify
* @param statusCode the status to notify
*/
protected void completeRegistration(IAmbientContextObserver observer, int statusCode) {
try {
observer.onRegistrationComplete(statusCode);
} catch (RemoteException e) {
Slog.w(TAG, "Failed to call IAmbientContextObserver.onRegistrationComplete: "
+ e.getMessage());
}
}
/**
* Sends the status on the {@link RemoteCallback}.
*
* @param statusCallback the callback to send the status on
* @param statusCode the status to send
*/
protected void sendStatusCallback(RemoteCallback statusCallback,
@AmbientContextManager.StatusCode int statusCode) {
Bundle bundle = new Bundle();
bundle.putInt(
AmbientContextManager.STATUS_RESPONSE_BUNDLE_KEY,
statusCode);
statusCallback.sendResult(bundle);
}
/**
* Sends out the Intent to the client after the event is detected.
*
* @param pendingIntent Client's PendingIntent for callback
* @param events detected events from the detection service
*/
void sendDetectionResultIntent(PendingIntent pendingIntent,
protected void sendDetectionResultIntent(PendingIntent pendingIntent,
List<AmbientContextEvent> events) {
Intent intent = new Intent();
intent.putExtra(AmbientContextManager.EXTRA_AMBIENT_CONTEXT_EVENTS,
@@ -384,8 +409,8 @@ final class AmbientContextManagerPerUserService extends
BroadcastOptions options = BroadcastOptions.makeBasic();
options.setPendingIntentBackgroundActivityLaunchAllowed(false);
try {
pendingIntent.send(getContext(), 0, intent, null, null, null,
options.toBundle());
pendingIntent.send(getContext(), 0, intent, null,
null, null, options.toBundle());
Slog.i(TAG, "Sending PendingIntent to " + pendingIntent.getCreatorPackage() + ": "
+ events);
} catch (PendingIntent.CanceledException e) {
@@ -394,7 +419,7 @@ final class AmbientContextManagerPerUserService extends
}
@NonNull
RemoteCallback createDetectionResultRemoteCallback() {
protected RemoteCallback createDetectionResultRemoteCallback() {
return new RemoteCallback(result -> {
AmbientContextDetectionResult detectionResult =
(AmbientContextDetectionResult) result.get(
@@ -418,4 +443,80 @@ final class AmbientContextManagerPerUserService extends
}
});
}
/**
* Resolves and sets up the service if it had not been done yet. Returns true if the service
* is available.
*/
@GuardedBy("mLock")
@VisibleForTesting
private boolean setUpServiceIfNeeded() {
if (getComponentName() == null) {
ComponentName[] componentNames = updateServiceInfoListLocked();
if (componentNames == null || componentNames.length != 2) {
Slog.d(TAG, "updateServiceInfoListLocked returned incorrect componentNames");
return false;
}
switch (getServiceType()) {
case DEFAULT:
setComponentName(componentNames[0]);
break;
case WEARABLE:
setComponentName(componentNames[1]);
break;
default:
Slog.d(TAG, "updateServiceInfoListLocked returned unknown service types.");
return false;
}
}
if (getComponentName() == null) {
return false;
}
ServiceInfo serviceInfo;
try {
serviceInfo = AppGlobals.getPackageManager().getServiceInfo(
getComponentName(), 0, mUserId);
} catch (RemoteException e) {
Slog.w(TAG, "RemoteException while setting up service");
return false;
}
return serviceInfo != null;
}
/**
* Returns a RemoteCallback that handles the status from the detection service, and
* sends results to the client callback.
*/
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 {
int statusCode = serviceStatus.getStatusCode();
statusConsumer.accept(statusCode);
Slog.i(TAG, "Got detection status of " + statusCode
+ " for " + serviceStatus.getPackageName());
} finally {
Binder.restoreCallingIdentity(token);
}
});
}
/**
* Returns the consent activity component from config lookup.
*/
private ComponentName getConsentComponent() {
Context context = getContext();
String consentComponent = context.getResources().getString(getConsentComponentConfig());
if (TextUtils.isEmpty(consentComponent)) {
return null;
}
Slog.i(TAG, "Consent component name: " + consentComponent);
return ComponentName.unflattenFromString(consentComponent);
}
}

View File

@@ -17,6 +17,7 @@
package com.android.server.ambientcontext;
import static android.provider.DeviceConfig.NAMESPACE_AMBIENT_CONTEXT_MANAGER_SERVICE;
import static android.provider.DeviceConfig.NAMESPACE_WEARABLE_SENSING;
import android.Manifest;
import android.annotation.NonNull;
@@ -44,12 +45,16 @@ import com.android.internal.R;
import com.android.internal.util.DumpUtils;
import com.android.server.LocalServices;
import com.android.server.SystemService;
import com.android.server.ambientcontext.AmbientContextManagerPerUserService.ServiceType;
import com.android.server.infra.AbstractMasterSystemService;
import com.android.server.infra.FrameworkResourcesServiceNameResolver;
import com.android.server.pm.KnownPackages;
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Objects;
import java.util.Set;
@@ -62,6 +67,12 @@ public class AmbientContextManagerService extends
AmbientContextManagerPerUserService> {
private static final String TAG = AmbientContextManagerService.class.getSimpleName();
private static final String KEY_SERVICE_ENABLED = "service_enabled";
private static final Set<Integer> DEFAULT_EVENT_SET = new HashSet<>(){{
add(AmbientContextEvent.EVENT_COUGH);
add(AmbientContextEvent.EVENT_SNORE);
add(AmbientContextEvent.EVENT_BACK_DOUBLE_TAP);
}
};
/** Default value in absence of {@link DeviceConfig} override. */
private static final boolean DEFAULT_SERVICE_ENABLED = true;
@@ -104,14 +115,16 @@ public class AmbientContextManagerService extends
private final Context mContext;
boolean mIsServiceEnabled;
boolean mIsWearableServiceEnabled;
private Set<ClientRequest> mExistingClientRequests;
public AmbientContextManagerService(Context context) {
super(context,
new FrameworkResourcesServiceNameResolver(
context,
R.string.config_defaultAmbientContextDetectionService),
/*disallowProperty=*/null,
R.array.config_defaultAmbientContextServices,
/*isMultiple=*/ true),
/*disallowProperty=*/null,
PACKAGE_UPDATE_POLICY_REFRESH_EAGER
| /*To avoid high latency*/ PACKAGE_RESTART_POLICY_REFRESH_EAGER);
mContext = context;
@@ -134,6 +147,9 @@ public class AmbientContextManagerService extends
mIsServiceEnabled = DeviceConfig.getBoolean(
NAMESPACE_AMBIENT_CONTEXT_MANAGER_SERVICE,
KEY_SERVICE_ENABLED, DEFAULT_SERVICE_ENABLED);
mIsWearableServiceEnabled = DeviceConfig.getBoolean(
NAMESPACE_WEARABLE_SENSING,
KEY_SERVICE_ENABLED, DEFAULT_SERVICE_ENABLED);
}
}
@@ -180,13 +196,63 @@ public class AmbientContextManagerService extends
mIsServiceEnabled = DeviceConfig.getBoolean(
NAMESPACE_AMBIENT_CONTEXT_MANAGER_SERVICE,
KEY_SERVICE_ENABLED, DEFAULT_SERVICE_ENABLED);
mIsWearableServiceEnabled = DeviceConfig.getBoolean(
NAMESPACE_WEARABLE_SENSING,
KEY_SERVICE_ENABLED, DEFAULT_SERVICE_ENABLED);
}
}
@Override
protected AmbientContextManagerPerUserService newServiceLocked(int resolvedUserId,
boolean disabled) {
return new AmbientContextManagerPerUserService(this, mLock, resolvedUserId);
// This service uses newServiceListLocked, it is configured in multiple mode.
return null;
}
@Override // from AbstractMasterSystemService
protected List<AmbientContextManagerPerUserService> newServiceListLocked(int resolvedUserId,
boolean disabled, String[] serviceNames) {
if (serviceNames == null || serviceNames.length == 0) {
Slog.i(TAG, "serviceNames sent in newServiceListLocked is null, or empty");
return new ArrayList<>();
}
List<AmbientContextManagerPerUserService> serviceList =
new ArrayList<>(serviceNames.length);
if (!isDefaultServiceEnabled(resolvedUserId)) {
Slog.i(TAG, "Not using default services, "
+ "services provided for testing should be exactly two services.");
if (serviceNames.length == 2) {
// Expecting two services for testing, first being the default and second wearable.
serviceList.add(new DefaultAmbientContextManagerPerUserService(
this, mLock, resolvedUserId,
AmbientContextManagerPerUserService.ServiceType.DEFAULT, serviceNames[0]));
serviceList.add(new WearableAmbientContextManagerPerUserService(
this, mLock, resolvedUserId,
AmbientContextManagerPerUserService.ServiceType.WEARABLE,
serviceNames[1]));
} else {
Slog.i(TAG, "Incorrect number of services provided for testing.");
}
return serviceList;
}
for (String serviceName : serviceNames) {
Slog.d(TAG, "newServicesListLocked with service name: " + serviceName);
if (getServiceType(serviceName)
== AmbientContextManagerPerUserService.ServiceType.WEARABLE) {
serviceList.add(new
WearableAmbientContextManagerPerUserService(
this, mLock, resolvedUserId,
AmbientContextManagerPerUserService.ServiceType.WEARABLE, serviceName));
} else {
serviceList.add(new DefaultAmbientContextManagerPerUserService(
this, mLock, resolvedUserId,
AmbientContextManagerPerUserService.ServiceType.DEFAULT, serviceName));
}
}
return serviceList;
}
@Override
@@ -239,7 +305,10 @@ public class AmbientContextManagerService extends
mContext.enforceCallingOrSelfPermission(
Manifest.permission.ACCESS_AMBIENT_CONTEXT_EVENT, TAG);
synchronized (mLock) {
final AmbientContextManagerPerUserService service = getServiceForUserLocked(userId);
AmbientContextManagerPerUserService service =
getAmbientContextManagerPerUserServiceForEventTypes(
userId,
request.getEventTypes());
if (service != null) {
service.startDetection(request, packageName, observer);
} else {
@@ -257,11 +326,19 @@ public class AmbientContextManagerService extends
mContext.enforceCallingOrSelfPermission(
Manifest.permission.ACCESS_AMBIENT_CONTEXT_EVENT, TAG);
synchronized (mLock) {
final AmbientContextManagerPerUserService service = getServiceForUserLocked(userId);
if (service != null) {
service.stopDetection(packageName);
} else {
Slog.i(TAG, "service not available for user_id: " + userId);
for (ClientRequest cr : mExistingClientRequests) {
Slog.i(TAG, "Looping through clients");
if (cr.hasUserIdAndPackageName(userId, packageName)) {
Slog.i(TAG, "we have an existing client");
AmbientContextManagerPerUserService service =
getAmbientContextManagerPerUserServiceForEventTypes(
userId, cr.getRequest().getEventTypes());
if (service != null) {
service.stopDetection(packageName);
} else {
Slog.i(TAG, "service not available for user_id: " + userId);
}
}
}
}
}
@@ -276,7 +353,9 @@ public class AmbientContextManagerService extends
mContext.enforceCallingOrSelfPermission(
Manifest.permission.ACCESS_AMBIENT_CONTEXT_EVENT, TAG);
synchronized (mLock) {
final AmbientContextManagerPerUserService service = getServiceForUserLocked(userId);
AmbientContextManagerPerUserService service =
getAmbientContextManagerPerUserServiceForEventTypes(
userId, intArrayToIntegerSet(eventTypes));
if (service != null) {
service.onQueryServiceStatus(eventTypes, packageName, callback);
} else {
@@ -287,14 +366,18 @@ public class AmbientContextManagerService extends
private void restorePreviouslyEnabledClients(int userId) {
synchronized (mLock) {
final AmbientContextManagerPerUserService service = getServiceForUserLocked(userId);
for (ClientRequest clientRequest : mExistingClientRequests) {
// Start detection for previously enabled clients
if (clientRequest.hasUserId(userId)) {
Slog.d(TAG, "Restoring detection for " + clientRequest.getPackageName());
service.startDetection(clientRequest.getRequest(),
clientRequest.getPackageName(),
clientRequest.getObserver());
final List<AmbientContextManagerPerUserService> services =
getServiceListForUserLocked(userId);
for (AmbientContextManagerPerUserService service : services) {
for (ClientRequest clientRequest : mExistingClientRequests) {
// Start detection for previously enabled clients
if (clientRequest.hasUserId(userId)) {
Slog.d(TAG, "Restoring detection for "
+ clientRequest.getPackageName());
service.startDetection(clientRequest.getRequest(),
clientRequest.getPackageName(),
clientRequest.getObserver());
}
}
}
}
@@ -303,9 +386,12 @@ public class AmbientContextManagerService extends
/**
* Returns the AmbientContextManagerPerUserService component for this user.
*/
public ComponentName getComponentName(@UserIdInt int userId) {
public ComponentName getComponentName(
@UserIdInt int userId,
AmbientContextManagerPerUserService.ServiceType serviceType) {
synchronized (mLock) {
final AmbientContextManagerPerUserService service = getServiceForUserLocked(userId);
final AmbientContextManagerPerUserService service =
getServiceForType(userId, serviceType);
if (service != null) {
return service.getComponentName();
}
@@ -313,10 +399,114 @@ public class AmbientContextManagerService extends
return null;
}
private final class AmbientContextManagerInternal extends IAmbientContextManager.Stub {
final AmbientContextManagerPerUserService mService = getServiceForUserLocked(
UserHandle.getCallingUserId());
private AmbientContextManagerPerUserService getAmbientContextManagerPerUserServiceForEventTypes(
@UserIdInt int userId, Set<Integer> eventTypes) {
if (isWearableEventTypesOnly(eventTypes)) {
return getServiceForType(userId,
AmbientContextManagerPerUserService.ServiceType.WEARABLE);
} else {
return getServiceForType(userId,
AmbientContextManagerPerUserService.ServiceType.DEFAULT);
}
}
private Set<Integer> intArrayToIntegerSet(int[] eventTypes) {
Set<Integer> types = new HashSet<>();
for (Integer i : eventTypes) {
types.add(i);
}
return types;
}
private AmbientContextManagerPerUserService.ServiceType getServiceType(String serviceName) {
final String wearableService = mContext.getResources()
.getString(R.string.config_defaultWearableSensingService);
if (wearableService != null && wearableService.equals(serviceName)) {
return AmbientContextManagerPerUserService.ServiceType.WEARABLE;
}
return AmbientContextManagerPerUserService.ServiceType.DEFAULT;
}
private AmbientContextManagerPerUserService getServiceForType(int userId,
AmbientContextManagerPerUserService.ServiceType serviceType) {
Slog.d(TAG, "getServiceForType with userid: "
+ userId + " service type: " + serviceType.name());
synchronized (mLock) {
final List<AmbientContextManagerPerUserService> services =
getServiceListForUserLocked(userId);
Slog.d(TAG, "Services that are available: "
+ (services == null ? "null services" : services.size()
+ " number of services"));
if (services == null) {
return null;
}
for (AmbientContextManagerPerUserService service : services) {
if (service.getServiceType() == serviceType) {
return service;
}
}
}
return null;
}
private boolean isWearableEventTypesOnly(Set<Integer> eventTypes) {
if (eventTypes.isEmpty()) {
Slog.d(TAG, "empty event types.");
return false;
}
for (Integer eventType : eventTypes) {
if (eventType < AmbientContextEvent.EVENT_VENDOR_WEARABLE_START) {
Slog.d(TAG, "Not all events types are wearable events.");
return false;
}
}
Slog.d(TAG, "only wearable events.");
return true;
}
private boolean isWearableEventTypesOnly(int[] eventTypes) {
Integer[] events = intArrayToIntegerArray(eventTypes);
return isWearableEventTypesOnly(new HashSet<>(Arrays.asList(events)));
}
private boolean containsMixedEvents(int[] eventTypes) {
if (isWearableEventTypesOnly(eventTypes)) {
return false;
}
// It's not only wearable events so check if it's only default events.
for (Integer event : eventTypes) {
if (!DEFAULT_EVENT_SET.contains(event)) {
// mixed events.
Slog.w(TAG, "Received mixed event types, this is not supported.");
return true;
}
}
// Only default events.
return false;
}
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;
}
@NonNull
private static Integer[] intArrayToIntegerArray(@NonNull int[] integerSet) {
Integer[] intArray = new Integer[integerSet.length];
int i = 0;
for (Integer type : integerSet) {
intArray[i++] = type;
}
return intArray;
}
private final class AmbientContextManagerInternal extends IAmbientContextManager.Stub {
@Override
public void registerObserver(
AmbientContextEventRequest request, PendingIntent resultPendingIntent,
@@ -324,17 +514,21 @@ public class AmbientContextManagerService extends
Objects.requireNonNull(request);
Objects.requireNonNull(resultPendingIntent);
Objects.requireNonNull(statusCallback);
AmbientContextManagerPerUserService service =
getAmbientContextManagerPerUserServiceForEventTypes(
UserHandle.getCallingUserId(),
request.getEventTypes());
// 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);
service.sendDetectionResultIntent(resultPendingIntent, events);
}
@Override
public void onRegistrationComplete(int statusCode) throws RemoteException {
AmbientContextManagerPerUserService.sendStatusCallback(statusCallback,
service.sendStatusCallback(statusCallback,
statusCode);
}
};
@@ -356,13 +550,37 @@ public class AmbientContextManagerService extends
mContext.enforceCallingOrSelfPermission(
Manifest.permission.ACCESS_AMBIENT_CONTEXT_EVENT, TAG);
assertCalledByPackageOwner(packageName);
if (!mIsServiceEnabled) {
Slog.w(TAG, "Service not available.");
AmbientContextManagerPerUserService.completeRegistration(observer,
AmbientContextManagerPerUserService service =
getAmbientContextManagerPerUserServiceForEventTypes(
UserHandle.getCallingUserId(),
request.getEventTypes());
if (service == null) {
Slog.w(TAG, "onRegisterObserver unavailable user_id: "
+ UserHandle.getCallingUserId());
}
if (service.getServiceType() == ServiceType.DEFAULT && !mIsServiceEnabled) {
Slog.d(TAG, "Service not available.");
service.completeRegistration(observer,
AmbientContextManager.STATUS_SERVICE_UNAVAILABLE);
return;
}
mService.onRegisterObserver(request, packageName, observer);
if (service.getServiceType() == ServiceType.WEARABLE && !mIsWearableServiceEnabled) {
Slog.d(TAG, "Wearable Service not available.");
service.completeRegistration(observer,
AmbientContextManager.STATUS_SERVICE_UNAVAILABLE);
return;
}
if (containsMixedEvents(integerSetToIntArray(request.getEventTypes()))) {
Slog.d(TAG, "AmbientContextEventRequest contains mixed events,"
+ " this is not supported.");
service.completeRegistration(observer,
AmbientContextManager.STATUS_NOT_SUPPORTED);
return;
}
service.onRegisterObserver(request, packageName, observer);
}
@Override
@@ -370,7 +588,20 @@ public class AmbientContextManagerService extends
mContext.enforceCallingOrSelfPermission(
Manifest.permission.ACCESS_AMBIENT_CONTEXT_EVENT, TAG);
assertCalledByPackageOwner(callingPackage);
mService.onUnregisterObserver(callingPackage);
AmbientContextManagerPerUserService service = null;
for (ClientRequest cr : mExistingClientRequests) {
if (cr.getPackageName().equals(callingPackage)) {
service = getAmbientContextManagerPerUserServiceForEventTypes(
UserHandle.getCallingUserId(), cr.getRequest().getEventTypes());
if (service != null) {
service.onUnregisterObserver(callingPackage);
} else {
Slog.w(TAG, "onUnregisterObserver unavailable user_id: "
+ UserHandle.getCallingUserId());
}
}
}
}
@Override
@@ -382,14 +613,40 @@ public class AmbientContextManagerService extends
mContext.enforceCallingOrSelfPermission(
Manifest.permission.ACCESS_AMBIENT_CONTEXT_EVENT, TAG);
assertCalledByPackageOwner(callingPackage);
if (!mIsServiceEnabled) {
Slog.w(TAG, "Detection service not available.");
AmbientContextManagerPerUserService.sendStatusCallback(statusCallback,
AmbientContextManager.STATUS_SERVICE_UNAVAILABLE);
return;
synchronized (mLock) {
AmbientContextManagerPerUserService service =
getAmbientContextManagerPerUserServiceForEventTypes(
UserHandle.getCallingUserId(), intArrayToIntegerSet(eventTypes));
if (service == null) {
Slog.w(TAG, "onQueryServiceStatus unavailable user_id: "
+ UserHandle.getCallingUserId());
}
if (service.getServiceType() == ServiceType.DEFAULT && !mIsServiceEnabled) {
Slog.d(TAG, "Service not available.");
service.sendStatusCallback(statusCallback,
AmbientContextManager.STATUS_SERVICE_UNAVAILABLE);
return;
}
if (service.getServiceType() == ServiceType.WEARABLE
&& !mIsWearableServiceEnabled) {
Slog.d(TAG, "Wearable Service not available.");
service.sendStatusCallback(statusCallback,
AmbientContextManager.STATUS_SERVICE_UNAVAILABLE);
return;
}
if (containsMixedEvents(eventTypes)) {
Slog.d(TAG, "AmbientContextEventRequest contains mixed events,"
+ " this is not supported.");
service.sendStatusCallback(statusCallback,
AmbientContextManager.STATUS_NOT_SUPPORTED);
return;
}
service.onQueryServiceStatus(eventTypes, callingPackage,
statusCallback);
}
mService.onQueryServiceStatus(eventTypes, callingPackage,
statusCallback);
}
@Override
@@ -399,7 +656,23 @@ public class AmbientContextManagerService extends
assertCalledByPackageOwner(callingPackage);
mContext.enforceCallingOrSelfPermission(
Manifest.permission.ACCESS_AMBIENT_CONTEXT_EVENT, TAG);
mService.onStartConsentActivity(eventTypes, callingPackage);
if (containsMixedEvents(eventTypes)) {
Slog.d(TAG, "AmbientContextEventRequest contains mixed events,"
+ " this is not supported.");
return;
}
AmbientContextManagerPerUserService service =
getAmbientContextManagerPerUserServiceForEventTypes(
UserHandle.getCallingUserId(), intArrayToIntegerSet(eventTypes));
if (service != null) {
service.onStartConsentActivity(eventTypes, callingPackage);
} else {
Slog.w(TAG, "startConsentActivity unavailable user_id: "
+ UserHandle.getCallingUserId());
}
}
@Override

View File

@@ -28,6 +28,7 @@ import android.os.Binder;
import android.os.RemoteCallback;
import android.os.RemoteException;
import android.os.ShellCommand;
import android.util.Slog;
import java.io.PrintWriter;
import java.util.List;
@@ -36,6 +37,7 @@ import java.util.List;
* Shell command for {@link AmbientContextManagerService}.
*/
final class AmbientContextShellCommand extends ShellCommand {
private static final String TAG = AmbientContextShellCommand.class.getSimpleName();
private static final AmbientContextEventRequest REQUEST =
new AmbientContextEventRequest.Builder()
@@ -44,6 +46,20 @@ final class AmbientContextShellCommand extends ShellCommand {
.addEventType(AmbientContextEvent.EVENT_BACK_DOUBLE_TAP)
.build();
private static final int WEARABLE_AMBIENT_CONTEXT_EVENT_FOR_TESTING =
AmbientContextEvent.EVENT_VENDOR_WEARABLE_START + 1;
private static final AmbientContextEventRequest WEARABLE_REQUEST =
new AmbientContextEventRequest.Builder()
.addEventType(WEARABLE_AMBIENT_CONTEXT_EVENT_FOR_TESTING)
.build();
private static final AmbientContextEventRequest MIXED_REQUEST =
new AmbientContextEventRequest.Builder()
.addEventType(AmbientContextEvent.EVENT_COUGH)
.addEventType(WEARABLE_AMBIENT_CONTEXT_EVENT_FOR_TESTING)
.build();
@NonNull
private final AmbientContextManagerService mService;
@@ -106,16 +122,26 @@ final class AmbientContextShellCommand extends ShellCommand {
switch (cmd) {
case "start-detection":
return runStartDetection();
case "start-detection-wearable":
return runWearableStartDetection();
case "start-detection-mixed":
return runMixedStartDetection();
case "stop-detection":
return runStopDetection();
case "get-last-status-code":
return getLastStatusCode();
case "query-service-status":
return runQueryServiceStatus();
case "query-wearable-service-status":
return runQueryWearableServiceStatus();
case "query-mixed-service-status":
return runQueryMixedServiceStatus();
case "get-bound-package":
return getBoundPackageName();
case "set-temporary-service":
return setTemporaryService();
case "set-temporary-services":
return setTemporaryServices();
default:
return handleDefaultCommands(cmd);
}
@@ -127,6 +153,30 @@ final class AmbientContextShellCommand extends ShellCommand {
mService.startDetection(
userId, REQUEST, packageName,
sTestableCallbackInternal.createAmbientContextObserver());
mService.newClientAdded(userId, REQUEST, packageName,
sTestableCallbackInternal.createAmbientContextObserver());
return 0;
}
private int runWearableStartDetection() {
final int userId = Integer.parseInt(getNextArgRequired());
final String packageName = getNextArgRequired();
mService.startDetection(
userId, WEARABLE_REQUEST, packageName,
sTestableCallbackInternal.createAmbientContextObserver());
mService.newClientAdded(userId, WEARABLE_REQUEST, packageName,
sTestableCallbackInternal.createAmbientContextObserver());
return 0;
}
private int runMixedStartDetection() {
final int userId = Integer.parseInt(getNextArgRequired());
final String packageName = getNextArgRequired();
mService.startDetection(
userId, MIXED_REQUEST, packageName,
sTestableCallbackInternal.createAmbientContextObserver());
mService.newClientAdded(userId, MIXED_REQUEST, packageName,
sTestableCallbackInternal.createAmbientContextObserver());
return 0;
}
@@ -148,6 +198,26 @@ final class AmbientContextShellCommand extends ShellCommand {
return 0;
}
private int runQueryWearableServiceStatus() {
final int userId = Integer.parseInt(getNextArgRequired());
final String packageName = getNextArgRequired();
int[] types = new int[] {WEARABLE_AMBIENT_CONTEXT_EVENT_FOR_TESTING};
mService.queryServiceStatus(userId, packageName, types,
sTestableCallbackInternal.createRemoteStatusCallback());
return 0;
}
private int runQueryMixedServiceStatus() {
final int userId = Integer.parseInt(getNextArgRequired());
final String packageName = getNextArgRequired();
int[] types = new int[] {
AmbientContextEvent.EVENT_COUGH,
WEARABLE_AMBIENT_CONTEXT_EVENT_FOR_TESTING};
mService.queryServiceStatus(userId, packageName, types,
sTestableCallbackInternal.createRemoteStatusCallback());
return 0;
}
private int getLastStatusCode() {
final PrintWriter resultPrinter = getOutPrintWriter();
int lastStatus = sTestableCallbackInternal.getLastStatus();
@@ -163,20 +233,33 @@ final class AmbientContextShellCommand extends ShellCommand {
pw.println(" Print this help text.");
pw.println();
pw.println(" start-detection USER_ID PACKAGE_NAME: Starts AmbientContextEvent detection.");
pw.println(" start-detection-wearable USER_ID PACKAGE_NAME: "
+ "Starts AmbientContextEvent detection for wearable.");
pw.println(" start-detection-mixed USER_ID PACKAGE_NAME: "
+ " Starts AmbientContextEvent detection for mixed events.");
pw.println(" stop-detection USER_ID PACKAGE_NAME: Stops AmbientContextEvent detection.");
pw.println(" get-last-status-code: Prints the latest request status code.");
pw.println(" query-service-status USER_ID PACKAGE_NAME: Prints the service status code.");
pw.println(" query-wearable-service-status USER_ID PACKAGE_NAME: "
+ "Prints the service status code for wearable.");
pw.println(" query-mixed-service-status USER_ID PACKAGE_NAME: "
+ "Prints the service status code for mixed events.");
pw.println(" get-bound-package USER_ID:"
+ " Print the bound package that implements the service.");
pw.println(" set-temporary-service USER_ID [PACKAGE_NAME] [COMPONENT_NAME DURATION]");
pw.println(" Temporarily (for DURATION ms) changes the service implementation.");
pw.println(" To reset, call with just the USER_ID argument.");
pw.println(" set-temporary-services USER_ID "
+ "[FIRST_PACKAGE_NAME] [SECOND_PACKAGE_NAME] [COMPONENT_NAME DURATION]");
pw.println(" Temporarily (for DURATION ms) changes the service implementation.");
pw.println(" To reset, call with just the USER_ID argument.");
}
private int getBoundPackageName() {
final PrintWriter resultPrinter = getOutPrintWriter();
final int userId = Integer.parseInt(getNextArgRequired());
final ComponentName componentName = mService.getComponentName(userId);
final ComponentName componentName = mService.getComponentName(userId,
AmbientContextManagerPerUserService.ServiceType.DEFAULT);
resultPrinter.println(componentName == null ? "" : componentName.getPackageName());
return 0;
}
@@ -188,6 +271,7 @@ final class AmbientContextShellCommand extends ShellCommand {
if (serviceName == null) {
mService.resetTemporaryService(userId);
out.println("AmbientContextDetectionService temporary reset. ");
mService.setDefaultServiceEnabled(userId, true);
return 0;
}
@@ -197,4 +281,30 @@ final class AmbientContextShellCommand extends ShellCommand {
+ " for " + duration + "ms");
return 0;
}
private int setTemporaryServices() {
String[] serviceNames = new String[2];
final PrintWriter out = getOutPrintWriter();
final int userId = Integer.parseInt(getNextArgRequired());
mService.setDefaultServiceEnabled(userId, false);
final String firstServiceName = getNextArg();
final String secondServiceName = getNextArg();
if (firstServiceName == null || secondServiceName == null) {
mService.resetTemporaryService(userId);
mService.setDefaultServiceEnabled(userId, true);
out.println("AmbientContextDetectionService temporary reset.");
return 0;
}
serviceNames[0] = firstServiceName;
serviceNames[1] = secondServiceName;
final int duration = Integer.parseInt(getNextArgRequired());
mService.setTemporaryServices(userId, serviceNames, duration);
Slog.w(TAG, "AmbientContextDetectionService temporarily set to " + serviceNames[0]
+ " and " + serviceNames[1]
+ " for " + duration + "ms");
out.println("AmbientContextDetectionService temporarily set to " + serviceNames[0]
+ " and " + serviceNames[1]
+ " for " + duration + "ms");
return 0;
}
}

View File

@@ -0,0 +1,112 @@
/*
* 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 com.android.server.ambientcontext;
import android.Manifest;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.UserIdInt;
import android.app.ambientcontext.AmbientContextEvent;
import android.content.ComponentName;
import android.util.Slog;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
/**
* Per-user manager service for {@link AmbientContextEvent}s.
*/
public class DefaultAmbientContextManagerPerUserService extends
AmbientContextManagerPerUserService {
private static final String TAG =
DefaultAmbientContextManagerPerUserService.class.getSimpleName();
@Nullable
@VisibleForTesting
DefaultRemoteAmbientContextDetectionService mRemoteService;
private ComponentName mComponentName;
private final ServiceType mServiceType;
private final String mServiceName;
DefaultAmbientContextManagerPerUserService(
@NonNull AmbientContextManagerService master, Object lock,
@UserIdInt int userId, ServiceType serviceType, String serviceName) {
super(master, lock, userId);
this.mServiceType = serviceType;
this.mServiceName = serviceName;
this.mComponentName = ComponentName.unflattenFromString(mServiceName);
Slog.d(TAG, "Created DefaultAmbientContextManagerPerUserService"
+ "and service type: " + mServiceType.name() + " and service name: " + serviceName);
}
@GuardedBy("mLock")
@Override
protected void ensureRemoteServiceInitiated() {
if (mRemoteService == null) {
mRemoteService = new DefaultRemoteAmbientContextDetectionService(
getContext(), mComponentName, getUserId());
}
}
@VisibleForTesting
@Override
ComponentName getComponentName() {
return mComponentName;
}
@Override
protected void setComponentName(ComponentName componentName) {
this.mComponentName = componentName;
}
@Override
protected RemoteAmbientDetectionService getRemoteService() {
return mRemoteService;
}
@Override
protected String getProtectedBindPermission() {
return Manifest.permission.BIND_AMBIENT_CONTEXT_DETECTION_SERVICE;
}
@Override
public ServiceType getServiceType() {
return mServiceType;
}
@Override
protected int getAmbientContextPackageNameExtraKeyConfig() {
return com.android.internal.R.string.config_ambientContextPackageNameExtraKey;
}
@Override
protected int getAmbientContextEventArrayExtraKeyConfig() {
return com.android.internal.R.string.config_ambientContextEventArrayExtraKey;
}
@Override
protected int getConsentComponentConfig() {
return com.android.internal.R.string.config_defaultAmbientContextConsentComponent;
}
@Override
protected void clearRemoteService() {
mRemoteService = null;
}
}

View File

@@ -32,13 +32,16 @@ import android.util.Slog;
import com.android.internal.infra.ServiceConnector;
/** Manages the connection to the remote service. */
final class RemoteAmbientContextDetectionService
extends ServiceConnector.Impl<IAmbientContextDetectionService> {
private static final String TAG =
RemoteAmbientContextDetectionService.class.getSimpleName();
import java.io.PrintWriter;
RemoteAmbientContextDetectionService(Context context, ComponentName serviceName,
/** Manages the connection to the remote service. */
final class DefaultRemoteAmbientContextDetectionService
extends ServiceConnector.Impl<IAmbientContextDetectionService>
implements RemoteAmbientDetectionService {
private static final String TAG =
DefaultRemoteAmbientContextDetectionService.class.getSimpleName();
DefaultRemoteAmbientContextDetectionService(Context context, ComponentName serviceName,
int userId) {
super(context, new Intent(
AmbientContextDetectionService.SERVICE_INTERFACE).setComponent(serviceName),
@@ -55,14 +58,7 @@ final class RemoteAmbientContextDetectionService
return -1;
}
/**
* Asks the implementation to start detection.
*
* @param request The request with events to detect, and optional detection options.
* @param packageName The app package that requested the detection
* @param detectionResultCallback callback for detection results
* @param statusCallback callback for service status
*/
@Override
public void startDetection(
@NonNull AmbientContextEventRequest request, String packageName,
RemoteCallback detectionResultCallback, RemoteCallback statusCallback) {
@@ -71,19 +67,13 @@ final class RemoteAmbientContextDetectionService
statusCallback));
}
/**
* Asks the implementation to stop detection.
*
* @param packageName stop detection for the given package
*/
@Override
public void stopDetection(String packageName) {
Slog.i(TAG, "Stop detection for " + packageName);
post(service -> service.stopDetection(packageName));
}
/**
* Asks the implementation to return the event status for the package.
*/
@Override
public void queryServiceStatus(
@AmbientContextEvent.EventCode int[] eventTypes,
String packageName,
@@ -91,4 +81,14 @@ final class RemoteAmbientContextDetectionService
Slog.i(TAG, "Query status for " + packageName);
post(service -> service.queryServiceStatus(eventTypes, packageName, callback));
}
@Override
public void dump(@NonNull String prefix, @NonNull PrintWriter pw) {
super.dump(prefix, pw);
}
@Override
public void unbind() {
super.unbind();
}
}

View File

@@ -0,0 +1,66 @@
/*
* 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 com.android.server.ambientcontext;
import android.annotation.NonNull;
import android.app.ambientcontext.AmbientContextEvent;
import android.app.ambientcontext.AmbientContextEventRequest;
import android.os.RemoteCallback;
import java.io.PrintWriter;
/**
* Interface for a remote service implementing Ambient Context Detection Service capabilities.
*/
interface RemoteAmbientDetectionService {
/**
* Asks the implementation to start detection.
*
* @param request The request with events to detect, and optional detection options.
* @param packageName The app package that requested the detection
* @param detectionResultCallback callback for detection results
* @param statusCallback callback for service status
*/
void startDetection(
@NonNull AmbientContextEventRequest request, String packageName,
RemoteCallback detectionResultCallback, RemoteCallback statusCallback);
/**
* Asks the implementation to stop detection.
*
* @param packageName stop detection for the given package
*/
void stopDetection(String packageName);
/**
* Asks the implementation to return the event status for the package.
*/
void queryServiceStatus(
@AmbientContextEvent.EventCode int[] eventTypes,
String packageName,
RemoteCallback callback);
/**
* Dumps the RemoteAmbientDetectionService.
*/
void dump(@NonNull String prefix, @NonNull PrintWriter pw);
/**
* Unbinds from the remote service.
*/
void unbind();
}

View File

@@ -0,0 +1,94 @@
/*
* 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 com.android.server.ambientcontext;
import static android.content.Context.BIND_FOREGROUND_SERVICE;
import static android.content.Context.BIND_INCLUDE_CAPABILITIES;
import android.annotation.NonNull;
import android.app.ambientcontext.AmbientContextEvent;
import android.app.ambientcontext.AmbientContextEventRequest;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.os.RemoteCallback;
import android.service.wearable.IWearableSensingService;
import android.service.wearable.WearableSensingService;
import android.util.Slog;
import com.android.internal.infra.ServiceConnector;
import java.io.PrintWriter;
/** Manages the connection to the remote wearable sensing service. */
final class RemoteWearableSensingService
extends ServiceConnector.Impl<IWearableSensingService>
implements RemoteAmbientDetectionService {
private static final String TAG =
RemoteWearableSensingService.class.getSimpleName();
RemoteWearableSensingService(Context context, ComponentName serviceName,
int userId) {
super(context, new Intent(
WearableSensingService.SERVICE_INTERFACE).setComponent(serviceName),
BIND_FOREGROUND_SERVICE | BIND_INCLUDE_CAPABILITIES, userId,
IWearableSensingService.Stub::asInterface);
// Bind right away
connect();
}
@Override
protected long getAutoDisconnectTimeoutMs() {
// Disable automatic unbinding.
return -1;
}
@Override
public void startDetection(
@NonNull AmbientContextEventRequest request, String packageName,
RemoteCallback detectionResultCallback, RemoteCallback statusCallback) {
Slog.i(TAG, "Start detection for " + request.getEventTypes());
post(service -> service.startDetection(request, packageName, detectionResultCallback,
statusCallback));
}
@Override
public void stopDetection(String packageName) {
Slog.i(TAG, "Stop detection for " + packageName);
post(service -> service.stopDetection(packageName));
}
@Override
public void queryServiceStatus(
@AmbientContextEvent.EventCode int[] eventTypes,
String packageName,
RemoteCallback callback) {
Slog.i(TAG, "Query status for " + packageName);
post(service -> service.queryServiceStatus(eventTypes, packageName, callback));
}
@Override
public void dump(@NonNull String prefix, @NonNull PrintWriter pw) {
super.dump(prefix, pw);
}
@Override
public void unbind() {
super.unbind();
}
}

View File

@@ -0,0 +1,112 @@
/*
* 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 com.android.server.ambientcontext;
import android.Manifest;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.UserIdInt;
import android.app.ambientcontext.AmbientContextEvent;
import android.content.ComponentName;
import android.util.Slog;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
/**
* Per-user manager service for {@link AmbientContextEvent}s for the Wearable Sensing.
*/
public class WearableAmbientContextManagerPerUserService extends
AmbientContextManagerPerUserService {
private static final String TAG =
WearableAmbientContextManagerPerUserService.class.getSimpleName();
@Nullable
@VisibleForTesting
RemoteWearableSensingService mRemoteService;
private ComponentName mComponentName;
private final ServiceType mServiceType;
private final String mServiceName;
WearableAmbientContextManagerPerUserService(
@NonNull AmbientContextManagerService master, Object lock,
@UserIdInt int userId, ServiceType serviceType, String serviceName) {
super(master, lock, userId);
this.mServiceType = serviceType;
this.mServiceName = serviceName;
this.mComponentName = ComponentName.unflattenFromString(mServiceName);
Slog.d(TAG, "Created WearableAmbientContextManagerPerUserService"
+ "and service type: " + mServiceType.name() + " and service name: " + serviceName);
}
@GuardedBy("mLock")
@Override
protected void ensureRemoteServiceInitiated() {
if (mRemoteService == null) {
mRemoteService = new RemoteWearableSensingService(
getContext(), mComponentName, getUserId());
}
}
@VisibleForTesting
@Override
ComponentName getComponentName() {
return mComponentName;
}
@Override
protected void setComponentName(ComponentName componentName) {
this.mComponentName = componentName;
}
@Override
protected RemoteAmbientDetectionService getRemoteService() {
return mRemoteService;
}
@Override
protected String getProtectedBindPermission() {
return Manifest.permission.BIND_WEARABLE_SENSING_SERVICE;
}
@Override
public ServiceType getServiceType() {
return mServiceType;
}
@Override
protected int getAmbientContextPackageNameExtraKeyConfig() {
return com.android.internal.R.string.config_wearableAmbientContextPackageNameExtraKey;
}
@Override
protected int getAmbientContextEventArrayExtraKeyConfig() {
return com.android.internal.R.string.config_wearableAmbientContextEventArrayExtraKey;
}
@Override
protected int getConsentComponentConfig() {
return com.android.internal.R.string.config_defaultWearableSensingConsentComponent;
}
@Override
protected void clearRemoteService() {
mRemoteService = null;
}
}

View File

@@ -182,8 +182,12 @@ public abstract class AbstractPerUserSystemService<S extends AbstractPerUserSyst
final String componentName = getComponentNameLocked();
return new ComponentName[] { getServiceComponent(componentName) };
}
final String[] componentNames = mMaster.mServiceNameResolver.getServiceNameList(
mUserId);
if (componentNames == null) {
return null;
}
ComponentName[] serviceComponents = new ComponentName[componentNames.length];
for (int i = 0; i < componentNames.length; i++) {
serviceComponents[i] = getServiceComponent(componentNames[i]);