Merge "AmbientContext (aka Ambient PCC) Framework API, with a client API for apps to subscribe for AmbientContextEvents, and a provider API for AiAi to implement and provide the detected events."

This commit is contained in:
Kathy Chen
2022-01-23 01:54:29 +00:00
committed by Android (Google) Code Review
28 changed files with 2295 additions and 1 deletions

View File

@@ -2,6 +2,7 @@
package android {
public static final class Manifest.permission {
field public static final String ACCESS_AMBIENT_CONTEXT_EVENT = "android.permission.ACCESS_AMBIENT_CONTEXT_EVENT";
field public static final String ACCESS_AMBIENT_LIGHT_STATS = "android.permission.ACCESS_AMBIENT_LIGHT_STATS";
field public static final String ACCESS_BROADCAST_RADIO = "android.permission.ACCESS_BROADCAST_RADIO";
field public static final String ACCESS_CACHE_FILESYSTEM = "android.permission.ACCESS_CACHE_FILESYSTEM";
@@ -38,6 +39,7 @@ package android {
field public static final String BACKGROUND_CAMERA = "android.permission.BACKGROUND_CAMERA";
field public static final String BACKUP = "android.permission.BACKUP";
field public static final String BATTERY_PREDICTION = "android.permission.BATTERY_PREDICTION";
field public static final String BIND_AMBIENT_CONTEXT_DETECTION_SERVICE = "android.permission.BIND_AMBIENT_CONTEXT_DETECTION_SERVICE";
field public static final String BIND_ATTENTION_SERVICE = "android.permission.BIND_ATTENTION_SERVICE";
field public static final String BIND_AUGMENTED_AUTOFILL_SERVICE = "android.permission.BIND_AUGMENTED_AUTOFILL_SERVICE";
field public static final String BIND_CALL_DIAGNOSTIC_SERVICE = "android.permission.BIND_CALL_DIAGNOSTIC_SERVICE";
@@ -1213,6 +1215,87 @@ package android.app.admin {
}
package android.app.ambientcontext {
public final class AmbientContextEvent implements android.os.Parcelable {
method public int describeContents();
method public int getConfidenceLevel();
method public int getDensityLevel();
method @NonNull public java.time.Instant getEndTime();
method public int getEventType();
method @NonNull public java.time.Instant getStartTime();
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_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 LEVEL_HIGH = 5; // 0x5
field public static final int LEVEL_LOW = 1; // 0x1
field public static final int LEVEL_MEDIUM = 3; // 0x3
field public static final int LEVEL_MEDIUM_HIGH = 4; // 0x4
field public static final int LEVEL_MEDIUM_LOW = 2; // 0x2
field public static final int LEVEL_UNKNOWN = 0; // 0x0
}
public static final class AmbientContextEvent.Builder {
ctor public AmbientContextEvent.Builder();
method @NonNull public android.app.ambientcontext.AmbientContextEvent build();
method @NonNull public android.app.ambientcontext.AmbientContextEvent.Builder setConfidenceLevel(int);
method @NonNull public android.app.ambientcontext.AmbientContextEvent.Builder setDensityLevel(int);
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);
}
public final class AmbientContextEventRequest implements android.os.Parcelable {
method public int describeContents();
method @NonNull public java.util.Set<java.lang.Integer> getEventTypes();
method @NonNull public android.os.PersistableBundle getOptions();
method public void writeToParcel(@NonNull android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.app.ambientcontext.AmbientContextEventRequest> CREATOR;
}
public static final class AmbientContextEventRequest.Builder {
ctor public AmbientContextEventRequest.Builder();
method @NonNull public android.app.ambientcontext.AmbientContextEventRequest.Builder addEventType(int);
method @NonNull public android.app.ambientcontext.AmbientContextEventRequest build();
method @NonNull public android.app.ambientcontext.AmbientContextEventRequest.Builder setOptions(@NonNull android.os.PersistableBundle);
}
public final class AmbientContextEventResponse implements android.os.Parcelable {
method public int describeContents();
method @Nullable public android.app.PendingIntent getActionPendingIntent();
method @NonNull public java.util.List<android.app.ambientcontext.AmbientContextEvent> getEvents();
method @NonNull public String getPackageName();
method public int getStatusCode();
method public void writeToParcel(@NonNull android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.app.ambientcontext.AmbientContextEventResponse> CREATOR;
field public static final int STATUS_ACCESS_DENIED = 5; // 0x5
field public static final int STATUS_MICROPHONE_DISABLED = 4; // 0x4
field public static final int STATUS_NOT_SUPPORTED = 2; // 0x2
field public static final int STATUS_SERVICE_UNAVAILABLE = 3; // 0x3
field public static final int STATUS_SUCCESS = 1; // 0x1
field public static final int STATUS_UNKNOWN = 0; // 0x0
}
public static final class AmbientContextEventResponse.Builder {
ctor public AmbientContextEventResponse.Builder();
method @NonNull public android.app.ambientcontext.AmbientContextEventResponse.Builder addEvent(@NonNull android.app.ambientcontext.AmbientContextEvent);
method @NonNull public android.app.ambientcontext.AmbientContextEventResponse build();
method @NonNull public android.app.ambientcontext.AmbientContextEventResponse.Builder setActionPendingIntent(@NonNull android.app.PendingIntent);
method @NonNull public android.app.ambientcontext.AmbientContextEventResponse.Builder setPackageName(@NonNull String);
method @NonNull public android.app.ambientcontext.AmbientContextEventResponse.Builder setStatusCode(int);
}
public final class AmbientContextManager {
method @Nullable public static android.app.ambientcontext.AmbientContextEventResponse getResponseFromIntent(@NonNull android.content.Intent);
method @RequiresPermission(android.Manifest.permission.ACCESS_AMBIENT_CONTEXT_EVENT) public void registerObserver(@NonNull android.app.ambientcontext.AmbientContextEventRequest, @NonNull android.app.PendingIntent);
method @RequiresPermission(android.Manifest.permission.ACCESS_AMBIENT_CONTEXT_EVENT) public void unregisterObserver();
field public static final String EXTRA_AMBIENT_CONTEXT_EVENT_RESPONSE = "android.app.ambientcontext.extra.AMBIENT_CONTEXT_EVENT_RESPONSE";
}
}
package android.app.assist {
public class ActivityId {
@@ -2703,6 +2786,7 @@ package android.content {
method @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS) public abstract void sendBroadcastAsUser(@RequiresPermission android.content.Intent, android.os.UserHandle, @Nullable String, @Nullable android.os.Bundle);
method public abstract void sendOrderedBroadcast(@NonNull android.content.Intent, @Nullable String, @Nullable android.os.Bundle, @Nullable android.content.BroadcastReceiver, @Nullable android.os.Handler, int, @Nullable String, @Nullable android.os.Bundle);
method @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS) public void startActivityAsUser(@NonNull @RequiresPermission android.content.Intent, @NonNull android.os.UserHandle);
field public static final String AMBIENT_CONTEXT_SERVICE = "ambient_context";
field public static final String APP_HIBERNATION_SERVICE = "app_hibernation";
field public static final String APP_INTEGRITY_SERVICE = "app_integrity";
field public static final String APP_PREDICTION_SERVICE = "app_prediction";
@@ -9962,6 +10046,7 @@ package android.provider {
method @RequiresPermission(android.Manifest.permission.WRITE_DEVICE_CONFIG) public static boolean setProperty(@NonNull String, @NonNull String, @Nullable String, boolean);
field public static final String NAMESPACE_ACTIVITY_MANAGER = "activity_manager";
field public static final String NAMESPACE_ACTIVITY_MANAGER_NATIVE_BOOT = "activity_manager_native_boot";
field public static final String NAMESPACE_AMBIENT_CONTEXT_MANAGER_SERVICE = "ambient_context_manager_service";
field public static final String NAMESPACE_APPSEARCH = "appsearch";
field public static final String NAMESPACE_APP_COMPAT = "app_compat";
field public static final String NAMESPACE_APP_HIBERNATION = "app_hibernation";
@@ -10534,6 +10619,18 @@ package android.security.keystore.recovery {
}
package android.service.ambientcontext {
public abstract class AmbientContextDetectionService extends android.app.Service {
ctor public AmbientContextDetectionService();
method @Nullable public final android.os.IBinder onBind(@NonNull android.content.Intent);
method public abstract void onStartDetection(@NonNull android.app.ambientcontext.AmbientContextEventRequest, @NonNull String, @NonNull java.util.function.Consumer<android.app.ambientcontext.AmbientContextEventResponse>);
method public abstract void onStopDetection(@NonNull String);
field public static final String SERVICE_INTERFACE = "android.service.ambientcontext.AmbientContextDetectionService";
}
}
package android.service.appprediction {
public abstract class AppPredictionService extends android.app.Service {

View File

@@ -24,6 +24,8 @@ import android.annotation.SystemApi;
import android.app.ContextImpl.ServiceInitializationState;
import android.app.admin.DevicePolicyManager;
import android.app.admin.IDevicePolicyManager;
import android.app.ambientcontext.AmbientContextManager;
import android.app.ambientcontext.IAmbientContextEventObserver;
import android.app.appsearch.AppSearchManagerFrameworkInitializer;
import android.app.blob.BlobStoreManagerFrameworkInitializer;
import android.app.contentsuggestions.ContentSuggestionsManager;
@@ -1523,6 +1525,18 @@ public final class SystemServiceRegistry {
}
});
registerService(Context.AMBIENT_CONTEXT_SERVICE, AmbientContextManager.class,
new CachedServiceFetcher<AmbientContextManager>() {
@Override
public AmbientContextManager createService(ContextImpl ctx)
throws ServiceNotFoundException {
IBinder iBinder = ServiceManager.getServiceOrThrow(
Context.AMBIENT_CONTEXT_SERVICE);
IAmbientContextEventObserver manager =
IAmbientContextEventObserver.Stub.asInterface(iBinder);
return new AmbientContextManager(ctx.getOuterContext(), manager);
}});
sInitializing = true;
try {
// Note: the following functions need to be @SystemApis, once they become mainline

View File

@@ -0,0 +1,19 @@
/*
* Copyright (C) 2021 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package android.app.ambientcontext;
parcelable AmbientContextEvent;

View File

@@ -0,0 +1,492 @@
/*
* Copyright (C) 2022 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package android.app.ambientcontext;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.SystemApi;
import android.os.Parcelable;
import com.android.internal.util.DataClass;
import com.android.internal.util.Parcelling;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.time.Instant;
/**
* Represents a detected ambient event. Each event has a type, start time, end time,
* plus some optional data.
*
* @hide
*/
@SystemApi
@DataClass(
genBuilder = true,
genConstructor = false,
genHiddenConstDefs = true,
genParcelable = true,
genToString = true
)
public final class AmbientContextEvent implements Parcelable {
/**
* The integer indicating an unknown event was detected.
*/
public static final int EVENT_UNKNOWN = 0;
/**
* The integer indicating a cough event was detected.
*/
public static final int EVENT_COUGH = 1;
/**
* The integer indicating a snore event was detected.
*/
public static final int EVENT_SNORE = 2;
/** @hide */
@IntDef(prefix = { "EVENT_" }, value = {
EVENT_UNKNOWN,
EVENT_COUGH,
EVENT_SNORE,
}) public @interface EventCode {}
/** The integer indicating an unknown level. */
public static final int LEVEL_UNKNOWN = 0;
/** The integer indicating a low level. */
public static final int LEVEL_LOW = 1;
/** The integer indicating a medium low level. */
public static final int LEVEL_MEDIUM_LOW = 2;
/** The integer indicating a medium Level. */
public static final int LEVEL_MEDIUM = 3;
/** The integer indicating a medium high level. */
public static final int LEVEL_MEDIUM_HIGH = 4;
/** The integer indicating a high level. */
public static final int LEVEL_HIGH = 5;
/** @hide */
@IntDef(prefix = {"LEVEL_"}, value = {
LEVEL_UNKNOWN,
LEVEL_LOW,
LEVEL_MEDIUM_LOW,
LEVEL_MEDIUM,
LEVEL_MEDIUM_HIGH,
LEVEL_HIGH
}) public @interface LevelValue {}
@EventCode private final int mEventType;
private static int defaultEventType() {
return EVENT_UNKNOWN;
}
/** Event start time */
@DataClass.ParcelWith(Parcelling.BuiltIn.ForInstant.class)
@NonNull private final Instant mStartTime;
@NonNull private static Instant defaultStartTime() {
return Instant.MIN;
}
/** Event end time */
@DataClass.ParcelWith(Parcelling.BuiltIn.ForInstant.class)
@NonNull private final Instant mEndTime;
@NonNull private static Instant defaultEndTime() {
return Instant.MAX;
}
/**
* Confidence level from LEVEL_LOW to LEVEL_HIGH, or LEVEL_NONE if not available.
* Apps can add post-processing filter using this value if needed.
*/
@LevelValue private final int mConfidenceLevel;
private static int defaultConfidenceLevel() {
return LEVEL_UNKNOWN;
}
/**
* Density level from LEVEL_LOW to LEVEL_HIGH, or LEVEL_NONE if not available.
* Apps can add post-processing filter using this value if needed.
*/
@LevelValue private final int mDensityLevel;
private static int defaultDensityLevel() {
return LEVEL_UNKNOWN;
}
// Code below generated by codegen v1.0.23.
//
// DO NOT MODIFY!
// CHECKSTYLE:OFF Generated code
//
// To regenerate run:
// $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/app/ambientcontext/AmbientContextEvent.java
//
// To exclude the generated code from IntelliJ auto-formatting enable (one-time):
// Settings > Editor > Code Style > Formatter Control
//@formatter:off
/** @hide */
@IntDef(prefix = "EVENT_", value = {
EVENT_UNKNOWN,
EVENT_COUGH,
EVENT_SNORE
})
@Retention(RetentionPolicy.SOURCE)
@DataClass.Generated.Member
public @interface Event {}
/** @hide */
@DataClass.Generated.Member
public static String eventToString(@Event int value) {
switch (value) {
case EVENT_UNKNOWN:
return "EVENT_UNKNOWN";
case EVENT_COUGH:
return "EVENT_COUGH";
case EVENT_SNORE:
return "EVENT_SNORE";
default: return Integer.toHexString(value);
}
}
/** @hide */
@IntDef(prefix = "LEVEL_", value = {
LEVEL_UNKNOWN,
LEVEL_LOW,
LEVEL_MEDIUM_LOW,
LEVEL_MEDIUM,
LEVEL_MEDIUM_HIGH,
LEVEL_HIGH
})
@Retention(RetentionPolicy.SOURCE)
@DataClass.Generated.Member
public @interface Level {}
/** @hide */
@DataClass.Generated.Member
public static String levelToString(@Level int value) {
switch (value) {
case LEVEL_UNKNOWN:
return "LEVEL_UNKNOWN";
case LEVEL_LOW:
return "LEVEL_LOW";
case LEVEL_MEDIUM_LOW:
return "LEVEL_MEDIUM_LOW";
case LEVEL_MEDIUM:
return "LEVEL_MEDIUM";
case LEVEL_MEDIUM_HIGH:
return "LEVEL_MEDIUM_HIGH";
case LEVEL_HIGH:
return "LEVEL_HIGH";
default: return Integer.toHexString(value);
}
}
@DataClass.Generated.Member
/* package-private */ AmbientContextEvent(
@EventCode int eventType,
@NonNull Instant startTime,
@NonNull Instant endTime,
@LevelValue int confidenceLevel,
@LevelValue int densityLevel) {
this.mEventType = eventType;
com.android.internal.util.AnnotationValidations.validate(
EventCode.class, null, mEventType);
this.mStartTime = startTime;
com.android.internal.util.AnnotationValidations.validate(
NonNull.class, null, mStartTime);
this.mEndTime = endTime;
com.android.internal.util.AnnotationValidations.validate(
NonNull.class, null, mEndTime);
this.mConfidenceLevel = confidenceLevel;
com.android.internal.util.AnnotationValidations.validate(
LevelValue.class, null, mConfidenceLevel);
this.mDensityLevel = densityLevel;
com.android.internal.util.AnnotationValidations.validate(
LevelValue.class, null, mDensityLevel);
// onConstructed(); // You can define this method to get a callback
}
@DataClass.Generated.Member
public @EventCode int getEventType() {
return mEventType;
}
/**
* Event start time
*/
@DataClass.Generated.Member
public @NonNull Instant getStartTime() {
return mStartTime;
}
/**
* Event end time
*/
@DataClass.Generated.Member
public @NonNull Instant getEndTime() {
return mEndTime;
}
/**
* Confidence level from LEVEL_LOW to LEVEL_HIGH, or LEVEL_NONE if not available.
* Apps can add post-processing filter using this value if needed.
*/
@DataClass.Generated.Member
public @LevelValue int getConfidenceLevel() {
return mConfidenceLevel;
}
/**
* Density level from LEVEL_LOW to LEVEL_HIGH, or LEVEL_NONE if not available.
* Apps can add post-processing filter using this value if needed.
*/
@DataClass.Generated.Member
public @LevelValue int getDensityLevel() {
return mDensityLevel;
}
@Override
@DataClass.Generated.Member
public String toString() {
// You can override field toString logic by defining methods like:
// String fieldNameToString() { ... }
return "AmbientContextEvent { " +
"eventType = " + mEventType + ", " +
"startTime = " + mStartTime + ", " +
"endTime = " + mEndTime + ", " +
"confidenceLevel = " + mConfidenceLevel + ", " +
"densityLevel = " + mDensityLevel +
" }";
}
@DataClass.Generated.Member
static Parcelling<Instant> sParcellingForStartTime =
Parcelling.Cache.get(
Parcelling.BuiltIn.ForInstant.class);
static {
if (sParcellingForStartTime == null) {
sParcellingForStartTime = Parcelling.Cache.put(
new Parcelling.BuiltIn.ForInstant());
}
}
@DataClass.Generated.Member
static Parcelling<Instant> sParcellingForEndTime =
Parcelling.Cache.get(
Parcelling.BuiltIn.ForInstant.class);
static {
if (sParcellingForEndTime == null) {
sParcellingForEndTime = Parcelling.Cache.put(
new Parcelling.BuiltIn.ForInstant());
}
}
@Override
@DataClass.Generated.Member
public void writeToParcel(@NonNull android.os.Parcel dest, int flags) {
// You can override field parcelling by defining methods like:
// void parcelFieldName(Parcel dest, int flags) { ... }
dest.writeInt(mEventType);
sParcellingForStartTime.parcel(mStartTime, dest, flags);
sParcellingForEndTime.parcel(mEndTime, dest, flags);
dest.writeInt(mConfidenceLevel);
dest.writeInt(mDensityLevel);
}
@Override
@DataClass.Generated.Member
public int describeContents() { return 0; }
/** @hide */
@SuppressWarnings({"unchecked", "RedundantCast"})
@DataClass.Generated.Member
/* package-private */ AmbientContextEvent(@NonNull android.os.Parcel in) {
// You can override field unparcelling by defining methods like:
// static FieldType unparcelFieldName(Parcel in) { ... }
int eventType = in.readInt();
Instant startTime = sParcellingForStartTime.unparcel(in);
Instant endTime = sParcellingForEndTime.unparcel(in);
int confidenceLevel = in.readInt();
int densityLevel = in.readInt();
this.mEventType = eventType;
com.android.internal.util.AnnotationValidations.validate(
EventCode.class, null, mEventType);
this.mStartTime = startTime;
com.android.internal.util.AnnotationValidations.validate(
NonNull.class, null, mStartTime);
this.mEndTime = endTime;
com.android.internal.util.AnnotationValidations.validate(
NonNull.class, null, mEndTime);
this.mConfidenceLevel = confidenceLevel;
com.android.internal.util.AnnotationValidations.validate(
LevelValue.class, null, mConfidenceLevel);
this.mDensityLevel = densityLevel;
com.android.internal.util.AnnotationValidations.validate(
LevelValue.class, null, mDensityLevel);
// onConstructed(); // You can define this method to get a callback
}
@DataClass.Generated.Member
public static final @NonNull Parcelable.Creator<AmbientContextEvent> CREATOR
= new Parcelable.Creator<AmbientContextEvent>() {
@Override
public AmbientContextEvent[] newArray(int size) {
return new AmbientContextEvent[size];
}
@Override
public AmbientContextEvent createFromParcel(@NonNull android.os.Parcel in) {
return new AmbientContextEvent(in);
}
};
/**
* A builder for {@link AmbientContextEvent}
*/
@SuppressWarnings("WeakerAccess")
@DataClass.Generated.Member
public static final class Builder {
private @EventCode int mEventType;
private @NonNull Instant mStartTime;
private @NonNull Instant mEndTime;
private @LevelValue int mConfidenceLevel;
private @LevelValue int mDensityLevel;
private long mBuilderFieldsSet = 0L;
public Builder() {
}
@DataClass.Generated.Member
public @NonNull Builder setEventType(@EventCode int value) {
checkNotUsed();
mBuilderFieldsSet |= 0x1;
mEventType = value;
return this;
}
/**
* Event start time
*/
@DataClass.Generated.Member
public @NonNull Builder setStartTime(@NonNull Instant value) {
checkNotUsed();
mBuilderFieldsSet |= 0x2;
mStartTime = value;
return this;
}
/**
* Event end time
*/
@DataClass.Generated.Member
public @NonNull Builder setEndTime(@NonNull Instant value) {
checkNotUsed();
mBuilderFieldsSet |= 0x4;
mEndTime = value;
return this;
}
/**
* Confidence level from LEVEL_LOW to LEVEL_HIGH, or LEVEL_NONE if not available.
* Apps can add post-processing filter using this value if needed.
*/
@DataClass.Generated.Member
public @NonNull Builder setConfidenceLevel(@LevelValue int value) {
checkNotUsed();
mBuilderFieldsSet |= 0x8;
mConfidenceLevel = value;
return this;
}
/**
* Density level from LEVEL_LOW to LEVEL_HIGH, or LEVEL_NONE if not available.
* Apps can add post-processing filter using this value if needed.
*/
@DataClass.Generated.Member
public @NonNull Builder setDensityLevel(@LevelValue int value) {
checkNotUsed();
mBuilderFieldsSet |= 0x10;
mDensityLevel = 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
if ((mBuilderFieldsSet & 0x1) == 0) {
mEventType = defaultEventType();
}
if ((mBuilderFieldsSet & 0x2) == 0) {
mStartTime = defaultStartTime();
}
if ((mBuilderFieldsSet & 0x4) == 0) {
mEndTime = defaultEndTime();
}
if ((mBuilderFieldsSet & 0x8) == 0) {
mConfidenceLevel = defaultConfidenceLevel();
}
if ((mBuilderFieldsSet & 0x10) == 0) {
mDensityLevel = defaultDensityLevel();
}
AmbientContextEvent o = new AmbientContextEvent(
mEventType,
mStartTime,
mEndTime,
mConfidenceLevel,
mDensityLevel);
return o;
}
private void checkNotUsed() {
if ((mBuilderFieldsSet & 0x20) != 0) {
throw new IllegalStateException(
"This Builder should not be reused. Use a new Builder instance instead");
}
}
}
@DataClass.Generated(
time = 1642040319323L,
codegenVersion = "1.0.23",
sourceFile = "frameworks/base/core/java/android/app/ambientcontext/AmbientContextEvent.java",
inputSignatures = "public static final int EVENT_UNKNOWN\npublic static final int EVENT_COUGH\npublic static final int EVENT_SNORE\npublic static final int LEVEL_UNKNOWN\npublic static final int LEVEL_LOW\npublic static final int LEVEL_MEDIUM_LOW\npublic static final int LEVEL_MEDIUM\npublic static final int LEVEL_MEDIUM_HIGH\npublic static final int LEVEL_HIGH\nprivate final @android.app.ambientcontext.AmbientContextEvent.EventCode int mEventType\nprivate final @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInstant.class) @android.annotation.NonNull java.time.Instant mStartTime\nprivate final @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInstant.class) @android.annotation.NonNull java.time.Instant mEndTime\nprivate final @android.app.ambientcontext.AmbientContextEvent.LevelValue int mConfidenceLevel\nprivate final @android.app.ambientcontext.AmbientContextEvent.LevelValue int mDensityLevel\nprivate static int defaultEventType()\nprivate static @android.annotation.NonNull java.time.Instant defaultStartTime()\nprivate static @android.annotation.NonNull java.time.Instant defaultEndTime()\nprivate static int defaultConfidenceLevel()\nprivate static int defaultDensityLevel()\nclass AmbientContextEvent extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genBuilder=true, genConstructor=false, genHiddenConstDefs=true, genParcelable=true, genToString=true)")
@Deprecated
private void __metadata() {}
//@formatter:on
// End of generated code
}

View File

@@ -0,0 +1,19 @@
/*
* Copyright (C) 2022 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package android.app.ambientcontext;
parcelable AmbientContextEventRequest;

View File

@@ -0,0 +1,169 @@
/*
* Copyright (C) 2022 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package android.app.ambientcontext;
import android.annotation.NonNull;
import android.annotation.SystemApi;
import android.os.Parcel;
import android.os.Parcelable;
import android.os.PersistableBundle;
import android.util.ArraySet;
import java.util.HashSet;
import java.util.Set;
/**
* Represents the request for ambient event detection.
*
* @hide
*/
@SystemApi
public final class AmbientContextEventRequest implements Parcelable {
@NonNull private final Set<Integer> mEventTypes;
@NonNull private final PersistableBundle mOptions;
AmbientContextEventRequest(
@NonNull Set<Integer> eventTypes,
@NonNull PersistableBundle options) {
this.mEventTypes = eventTypes;
com.android.internal.util.AnnotationValidations.validate(
NonNull.class, null, mEventTypes);
this.mOptions = options;
com.android.internal.util.AnnotationValidations.validate(
NonNull.class, null, mOptions);
}
/**
* The event types to detect.
*/
public @NonNull Set<Integer> getEventTypes() {
return mEventTypes;
}
/**
* Optional detection options.
*/
public @NonNull PersistableBundle getOptions() {
return mOptions;
}
@Override
public String toString() {
return "AmbientContextEventRequest { " + "eventTypes = " + mEventTypes + ", "
+ "options = " + mOptions + " }";
}
@Override
public void writeToParcel(@NonNull Parcel dest, int flags) {
dest.writeArraySet(new ArraySet<>(mEventTypes));
dest.writeTypedObject(mOptions, flags);
}
@Override
public int describeContents() {
return 0;
}
/** @hide */
@SuppressWarnings({"unchecked", "RedundantCast"})
AmbientContextEventRequest(@NonNull Parcel in) {
Set<Integer> eventTypes = (Set<Integer>) in.readArraySet(Integer.class.getClassLoader());
PersistableBundle options = (PersistableBundle) in.readTypedObject(
PersistableBundle.CREATOR);
this.mEventTypes = eventTypes;
com.android.internal.util.AnnotationValidations.validate(
NonNull.class, null, mEventTypes);
this.mOptions = options;
com.android.internal.util.AnnotationValidations.validate(
NonNull.class, null, mOptions);
}
public static final @NonNull Parcelable.Creator<AmbientContextEventRequest> CREATOR =
new Parcelable.Creator<AmbientContextEventRequest>() {
@Override
public AmbientContextEventRequest[] newArray(int size) {
return new AmbientContextEventRequest[size];
}
@Override
public AmbientContextEventRequest createFromParcel(@NonNull Parcel in) {
return new AmbientContextEventRequest(in);
}
};
/**
* A builder for {@link AmbientContextEventRequest}
*/
@SuppressWarnings("WeakerAccess")
public static final class Builder {
private @NonNull Set<Integer> mEventTypes;
private @NonNull PersistableBundle mOptions;
private long mBuilderFieldsSet = 0L;
public Builder() {
}
/**
* Add an event type to detect.
*/
public @NonNull Builder addEventType(@AmbientContextEvent.EventCode int value) {
checkNotUsed();
if (mEventTypes == null) {
mBuilderFieldsSet |= 0x1;
mEventTypes = new HashSet<>();
}
mEventTypes.add(value);
return this;
}
/**
* Optional detection options.
*/
public @NonNull Builder setOptions(@NonNull PersistableBundle value) {
checkNotUsed();
mBuilderFieldsSet |= 0x2;
mOptions = value;
return this;
}
/** Builds the instance. This builder should not be touched after calling this! */
public @NonNull AmbientContextEventRequest build() {
checkNotUsed();
mBuilderFieldsSet |= 0x4; // Mark builder used
if ((mBuilderFieldsSet & 0x1) == 0) {
mEventTypes = new HashSet<>();
}
if ((mBuilderFieldsSet & 0x2) == 0) {
mOptions = new PersistableBundle();
}
AmbientContextEventRequest o = new AmbientContextEventRequest(
mEventTypes,
mOptions);
return o;
}
private void checkNotUsed() {
if ((mBuilderFieldsSet & 0x4) != 0) {
throw new IllegalStateException(
"This Builder should not be reused. Use a new Builder instance instead");
}
}
}
}

View File

@@ -0,0 +1,19 @@
/*
* Copyright (C) 2021 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package android.app.ambientcontext;
parcelable AmbientContextEventResponse;

View File

@@ -0,0 +1,293 @@
/*
* Copyright (C) 2022 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package android.app.ambientcontext;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SystemApi;
import android.app.PendingIntent;
import android.os.Parcelable;
import com.android.internal.util.AnnotationValidations;
import java.util.ArrayList;
import java.util.List;
/**
* Represents a response from the {@code AmbientContextEvent} service.
*
* @hide
*/
@SystemApi
public final class AmbientContextEventResponse implements Parcelable {
/**
* An unknown status.
*/
public static final int STATUS_UNKNOWN = 0;
/**
* The value of the status code that indicates success.
*/
public static final int STATUS_SUCCESS = 1;
/**
* The value of the status code that indicates one or more of the
* requested events are not supported.
*/
public static final int STATUS_NOT_SUPPORTED = 2;
/**
* The value of the status code that indicates service not available.
*/
public static final int STATUS_SERVICE_UNAVAILABLE = 3;
/**
* The value of the status code that microphone is disabled.
*/
public static final int STATUS_MICROPHONE_DISABLED = 4;
/**
* The value of the status code that the app is not granted access.
*/
public static final int STATUS_ACCESS_DENIED = 5;
/** @hide */
@IntDef(prefix = { "STATUS_" }, value = {
STATUS_UNKNOWN,
STATUS_SUCCESS,
STATUS_NOT_SUPPORTED,
STATUS_SERVICE_UNAVAILABLE,
STATUS_MICROPHONE_DISABLED,
STATUS_ACCESS_DENIED
}) public @interface StatusCode {}
@StatusCode private final int mStatusCode;
@NonNull private final List<AmbientContextEvent> mEvents;
@NonNull private final String mPackageName;
@Nullable private final PendingIntent mActionPendingIntent;
/** @hide */
public static String statusToString(@StatusCode int value) {
switch (value) {
case STATUS_UNKNOWN:
return "STATUS_UNKNOWN";
case STATUS_SUCCESS:
return "STATUS_SUCCESS";
case STATUS_NOT_SUPPORTED:
return "STATUS_NOT_SUPPORTED";
case STATUS_SERVICE_UNAVAILABLE:
return "STATUS_SERVICE_UNAVAILABLE";
case STATUS_MICROPHONE_DISABLED:
return "STATUS_MICROPHONE_DISABLED";
case STATUS_ACCESS_DENIED:
return "STATUS_ACCESS_DENIED";
default: return Integer.toHexString(value);
}
}
AmbientContextEventResponse(
@StatusCode int statusCode,
@NonNull List<AmbientContextEvent> events,
@NonNull String packageName,
@Nullable PendingIntent actionPendingIntent) {
this.mStatusCode = statusCode;
AnnotationValidations.validate(StatusCode.class, null, mStatusCode);
this.mEvents = events;
AnnotationValidations.validate(NonNull.class, null, mEvents);
this.mPackageName = packageName;
AnnotationValidations.validate(NonNull.class, null, mPackageName);
this.mActionPendingIntent = actionPendingIntent;
}
/**
* The status of the response.
*/
public @StatusCode int getStatusCode() {
return mStatusCode;
}
/**
* The detected event.
*/
public @NonNull List<AmbientContextEvent> getEvents() {
return mEvents;
}
/**
* The package to deliver the response to.
*/
public @NonNull String getPackageName() {
return mPackageName;
}
/**
* A {@link PendingIntent} that the client should call to allow further actions by user.
* For example, with {@link STATUS_ACCESS_DENIED}, the PendingIntent can redirect users to the
* grant access activity.
*/
public @Nullable PendingIntent getActionPendingIntent() {
return mActionPendingIntent;
}
@Override
public String toString() {
return "AmbientContextEventResponse { " + "statusCode = " + mStatusCode + ", "
+ "events = " + mEvents + ", " + "packageName = " + mPackageName + ", "
+ "callbackPendingIntent = " + mActionPendingIntent + " }";
}
@Override
public void writeToParcel(@NonNull android.os.Parcel dest, int flags) {
byte flg = 0;
if (mActionPendingIntent != null) flg |= 0x8;
dest.writeByte(flg);
dest.writeInt(mStatusCode);
dest.writeParcelableList(mEvents, flags);
dest.writeString(mPackageName);
if (mActionPendingIntent != null) dest.writeTypedObject(mActionPendingIntent, flags);
}
@Override
public int describeContents() {
return 0;
}
/** @hide */
@SuppressWarnings({"unchecked", "RedundantCast"})
AmbientContextEventResponse(@NonNull android.os.Parcel in) {
byte flg = in.readByte();
int statusCode = in.readInt();
List<AmbientContextEvent> events = new ArrayList<>();
in.readParcelableList(events, AmbientContextEvent.class.getClassLoader(),
AmbientContextEvent.class);
String packageName = in.readString();
PendingIntent callbackPendingIntent = (flg & 0x8) == 0 ? null
: (PendingIntent) in.readTypedObject(PendingIntent.CREATOR);
this.mStatusCode = statusCode;
AnnotationValidations.validate(
StatusCode.class, null, mStatusCode);
this.mEvents = events;
AnnotationValidations.validate(
NonNull.class, null, mEvents);
this.mPackageName = packageName;
AnnotationValidations.validate(
NonNull.class, null, mPackageName);
this.mActionPendingIntent = callbackPendingIntent;
}
public static final @NonNull Parcelable.Creator<AmbientContextEventResponse> CREATOR =
new Parcelable.Creator<AmbientContextEventResponse>() {
@Override
public AmbientContextEventResponse[] newArray(int size) {
return new AmbientContextEventResponse[size];
}
@Override
public AmbientContextEventResponse createFromParcel(@NonNull android.os.Parcel in) {
return new AmbientContextEventResponse(in);
}
};
/**
* A builder for {@link AmbientContextEventResponse}
*/
@SuppressWarnings("WeakerAccess")
public static final class Builder {
private @StatusCode int mStatusCode;
private @NonNull List<AmbientContextEvent> mEvents;
private @NonNull String mPackageName;
private @Nullable PendingIntent mCallbackPendingIntent;
private long mBuilderFieldsSet = 0L;
public Builder() {
}
/**
* The status of the response.
*/
public @NonNull Builder setStatusCode(@StatusCode int value) {
checkNotUsed();
mBuilderFieldsSet |= 0x1;
mStatusCode = value;
return this;
}
/**
* Adds an event to the builder.
*/
public @NonNull Builder addEvent(@NonNull AmbientContextEvent value) {
checkNotUsed();
if (mEvents == null) {
mBuilderFieldsSet |= 0x2;
mEvents = new ArrayList<>();
}
mEvents.add(value);
return this;
}
/**
* The package to deliver the response to.
*/
public @NonNull Builder setPackageName(@NonNull String value) {
checkNotUsed();
mBuilderFieldsSet |= 0x4;
mPackageName = value;
return this;
}
/**
* A {@link PendingIntent} that the client should call to allow further actions by user.
* For example, with {@link STATUS_ACCESS_DENIED}, the PendingIntent can redirect users to
* the grant access activity.
*/
public @NonNull Builder setActionPendingIntent(@NonNull PendingIntent value) {
checkNotUsed();
mBuilderFieldsSet |= 0x8;
mCallbackPendingIntent = value;
return this;
}
/** Builds the instance. This builder should not be touched after calling this! */
public @NonNull AmbientContextEventResponse build() {
checkNotUsed();
mBuilderFieldsSet |= 0x10; // Mark builder used
if ((mBuilderFieldsSet & 0x1) == 0) {
mStatusCode = STATUS_UNKNOWN;
}
if ((mBuilderFieldsSet & 0x2) == 0) {
mEvents = new ArrayList<>();
}
if ((mBuilderFieldsSet & 0x4) == 0) {
mPackageName = "";
}
if ((mBuilderFieldsSet & 0x8) == 0) {
mCallbackPendingIntent = null;
}
AmbientContextEventResponse o = new AmbientContextEventResponse(
mStatusCode,
mEvents,
mPackageName,
mCallbackPendingIntent);
return o;
}
private void checkNotUsed() {
if ((mBuilderFieldsSet & 0x10) != 0) {
throw new IllegalStateException(
"This Builder should not be reused. Use a new Builder instance instead");
}
}
}
}

View File

@@ -0,0 +1,147 @@
/*
* Copyright (C) 2022 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package android.app.ambientcontext;
import android.Manifest;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.RequiresPermission;
import android.annotation.SystemApi;
import android.annotation.SystemService;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.os.RemoteException;
import com.android.internal.util.Preconditions;
/**
* Allows granted apps to register for particular pre-defined {@link AmbientContextEvent}s.
* After successful registration, the app receives a callback on the provided {@link PendingIntent}
* when the requested event is detected.
* <p />
*
* Example:
*
* <pre><code>
* // Create request
* AmbientContextEventRequest request = new AmbientContextEventRequest.Builder()
* .addEventType(AmbientContextEvent.EVENT_COUGH)
* .addEventTYpe(AmbientContextEvent.EVENT_SNORE)
* .build();
* // Create PendingIntent
* Intent intent = new Intent(actionString, null, context, MyBroadcastReceiver.class)
* .addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
* PendingIntent pendingIntent = PendingIntents.getBroadcastMutable(context, 0, intent, 0);
* // Register for events
* AmbientContextManager ambientContextManager =
* context.getSystemService(AmbientContextManager.class);
* ambientContextManager.registerObserver(request, pendingIntent);
*
* // Handle the callback intent in your receiver
* {@literal @}Override
* protected void onReceive(Context context, Intent intent) {
* AmbientContextEventResponse response =
* AmbientContextManager.getResponseFromIntent(intent);
* if (response != null) {
* if (response.getStatusCode() == AmbientContextEventResponse.STATUS_SUCCESS) {
* // Do something useful with response.getEvent()
* } else if (response.getStatusCode() == AmbientContextEventResponse.STATUS_ACCESS_DENIED) {
* // Redirect users to grant access
* PendingIntent callbackPendingIntent = response.getCallbackPendingIntent();
* if (callbackPendingIntent != null) {
* callbackPendingIntent.send();
* }
* } else ...
* }
* }
* </code></pre>
*
* @hide
*/
@SystemApi
@SystemService(Context.AMBIENT_CONTEXT_SERVICE)
public final class AmbientContextManager {
/**
* The key of an Intent extra indicating the response.
*/
public static final String EXTRA_AMBIENT_CONTEXT_EVENT_RESPONSE =
"android.app.ambientcontext.extra.AMBIENT_CONTEXT_EVENT_RESPONSE";
/**
* Allows clients to retrieve the response from the intent.
* @param intent received from the PendingIntent callback
*
* @return the AmbientContextEventResponse, or null if not present
*/
@Nullable
public static AmbientContextEventResponse getResponseFromIntent(
@NonNull Intent intent) {
if (intent.hasExtra(AmbientContextManager.EXTRA_AMBIENT_CONTEXT_EVENT_RESPONSE)) {
return intent.getParcelableExtra(EXTRA_AMBIENT_CONTEXT_EVENT_RESPONSE);
} else {
return null;
}
}
private final Context mContext;
private final IAmbientContextEventObserver mService;
/**
* {@hide}
*/
public AmbientContextManager(Context context, IAmbientContextEventObserver service) {
mContext = context;
mService = service;
}
/**
* Allows app to register as a {@link AmbientContextEvent} observer. The
* observer receives a callback on the provided {@link PendingIntent} when the requested
* event is detected. Registering another observer from the same package that has already been
* registered will override the previous observer.
*
* @param request The request with events to observe.
* @param pendingIntent A mutable {@link PendingIntent} that will be dispatched when any
* requested event is detected.
*/
@RequiresPermission(Manifest.permission.ACCESS_AMBIENT_CONTEXT_EVENT)
public void registerObserver(
@NonNull AmbientContextEventRequest request,
@NonNull PendingIntent pendingIntent) {
Preconditions.checkArgument(!pendingIntent.isImmutable());
try {
mService.registerObserver(request, pendingIntent);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
/**
* Unregisters the requesting app as an {@code AmbientContextEvent} observer. Unregistering an
* observer that was already unregistered or never registered will have no effect.
*/
@RequiresPermission(Manifest.permission.ACCESS_AMBIENT_CONTEXT_EVENT)
public void unregisterObserver() {
try {
mService.unregisterObserver(mContext.getOpPackageName());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
}

View File

@@ -0,0 +1,30 @@
/*
* Copyright (C) 2021 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package android.app.ambientcontext;
import android.app.PendingIntent;
import android.app.ambientcontext.AmbientContextEventRequest;
/**
* Interface for an AmbientContextEventManager that provides access to AmbientContextEvents.
*
* @hide
*/
oneway interface IAmbientContextEventObserver {
void registerObserver(in AmbientContextEventRequest request, in PendingIntent pendingIntent);
void unregisterObserver(in String callingPackage);
}

View File

@@ -0,0 +1,3 @@
enxun@google.com
kxchen@google.com
tgadh@google.com

View File

@@ -41,6 +41,7 @@ import android.app.GameManager;
import android.app.IApplicationThread;
import android.app.IServiceConnection;
import android.app.VrManager;
import android.app.ambientcontext.AmbientContextManager;
import android.app.people.PeopleManager;
import android.app.time.TimeManager;
import android.compat.annotation.UnsupportedAppUsage;
@@ -5930,6 +5931,17 @@ public abstract class Context {
@SystemApi
public static final String NEARBY_SERVICE = "nearby";
/**
* Use with {@link #getSystemService(String)} to retrieve a
* {@link android.app.ambientcontext.AmbientContextManager}.
*
* @see #getSystemService(String)
* @see AmbientContextManager
* @hide
*/
@SystemApi
public static final String AMBIENT_CONTEXT_SERVICE = "ambient_context";
/**
* Determine whether the given permission is allowed for a particular
* process and user ID running in the system.

View File

@@ -694,6 +694,15 @@ public final class DeviceConfig {
@SystemApi
public static final String NAMESPACE_UWB = "uwb";
/**
* Namespace for AmbientContextEventManagerService related features.
*
* @hide
*/
@SystemApi
public static final String NAMESPACE_AMBIENT_CONTEXT_MANAGER_SERVICE =
"ambient_context_manager_service";
private static final Object sLock = new Object();
@GuardedBy("sLock")
private static ArrayMap<OnPropertiesChangedListener, Pair<String, Executor>> sListeners =

View File

@@ -0,0 +1,137 @@
/*
* Copyright (C) 2022 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package android.service.ambientcontext;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SystemApi;
import android.app.Service;
import android.app.ambientcontext.AmbientContextEvent;
import android.app.ambientcontext.AmbientContextEventRequest;
import android.app.ambientcontext.AmbientContextEventResponse;
import android.content.Intent;
import android.os.Bundle;
import android.os.IBinder;
import android.os.RemoteCallback;
import android.util.Slog;
import java.util.Objects;
import java.util.function.Consumer;
/**
* Abstract base class for {@link AmbientContextEvent} detection service.
*
* <p> A service that provides requested ambient context events to the system.
* The system's default AmbientContextDetectionService implementation is configured in
* {@code config_defaultAmbientContextDetectionService}. If this config has no value, a stub is
* returned.
*
* See: {@code AmbientContextManagerService}.
*
* <pre>
* {@literal
* <service android:name=".YourAmbientContextDetectionService"
* android:permission="android.permission.BIND_AMBIENT_CONTEXT_DETECTION_SERVICE">
* </service>}
* </pre>
*
* @hide
*/
@SystemApi
public abstract class AmbientContextDetectionService extends Service {
private static final String TAG = AmbientContextDetectionService.class.getSimpleName();
/**
* The {@link Intent} that must be declared as handled by the service. To be supported, the
* service must also require the
* {@link android.Manifest.permission#BIND_AMBIENT_CONTEXT_DETECTION_SERVICE}
* permission so that other applications can not abuse it.
*/
public static final String SERVICE_INTERFACE =
"android.service.ambientcontext.AmbientContextDetectionService";
/**
* The key for the bundle the parameter of {@code RemoteCallback#sendResult}. Implementation
* should set bundle result with this key.
*
* @hide
*/
public static final String RESPONSE_BUNDLE_KEY =
"android.service.ambientcontext.EventResponseKey";
@Nullable
@Override
public final IBinder onBind(@NonNull Intent intent) {
if (SERVICE_INTERFACE.equals(intent.getAction())) {
return new IAmbientContextDetectionService.Stub() {
/** {@inheritDoc} */
@Override
public void startDetection(
@NonNull AmbientContextEventRequest request, String packageName,
RemoteCallback callback) {
Objects.requireNonNull(request);
Objects.requireNonNull(callback);
Consumer<AmbientContextEventResponse> consumer =
response -> {
Bundle bundle = new Bundle();
bundle.putParcelable(
AmbientContextDetectionService.RESPONSE_BUNDLE_KEY,
response);
callback.sendResult(bundle);
};
AmbientContextDetectionService.this.onStartDetection(
request, packageName, consumer);
Slog.d(TAG, "startDetection " + request);
}
/** {@inheritDoc} */
@Override
public void stopDetection(String packageName) {
Objects.requireNonNull(packageName);
AmbientContextDetectionService.this.onStopDetection(packageName);
}
};
}
return null;
}
/**
* Starts detection and provides detected events to the consumer. The ongoing detection will
* keep running, until onStopDetection is called. If there were previously requested
* detection from the same package, the previous request will be replaced with the new request.
* The implementation should keep track of whether the user consented each requested
* AmbientContextEvent for the app. If not consented, the response should set status
* STATUS_ACCESS_DENIED and include an action PendingIntent for the app to redirect the user
* to the consent screen.
*
* @param request The request with events to detect, optional detection window and other
* options.
* @param packageName the requesting app's package name
* @param consumer the consumer for the detected event
*/
public abstract void onStartDetection(
@NonNull AmbientContextEventRequest request,
@NonNull String packageName,
@NonNull Consumer<AmbientContextEventResponse> consumer);
/**
* 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);
}

View File

@@ -0,0 +1,31 @@
/*
* Copyright (C) 2021 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package android.service.ambientcontext;
import android.app.ambientcontext.AmbientContextEventRequest;
import android.os.RemoteCallback;
/**
* Interface for a concrete implementation to provide AmbientContextEvents to the framework.
*
* @hide
*/
oneway interface IAmbientContextDetectionService {
void startDetection(in AmbientContextEventRequest request, in String packageName,
in RemoteCallback callback);
void stopDetection(in String packageName);
}

View File

@@ -0,0 +1,3 @@
enxun@google.com
kxchen@google.com
tgadh@google.com

View File

@@ -6166,6 +6166,19 @@
<permission android:name="android.permission.MANAGE_SAFETY_CENTER"
android:protectionLevel="internal|installer|role" />
<!-- @SystemApi Allows an application to access the AmbientContextEvent service.
@hide
-->
<permission android:name="android.permission.ACCESS_AMBIENT_CONTEXT_EVENT"
android:protectionLevel="internal|role"/>
<!-- @SystemApi Required by a AmbientContextEventDetectionService
to ensure that only the service with this permission can bind to it.
@hide
-->
<permission android:name="android.permission.BIND_AMBIENT_CONTEXT_DETECTION_SERVICE"
android:protectionLevel="signature" />
<!-- Attribution for Geofencing service. -->
<attribution android:tag="GeofencingService" android:label="@string/geofencing_service"/>
<!-- Attribution for Country Detector. -->

View File

@@ -4077,6 +4077,12 @@
-->
<string name="config_defaultRotationResolverService" translatable="false"></string>
<!-- The component name for the default system AmbientContextEvent detection service.
This service must be trusted, as it can be activated without explicit consent of the user.
See android.service.ambientcontext.AmbientContextDetectionService.
-->
<string name="config_defaultAmbientContextDetectionService" translatable="false"></string>
<!-- The component name for the system-wide captions service.
This service must be trusted, as it controls part of the UI of the volume bar.
Example: "com.android.captions/.SystemCaptionsService"

View File

@@ -3652,6 +3652,7 @@
<java-symbol type="string" name="config_defaultRotationResolverService" />
<java-symbol type="string" name="config_defaultSystemCaptionsService" />
<java-symbol type="string" name="config_defaultSystemCaptionsManagerService" />
<java-symbol type="string" name="config_defaultAmbientContextDetectionService" />
<java-symbol type="string" name="config_retailDemoPackage" />
<java-symbol type="string" name="config_retailDemoPackageSignature" />

View File

@@ -105,9 +105,10 @@ public abstract class PackageManagerInternal implements PackageSettingsSnapshotP
public static final int PACKAGE_COMPANION = 15;
public static final int PACKAGE_RETAIL_DEMO = 16;
public static final int PACKAGE_RECENTS = 17;
public static final int PACKAGE_AMBIENT_CONTEXT_DETECTION = 18;
// Integer value of the last known package ID. Increases as new ID is added to KnownPackage.
// Please note the numbers should be continuous.
public static final int LAST_KNOWN_PACKAGE = PACKAGE_RECENTS;
public static final int LAST_KNOWN_PACKAGE = PACKAGE_AMBIENT_CONTEXT_DETECTION;
@LongDef(flag = true, prefix = "RESOLVE_", value = {
RESOLVE_NON_BROWSER_ONLY,

View File

@@ -0,0 +1,294 @@
/*
* Copyright (C) 2021 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.server.ambientcontext;
import android.Manifest;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.UserIdInt;
import android.app.AppGlobals;
import android.app.BroadcastOptions;
import android.app.PendingIntent;
import android.app.ambientcontext.AmbientContextEvent;
import android.app.ambientcontext.AmbientContextEventRequest;
import android.app.ambientcontext.AmbientContextEventResponse;
import android.app.ambientcontext.AmbientContextManager;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.pm.ServiceInfo;
import android.os.Binder;
import android.os.RemoteCallback;
import android.os.RemoteException;
import android.service.ambientcontext.AmbientContextDetectionService;
import android.util.IndentingPrintWriter;
import android.util.Slog;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.server.infra.AbstractPerUserSystemService;
import java.io.PrintWriter;
import java.util.HashSet;
import java.util.Set;
/**
* Per-user manager service for {@link AmbientContextEvent}s.
*/
final class AmbientContextManagerPerUserService extends
AbstractPerUserSystemService<AmbientContextManagerPerUserService,
AmbientContextManagerService> {
private static final String TAG = AmbientContextManagerPerUserService.class.getSimpleName();
@Nullable
@VisibleForTesting
RemoteAmbientContextDetectionService mRemoteService;
private ComponentName mComponentName;
private Context mContext;
private Set<PendingIntent> mExistingPendingIntents;
AmbientContextManagerPerUserService(
@NonNull AmbientContextManagerService master, Object lock, @UserIdInt int userId) {
super(master, lock, userId);
mContext = master.getContext();
mExistingPendingIntents = new HashSet<>();
}
void destroyLocked() {
if (isVerbose()) {
Slog.v(TAG, "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());
}
}
/**
* get the currently bound component name.
*/
@VisibleForTesting
ComponentName getComponentName() {
return mComponentName;
}
/**
* Resolves and sets up the service if it had not been done yet. Returns true if the service
* is available.
*/
@GuardedBy("mLock")
@VisibleForTesting
boolean setUpServiceIfNeeded() {
if (mComponentName == null) {
mComponentName = updateServiceInfoLocked();
}
return mComponentName != 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, " "));
}
}
/**
* 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.
*/
public void onRegisterObserver(AmbientContextEventRequest request,
PendingIntent pendingIntent) {
synchronized (mLock) {
if (!setUpServiceIfNeeded()) {
Slog.w(TAG, "Service is not available at this moment.");
sendStatusUpdateIntent(
pendingIntent, AmbientContextEventResponse.STATUS_SERVICE_UNAVAILABLE);
return;
}
// Remove any existing intent and unregister for this package before adding a new one.
String callingPackage = pendingIntent.getCreatorPackage();
PendingIntent duplicatePendingIntent = findExistingRequestByPackage(callingPackage);
if (duplicatePendingIntent != null) {
Slog.d(TAG, "Unregister duplicate request from " + callingPackage);
onUnregisterObserver(callingPackage);
mExistingPendingIntents.remove(duplicatePendingIntent);
}
// Register new package and add request to mExistingRequests
startDetection(request, callingPackage, createRemoteCallback());
mExistingPendingIntents.add(pendingIntent);
}
}
@VisibleForTesting
void startDetection(AmbientContextEventRequest request, String callingPackage,
RemoteCallback callback) {
Slog.d(TAG, "Requested detection of " + request.getEventTypes());
synchronized (mLock) {
ensureRemoteServiceInitiated();
mRemoteService.startDetection(request, callingPackage, callback);
}
}
/**
* Sends an intent with a status code and empty events.
*/
void sendStatusUpdateIntent(PendingIntent pendingIntent, int statusCode) {
AmbientContextEventResponse response = new AmbientContextEventResponse.Builder()
.setStatusCode(statusCode)
.build();
sendResponseIntent(pendingIntent, response);
}
/**
* Unregisters the client from all previously registered events by removing from the
* mExistingRequests map, and unregister events from the service if those events are not
* requested by other apps.
*/
public void onUnregisterObserver(String callingPackage) {
synchronized (mLock) {
PendingIntent pendingIntent = findExistingRequestByPackage(callingPackage);
if (pendingIntent == null) {
Slog.d(TAG, "No registration found for " + callingPackage);
return;
}
// Remove from existing requests
mExistingPendingIntents.remove(pendingIntent);
stopDetection(pendingIntent.getCreatorPackage());
}
}
@VisibleForTesting
void stopDetection(String packageName) {
Slog.d(TAG, "Stop detection for " + packageName);
synchronized (mLock) {
ensureRemoteServiceInitiated();
mRemoteService.stopDetection(packageName);
}
}
@Nullable
private PendingIntent findExistingRequestByPackage(String callingPackage) {
for (PendingIntent pendingIntent : mExistingPendingIntents) {
if (pendingIntent.getCreatorPackage().equals(callingPackage)) {
return pendingIntent;
}
}
return null;
}
/**
* Sends out the Intent to the client after the event is detected.
*
* @param pendingIntent Client's PendingIntent for callback
* @param response Response with status code and detection result
*/
private void sendResponseIntent(PendingIntent pendingIntent,
AmbientContextEventResponse response) {
Intent intent = new Intent();
intent.putExtra(AmbientContextManager.EXTRA_AMBIENT_CONTEXT_EVENT_RESPONSE, response);
// Explicitly disallow the receiver from starting activities, to prevent apps from utilizing
// the PendingIntent as a backdoor to do this.
BroadcastOptions options = BroadcastOptions.makeBasic();
options.setPendingIntentBackgroundActivityLaunchAllowed(false);
try {
pendingIntent.send(getContext(), 0, intent, null, null, null,
options.toBundle());
Slog.i(TAG, "Sending PendingIntent to " + pendingIntent.getCreatorPackage() + ": "
+ response);
} catch (PendingIntent.CanceledException e) {
Slog.w(TAG, "Couldn't deliver pendingIntent:" + pendingIntent);
}
}
@NonNull
private RemoteCallback createRemoteCallback() {
return new RemoteCallback(result -> {
AmbientContextEventResponse response = (AmbientContextEventResponse) result.get(
AmbientContextDetectionService.RESPONSE_BUNDLE_KEY);
final long token = Binder.clearCallingIdentity();
try {
Set<PendingIntent> pendingIntentForFailedRequests = new HashSet<>();
for (PendingIntent pendingIntent : mExistingPendingIntents) {
// Send PendingIntent if a requesting package matches the response packages.
if (response.getPackageName().equals(pendingIntent.getCreatorPackage())) {
sendResponseIntent(pendingIntent, response);
int statusCode = response.getStatusCode();
if (statusCode != AmbientContextEventResponse.STATUS_SUCCESS) {
pendingIntentForFailedRequests.add(pendingIntent);
}
Slog.i(TAG, "Got response of " + response.getEvents() + " for "
+ pendingIntent.getCreatorPackage() + ". Status: " + statusCode);
}
}
// Removes the failed requests from the existing requests.
for (PendingIntent pendingIntent : pendingIntentForFailedRequests) {
mExistingPendingIntents.remove(pendingIntent);
}
} finally {
Binder.restoreCallingIdentity(token);
}
});
}
}

View File

@@ -0,0 +1,220 @@
/*
* Copyright (C) 2021 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.server.ambientcontext;
import static android.provider.DeviceConfig.NAMESPACE_AMBIENT_CONTEXT_MANAGER_SERVICE;
import android.Manifest;
import android.annotation.NonNull;
import android.annotation.UserIdInt;
import android.app.PendingIntent;
import android.app.ambientcontext.AmbientContextEvent;
import android.app.ambientcontext.AmbientContextEventRequest;
import android.app.ambientcontext.AmbientContextEventResponse;
import android.app.ambientcontext.IAmbientContextEventObserver;
import android.content.ComponentName;
import android.content.Context;
import android.content.pm.PackageManagerInternal;
import android.os.RemoteCallback;
import android.os.ResultReceiver;
import android.os.ShellCallback;
import android.os.UserHandle;
import android.provider.DeviceConfig;
import android.util.Slog;
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.infra.AbstractMasterSystemService;
import com.android.server.infra.FrameworkResourcesServiceNameResolver;
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.Objects;
import java.util.Set;
/**
* System service for managing {@link AmbientContextEvent}s.
*/
public class AmbientContextManagerService extends
AbstractMasterSystemService<AmbientContextManagerService,
AmbientContextManagerPerUserService> {
private static final String TAG = AmbientContextManagerService.class.getSimpleName();
private static final String KEY_SERVICE_ENABLED = "service_enabled";
/** Default value in absence of {@link DeviceConfig} override. */
private static final boolean DEFAULT_SERVICE_ENABLED = true;
private final Context mContext;
boolean mIsServiceEnabled;
public AmbientContextManagerService(Context context) {
super(context,
new FrameworkResourcesServiceNameResolver(
context,
R.string.config_defaultAmbientContextDetectionService),
/*disallowProperty=*/null,
PACKAGE_UPDATE_POLICY_REFRESH_EAGER
| /*To avoid high latency*/ PACKAGE_RESTART_POLICY_REFRESH_EAGER);
mContext = context;
}
@Override
public void onStart() {
publishBinderService(Context.AMBIENT_CONTEXT_SERVICE, new AmbientContextEventObserver());
}
@Override
public void onBootPhase(int phase) {
if (phase == SystemService.PHASE_SYSTEM_SERVICES_READY) {
DeviceConfig.addOnPropertiesChangedListener(
NAMESPACE_AMBIENT_CONTEXT_MANAGER_SERVICE,
getContext().getMainExecutor(),
(properties) -> onDeviceConfigChange(properties.getKeyset()));
mIsServiceEnabled = DeviceConfig.getBoolean(
NAMESPACE_AMBIENT_CONTEXT_MANAGER_SERVICE,
KEY_SERVICE_ENABLED, DEFAULT_SERVICE_ENABLED);
}
}
private void onDeviceConfigChange(@NonNull Set<String> keys) {
if (keys.contains(KEY_SERVICE_ENABLED)) {
mIsServiceEnabled = DeviceConfig.getBoolean(
NAMESPACE_AMBIENT_CONTEXT_MANAGER_SERVICE,
KEY_SERVICE_ENABLED, DEFAULT_SERVICE_ENABLED);
}
}
@Override
protected AmbientContextManagerPerUserService newServiceLocked(int resolvedUserId,
boolean disabled) {
return new AmbientContextManagerPerUserService(this, mLock, resolvedUserId);
}
@Override
protected void onServiceRemoved(
AmbientContextManagerPerUserService service, @UserIdInt int userId) {
service.destroyLocked();
}
/** Returns {@code true} if the detection service is configured on this device. */
public static boolean isDetectionServiceConfigured() {
final PackageManagerInternal pmi = LocalServices.getService(PackageManagerInternal.class);
final String[] packageNames = pmi.getKnownPackageNames(
PackageManagerInternal.PACKAGE_AMBIENT_CONTEXT_DETECTION, UserHandle.USER_SYSTEM);
boolean isServiceConfigured = (packageNames.length != 0);
Slog.i(TAG, "Detection service configured: " + isServiceConfigured);
return isServiceConfigured;
}
/**
* 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.
*/
void startAmbientContextEvent(@UserIdInt int userId, AmbientContextEventRequest request,
String packageName, RemoteCallback callback) {
mContext.enforceCallingOrSelfPermission(
Manifest.permission.ACCESS_AMBIENT_CONTEXT_EVENT, TAG);
synchronized (mLock) {
final AmbientContextManagerPerUserService service = getServiceForUserLocked(userId);
if (service != null) {
service.startDetection(request, packageName, callback);
} else {
Slog.i(TAG, "service not available for user_id: " + userId);
}
}
}
/**
* 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.
*/
void stopAmbientContextEvent(@UserIdInt int userId, String packageName) {
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);
}
}
}
/**
* Returns the AmbientContextManagerPerUserService component for this user.
*/
public ComponentName getComponentName(@UserIdInt int userId) {
synchronized (mLock) {
final AmbientContextManagerPerUserService service = getServiceForUserLocked(userId);
if (service != null) {
return service.getComponentName();
}
}
return null;
}
private final class AmbientContextEventObserver extends IAmbientContextEventObserver.Stub {
final AmbientContextManagerPerUserService mService = getServiceForUserLocked(
UserHandle.getCallingUserId());
@Override
public void registerObserver(
AmbientContextEventRequest request, PendingIntent pendingIntent) {
Objects.requireNonNull(request);
Objects.requireNonNull(pendingIntent);
mContext.enforceCallingOrSelfPermission(
Manifest.permission.ACCESS_AMBIENT_CONTEXT_EVENT, TAG);
if (!mIsServiceEnabled) {
Slog.w(TAG, "Service not available.");
mService.sendStatusUpdateIntent(pendingIntent,
AmbientContextEventResponse.STATUS_SERVICE_UNAVAILABLE);
return;
}
mService.onRegisterObserver(request, pendingIntent);
}
@Override
public void unregisterObserver(String callingPackage) {
mContext.enforceCallingOrSelfPermission(
Manifest.permission.ACCESS_AMBIENT_CONTEXT_EVENT, TAG);
mService.onUnregisterObserver(callingPackage);
}
@Override
protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) {
return;
}
synchronized (mLock) {
dumpLocked("", pw);
}
}
@Override
public void onShellCommand(FileDescriptor in, FileDescriptor out, FileDescriptor err,
String[] args, ShellCallback callback, ResultReceiver resultReceiver) {
new AmbientContextShellCommand(AmbientContextManagerService.this).exec(
this, in, out, err, args, callback, resultReceiver);
}
}
}

View File

@@ -0,0 +1,164 @@
/*
* Copyright (C) 2021 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.server.ambientcontext;
import static java.lang.System.out;
import android.annotation.NonNull;
import android.app.ambientcontext.AmbientContextEvent;
import android.app.ambientcontext.AmbientContextEventRequest;
import android.app.ambientcontext.AmbientContextEventResponse;
import android.content.ComponentName;
import android.os.Binder;
import android.os.RemoteCallback;
import android.os.ShellCommand;
import android.service.ambientcontext.AmbientContextDetectionService;
import java.io.PrintWriter;
/**
* Shell command for {@link AmbientContextManagerService}.
*/
final class AmbientContextShellCommand extends ShellCommand {
@NonNull
private final AmbientContextManagerService mService;
AmbientContextShellCommand(@NonNull AmbientContextManagerService service) {
mService = service;
}
/** Callbacks for AmbientContextEventService results used internally for testing. */
static class TestableCallbackInternal {
private AmbientContextEventResponse mLastResponse;
public AmbientContextEventResponse getLastResponse() {
return mLastResponse;
}
@NonNull
private RemoteCallback createRemoteCallback() {
return new RemoteCallback(result -> {
AmbientContextEventResponse response =
(AmbientContextEventResponse) result.get(
AmbientContextDetectionService.RESPONSE_BUNDLE_KEY);
final long token = Binder.clearCallingIdentity();
try {
mLastResponse = response;
out.println("Response available: " + response);
} finally {
Binder.restoreCallingIdentity(token);
}
});
}
}
static final TestableCallbackInternal sTestableCallbackInternal =
new TestableCallbackInternal();
@Override
public int onCommand(String cmd) {
if (cmd == null) {
return handleDefaultCommands(cmd);
}
switch (cmd) {
case "start-detection":
return runStartDetection();
case "stop-detection":
return runStopDetection();
case "get-last-status-code":
return getLastStatusCode();
case "get-bound-package":
return getBoundPackageName();
case "set-temporary-service":
return setTemporaryService();
default:
return handleDefaultCommands(cmd);
}
}
private int runStartDetection() {
final int userId = Integer.parseInt(getNextArgRequired());
final String packageName = getNextArgRequired();
AmbientContextEventRequest request = new AmbientContextEventRequest.Builder()
.addEventType(AmbientContextEvent.EVENT_COUGH)
.addEventType(AmbientContextEvent.EVENT_SNORE)
.build();
mService.startAmbientContextEvent(userId, request, packageName,
sTestableCallbackInternal.createRemoteCallback());
return 0;
}
private int runStopDetection() {
final int userId = Integer.parseInt(getNextArgRequired());
final String packageName = getNextArgRequired();
mService.stopAmbientContextEvent(userId, packageName);
return 0;
}
private int getLastStatusCode() {
AmbientContextEventResponse lastResponse = sTestableCallbackInternal.getLastResponse();
if (lastResponse == null) {
return -1;
}
return lastResponse.getStatusCode();
}
@Override
public void onHelp() {
PrintWriter pw = getOutPrintWriter();
pw.println("AmbientContextEvent commands: ");
pw.println(" help");
pw.println(" Print this help text.");
pw.println();
pw.println(" start-detection USER_ID PACKAGE_NAME: Starts AmbientContextEvent detection.");
pw.println(" stop-detection USER_ID: Stops AmbientContextEvent detection.");
pw.println(" get-last-status-code: Prints the latest request status code.");
pw.println(" get-bound-package USER_ID:"
+ " Print the bound package that implements the service.");
pw.println(" set-temporary-service USER_ID [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 out = getOutPrintWriter();
final int userId = Integer.parseInt(getNextArgRequired());
final ComponentName componentName = mService.getComponentName(userId);
out.println(componentName == null ? "" : componentName.getPackageName());
return 0;
}
private int setTemporaryService() {
final PrintWriter out = getOutPrintWriter();
final int userId = Integer.parseInt(getNextArgRequired());
final String serviceName = getNextArg();
if (serviceName == null) {
mService.resetTemporaryService(userId);
out.println("AmbientContextDetectionService temporary reset. ");
return 0;
}
final int duration = Integer.parseInt(getNextArgRequired());
mService.setTemporaryService(userId, serviceName, duration);
out.println("AmbientContextDetectionService temporarily set to " + serviceName
+ " for " + duration + "ms");
return 0;
}
}

View File

@@ -0,0 +1,3 @@
enxun@google.com
kxchen@google.com
tgadh@google.com

View File

@@ -0,0 +1,74 @@
/*
* Copyright (C) 2021 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package 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.AmbientContextEventRequest;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.os.RemoteCallback;
import android.service.ambientcontext.AmbientContextDetectionService;
import android.service.ambientcontext.IAmbientContextDetectionService;
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();
RemoteAmbientContextDetectionService(Context context, ComponentName serviceName,
int userId) {
super(context, new Intent(
AmbientContextDetectionService.SERVICE_INTERFACE).setComponent(serviceName),
BIND_FOREGROUND_SERVICE | BIND_INCLUDE_CAPABILITIES, userId,
IAmbientContextDetectionService.Stub::asInterface);
// Bind right away
connect();
}
/**
* 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 callback callback for detection results
*/
public void startDetection(
@NonNull AmbientContextEventRequest request, String packageName,
RemoteCallback callback) {
Slog.i(TAG, "Start detection for " + request.getEventTypes());
post(service -> service.startDetection(request, packageName, callback));
}
/**
* Asks the implementation to stop detection.
*
* @param packageName stop detection for the given package
*/
public void stopDetection(String packageName) {
Slog.i(TAG, "Stop detection for " + packageName);
post(service -> service.stopDetection(packageName));
}
}

View File

@@ -950,6 +950,7 @@ public class PackageManagerService extends IPackageManager.Stub
final @Nullable String mRetailDemoPackage;
final @Nullable String mOverlayConfigSignaturePackage;
final @Nullable String mRecentsPackage;
final @Nullable String mAmbientContextDetectionPackage;
@GuardedBy("mLock")
private final PackageUsage mPackageUsage = new PackageUsage();
@@ -1668,6 +1669,7 @@ public class PackageManagerService extends IPackageManager.Stub
mSystemTextClassifierPackageName = testParams.systemTextClassifierPackage;
mRetailDemoPackage = testParams.retailDemoPackage;
mRecentsPackage = testParams.recentsPackage;
mAmbientContextDetectionPackage = testParams.ambientContextDetectionPackage;
mConfiguratorPackage = testParams.configuratorPackage;
mAppPredictionServicePackage = testParams.appPredictionServicePackage;
mIncidentReportApproverPackage = testParams.incidentReportApproverPackage;
@@ -1995,6 +1997,7 @@ public class PackageManagerService extends IPackageManager.Stub
mRetailDemoPackage = getRetailDemoPackageName();
mOverlayConfigSignaturePackage = getOverlayConfigSignaturePackageName();
mRecentsPackage = getRecentsPackageName();
mAmbientContextDetectionPackage = getAmbientContextDetectionPackageName();
// Now that we know all of the shared libraries, update all clients to have
// the correct library paths.
@@ -5618,6 +5621,11 @@ public class PackageManagerService extends IPackageManager.Stub
return mPmInternal.getSetupWizardPackageName();
}
public @Nullable String getAmbientContextDetectionPackageName() {
return ensureSystemPackageName(getPackageFromComponentString(
R.string.config_defaultAmbientContextDetectionService));
}
public String getIncidentReportApproverPackageName() {
return ensureSystemPackageName(mContext.getString(
R.string.config_incidentReportApproverPackage));
@@ -8693,6 +8701,8 @@ public class PackageManagerService extends IPackageManager.Stub
return mComputer.filterOnlySystemPackages(mConfiguratorPackage);
case PackageManagerInternal.PACKAGE_INCIDENT_REPORT_APPROVER:
return mComputer.filterOnlySystemPackages(mIncidentReportApproverPackage);
case PackageManagerInternal.PACKAGE_AMBIENT_CONTEXT_DETECTION:
return mComputer.filterOnlySystemPackages(mAmbientContextDetectionPackage);
case PackageManagerInternal.PACKAGE_APP_PREDICTOR:
return mComputer.filterOnlySystemPackages(mAppPredictionServicePackage);
case PackageManagerInternal.PACKAGE_COMPANION:

View File

@@ -91,6 +91,7 @@ public final class PackageManagerServiceTestParams {
public ViewCompiler viewCompiler;
public @Nullable String retailDemoPackage;
public @Nullable String recentsPackage;
public @Nullable String ambientContextDetectionPackage;
public ComponentName resolveComponentName;
public ArrayMap<String, AndroidPackage> packages;
public boolean enableFreeCacheV2;

View File

@@ -104,6 +104,7 @@ import com.android.internal.util.EmergencyAffordanceManager;
import com.android.internal.util.FrameworkStatsLog;
import com.android.internal.widget.ILockSettings;
import com.android.server.am.ActivityManagerService;
import com.android.server.ambientcontext.AmbientContextManagerService;
import com.android.server.appbinding.AppBindingService;
import com.android.server.art.ArtManagerLocal;
import com.android.server.attention.AttentionManagerService;
@@ -1810,6 +1811,7 @@ public final class SystemServer implements Dumpable {
startRotationResolverService(context, t);
startSystemCaptionsManagerService(context, t);
startTextToSpeechManagerService(context, t);
startAmbientContextService(t);
// System Speech Recognition Service
t.traceBegin("StartSpeechRecognitionManagerService");
@@ -3162,6 +3164,17 @@ public final class SystemServer implements Dumpable {
}
private void startAmbientContextService(@NonNull TimingsTraceAndSlog t) {
if (!AmbientContextManagerService.isDetectionServiceConfigured()) {
Slog.d(TAG, "AmbientContextDetectionService is not configured on this device");
return;
}
t.traceBegin("StartAmbientContextService");
mSystemServiceManager.startService(AmbientContextManagerService.class);
t.traceEnd();
}
private static void startSystemUi(Context context, WindowManagerService windowManager) {
PackageManagerInternal pm = LocalServices.getService(PackageManagerInternal.class);
Intent intent = new Intent();