Merge "Wearable events for AmbientContextManager."
This commit is contained in:
@@ -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";
|
||||
}
|
||||
|
||||
|
||||
@@ -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() {}
|
||||
|
||||
|
||||
@@ -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)}.
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
|
||||
|
||||
@@ -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" />
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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]);
|
||||
|
||||
Reference in New Issue
Block a user