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:
@@ -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 {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
492
core/java/android/app/ambientcontext/AmbientContextEvent.java
Normal file
492
core/java/android/app/ambientcontext/AmbientContextEvent.java
Normal 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
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
@@ -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");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
@@ -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");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
147
core/java/android/app/ambientcontext/AmbientContextManager.java
Normal file
147
core/java/android/app/ambientcontext/AmbientContextManager.java
Normal 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();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
3
core/java/android/app/ambientcontext/OWNERS
Normal file
3
core/java/android/app/ambientcontext/OWNERS
Normal file
@@ -0,0 +1,3 @@
|
||||
enxun@google.com
|
||||
kxchen@google.com
|
||||
tgadh@google.com
|
||||
@@ -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.
|
||||
|
||||
@@ -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 =
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
3
core/java/android/service/ambientcontext/OWNERS
Normal file
3
core/java/android/service/ambientcontext/OWNERS
Normal file
@@ -0,0 +1,3 @@
|
||||
enxun@google.com
|
||||
kxchen@google.com
|
||||
tgadh@google.com
|
||||
@@ -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. -->
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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" />
|
||||
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
enxun@google.com
|
||||
kxchen@google.com
|
||||
tgadh@google.com
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
@@ -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:
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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();
|
||||
|
||||
Reference in New Issue
Block a user