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.

Client apps need the ACCESS_AMBIENT_CONTEXT_EVENT permission to use the service. The permission protection level is internal|role.

API overview: http://go/ambient-framework-api
PRD: http://go/ambient-attribution-prd
Design doc: http://go/ambient-service-api

Test: CTS test
Bug: 192476579
Change-Id: I9daede83af34f215c278cb0530238faba1be5c70
Ignore-AOSP-First: to prevent new feature leak.
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index 88b0086..1d1b5c7 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -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";
@@ -37,6 +38,7 @@
     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";
@@ -1192,6 +1194,87 @@
 
 }
 
+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 {
@@ -2643,6 +2726,7 @@
     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";
@@ -9884,6 +9968,7 @@
     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";
@@ -10454,6 +10539,18 @@
 
 }
 
+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 {
diff --git a/core/java/android/app/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java
index 67c42f6..dac90ce 100644
--- a/core/java/android/app/SystemServiceRegistry.java
+++ b/core/java/android/app/SystemServiceRegistry.java
@@ -24,6 +24,8 @@
 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;
@@ -1518,6 +1520,18 @@
                     }
                 });
 
+        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
diff --git a/core/java/android/app/ambientcontext/AmbientContextEvent.aidl b/core/java/android/app/ambientcontext/AmbientContextEvent.aidl
new file mode 100644
index 0000000..0965b1a
--- /dev/null
+++ b/core/java/android/app/ambientcontext/AmbientContextEvent.aidl
@@ -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;
diff --git a/core/java/android/app/ambientcontext/AmbientContextEvent.java b/core/java/android/app/ambientcontext/AmbientContextEvent.java
new file mode 100644
index 0000000..11e695ad
--- /dev/null
+++ b/core/java/android/app/ambientcontext/AmbientContextEvent.java
@@ -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
+
+}
diff --git a/core/java/android/app/ambientcontext/AmbientContextEventRequest.aidl b/core/java/android/app/ambientcontext/AmbientContextEventRequest.aidl
new file mode 100644
index 0000000..e24c6ad
--- /dev/null
+++ b/core/java/android/app/ambientcontext/AmbientContextEventRequest.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app.ambientcontext;
+
+parcelable AmbientContextEventRequest;
diff --git a/core/java/android/app/ambientcontext/AmbientContextEventRequest.java b/core/java/android/app/ambientcontext/AmbientContextEventRequest.java
new file mode 100644
index 0000000..82b16a2
--- /dev/null
+++ b/core/java/android/app/ambientcontext/AmbientContextEventRequest.java
@@ -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");
+            }
+        }
+    }
+}
diff --git a/core/java/android/app/ambientcontext/AmbientContextEventResponse.aidl b/core/java/android/app/ambientcontext/AmbientContextEventResponse.aidl
new file mode 100644
index 0000000..4dc6466
--- /dev/null
+++ b/core/java/android/app/ambientcontext/AmbientContextEventResponse.aidl
@@ -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;
\ No newline at end of file
diff --git a/core/java/android/app/ambientcontext/AmbientContextEventResponse.java b/core/java/android/app/ambientcontext/AmbientContextEventResponse.java
new file mode 100644
index 0000000..472a78b
--- /dev/null
+++ b/core/java/android/app/ambientcontext/AmbientContextEventResponse.java
@@ -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");
+            }
+        }
+    }
+}
diff --git a/core/java/android/app/ambientcontext/AmbientContextManager.java b/core/java/android/app/ambientcontext/AmbientContextManager.java
new file mode 100644
index 0000000..6841d1b
--- /dev/null
+++ b/core/java/android/app/ambientcontext/AmbientContextManager.java
@@ -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();
+        }
+    }
+}
diff --git a/core/java/android/app/ambientcontext/IAmbientContextEventObserver.aidl b/core/java/android/app/ambientcontext/IAmbientContextEventObserver.aidl
new file mode 100644
index 0000000..9032fe1
--- /dev/null
+++ b/core/java/android/app/ambientcontext/IAmbientContextEventObserver.aidl
@@ -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);
+}
\ No newline at end of file
diff --git a/core/java/android/app/ambientcontext/OWNERS b/core/java/android/app/ambientcontext/OWNERS
new file mode 100644
index 0000000..a863297
--- /dev/null
+++ b/core/java/android/app/ambientcontext/OWNERS
@@ -0,0 +1,3 @@
+enxun@google.com
+kxchen@google.com
+tgadh@google.com
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index 2309fb6..b1a1d20 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -41,6 +41,7 @@
 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;
@@ -5931,6 +5932,17 @@
     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.
      *
diff --git a/core/java/android/provider/DeviceConfig.java b/core/java/android/provider/DeviceConfig.java
index 6349cde..35e4e3d 100644
--- a/core/java/android/provider/DeviceConfig.java
+++ b/core/java/android/provider/DeviceConfig.java
@@ -687,6 +687,15 @@
     @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 =
diff --git a/core/java/android/service/ambientcontext/AmbientContextDetectionService.java b/core/java/android/service/ambientcontext/AmbientContextDetectionService.java
new file mode 100644
index 0000000..dccfe36
--- /dev/null
+++ b/core/java/android/service/ambientcontext/AmbientContextDetectionService.java
@@ -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);
+}
diff --git a/core/java/android/service/ambientcontext/IAmbientContextDetectionService.aidl b/core/java/android/service/ambientcontext/IAmbientContextDetectionService.aidl
new file mode 100644
index 0000000..1c6e25e
--- /dev/null
+++ b/core/java/android/service/ambientcontext/IAmbientContextDetectionService.aidl
@@ -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);
+}
\ No newline at end of file
diff --git a/core/java/android/service/ambientcontext/OWNERS b/core/java/android/service/ambientcontext/OWNERS
new file mode 100644
index 0000000..a863297
--- /dev/null
+++ b/core/java/android/service/ambientcontext/OWNERS
@@ -0,0 +1,3 @@
+enxun@google.com
+kxchen@google.com
+tgadh@google.com
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 0fd4629..5550d1f 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -6151,6 +6151,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. -->
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index e9ba92f..dfeeb6c 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -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"
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 5e88519..f639350 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -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" />
 
diff --git a/services/core/java/android/content/pm/PackageManagerInternal.java b/services/core/java/android/content/pm/PackageManagerInternal.java
index 1666d15..f56bfab 100644
--- a/services/core/java/android/content/pm/PackageManagerInternal.java
+++ b/services/core/java/android/content/pm/PackageManagerInternal.java
@@ -105,9 +105,10 @@
     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,
diff --git a/services/core/java/com/android/server/ambientcontext/AmbientContextManagerPerUserService.java b/services/core/java/com/android/server/ambientcontext/AmbientContextManagerPerUserService.java
new file mode 100644
index 0000000..6982513
--- /dev/null
+++ b/services/core/java/com/android/server/ambientcontext/AmbientContextManagerPerUserService.java
@@ -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);
+            }
+        });
+    }
+}
diff --git a/services/core/java/com/android/server/ambientcontext/AmbientContextManagerService.java b/services/core/java/com/android/server/ambientcontext/AmbientContextManagerService.java
new file mode 100644
index 0000000..33905f2
--- /dev/null
+++ b/services/core/java/com/android/server/ambientcontext/AmbientContextManagerService.java
@@ -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);
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/ambientcontext/AmbientContextShellCommand.java b/services/core/java/com/android/server/ambientcontext/AmbientContextShellCommand.java
new file mode 100644
index 0000000..b5cd985
--- /dev/null
+++ b/services/core/java/com/android/server/ambientcontext/AmbientContextShellCommand.java
@@ -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;
+    }
+}
diff --git a/services/core/java/com/android/server/ambientcontext/OWNERS b/services/core/java/com/android/server/ambientcontext/OWNERS
new file mode 100644
index 0000000..a863297
--- /dev/null
+++ b/services/core/java/com/android/server/ambientcontext/OWNERS
@@ -0,0 +1,3 @@
+enxun@google.com
+kxchen@google.com
+tgadh@google.com
diff --git a/services/core/java/com/android/server/ambientcontext/RemoteAmbientContextDetectionService.java b/services/core/java/com/android/server/ambientcontext/RemoteAmbientContextDetectionService.java
new file mode 100644
index 0000000..5cc29b3
--- /dev/null
+++ b/services/core/java/com/android/server/ambientcontext/RemoteAmbientContextDetectionService.java
@@ -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));
+    }
+}
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 81a2c00..dd26234 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -951,6 +951,7 @@
     final @Nullable String mRetailDemoPackage;
     final @Nullable String mOverlayConfigSignaturePackage;
     final @Nullable String mRecentsPackage;
+    final @Nullable String mAmbientContextDetectionPackage;
 
     @GuardedBy("mLock")
     private final PackageUsage mPackageUsage = new PackageUsage();
@@ -1669,6 +1670,7 @@
         mSystemTextClassifierPackageName = testParams.systemTextClassifierPackage;
         mRetailDemoPackage = testParams.retailDemoPackage;
         mRecentsPackage = testParams.recentsPackage;
+        mAmbientContextDetectionPackage = testParams.ambientContextDetectionPackage;
         mConfiguratorPackage = testParams.configuratorPackage;
         mAppPredictionServicePackage = testParams.appPredictionServicePackage;
         mIncidentReportApproverPackage = testParams.incidentReportApproverPackage;
@@ -1996,6 +1998,7 @@
             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.
@@ -5643,6 +5646,11 @@
         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));
@@ -8718,6 +8726,8 @@
                 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:
diff --git a/services/core/java/com/android/server/pm/PackageManagerServiceTestParams.java b/services/core/java/com/android/server/pm/PackageManagerServiceTestParams.java
index 0d6555c..db60686 100644
--- a/services/core/java/com/android/server/pm/PackageManagerServiceTestParams.java
+++ b/services/core/java/com/android/server/pm/PackageManagerServiceTestParams.java
@@ -91,6 +91,7 @@
     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;
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 233f2ff..e750ce0 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -103,6 +103,7 @@
 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;
@@ -1809,6 +1810,7 @@
             startRotationResolverService(context, t);
             startSystemCaptionsManagerService(context, t);
             startTextToSpeechManagerService(context, t);
+            startAmbientContextService(t);
 
             // System Speech Recognition Service
             t.traceBegin("StartSpeechRecognitionManagerService");
@@ -3160,6 +3162,17 @@
 
     }
 
+    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();