Merge "Add AppOpsManager.opToDefaultMode() as system API."
diff --git a/api/system-current.txt b/api/system-current.txt
index 0837a20..f2625b4 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -6041,6 +6041,7 @@
     method public void setCallExtra(java.lang.String, java.lang.String);
     method public void setCallExtraBoolean(java.lang.String, boolean);
     method public void setCallExtraInt(java.lang.String, int);
+    method public void setCallRestrictCause(int);
     method public void updateCallExtras(android.telephony.ims.ImsCallProfile);
     method public void updateCallType(android.telephony.ims.ImsCallProfile);
     method public void updateMediaProfile(android.telephony.ims.ImsCallProfile);
diff --git a/cmds/statsd/src/StatsLogProcessor.cpp b/cmds/statsd/src/StatsLogProcessor.cpp
index 065f49e..f0f5993 100644
--- a/cmds/statsd/src/StatsLogProcessor.cpp
+++ b/cmds/statsd/src/StatsLogProcessor.cpp
@@ -272,7 +272,7 @@
 }
 
 /*
- * onDumpReport dumps serialized ConfigMetricsReportList into outData.
+ * onDumpReport dumps serialized ConfigMetricsReportList into proto.
  */
 void StatsLogProcessor::onDumpReport(const ConfigKey& key, const int64_t dumpTimeStampNs,
                                      const bool include_current_partial_bucket,
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index 8d54e91..86ed267 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -120,6 +120,7 @@
 import android.view.autofill.AutofillManager.AutofillClient;
 import android.view.autofill.AutofillPopupWindow;
 import android.view.autofill.IAutofillWindowPresenter;
+import android.view.intelligence.ContentCaptureEvent;
 import android.view.intelligence.IntelligenceManager;
 import android.widget.AdapterView;
 import android.widget.Toast;
@@ -1023,6 +1024,31 @@
         return mIntelligenceManager;
     }
 
+    private void notifyIntelligenceManagerIfNeeded(@ContentCaptureEvent.EventType int event) {
+        final IntelligenceManager im = getIntelligenceManager();
+        if (im == null || !im.isContentCaptureEnabled()) {
+            return;
+        }
+        switch (event) {
+            case ContentCaptureEvent.TYPE_ACTIVITY_CREATED:
+                //TODO(b/111276913): decide whether the InteractionSessionId should be
+                // saved / restored in the activity bundle.
+                im.onActivityCreated(mToken, getComponentName());
+                break;
+            case ContentCaptureEvent.TYPE_ACTIVITY_DESTROYED:
+                im.onActivityDestroyed();
+                break;
+            case ContentCaptureEvent.TYPE_ACTIVITY_STARTED:
+            case ContentCaptureEvent.TYPE_ACTIVITY_RESUMED:
+            case ContentCaptureEvent.TYPE_ACTIVITY_PAUSED:
+            case ContentCaptureEvent.TYPE_ACTIVITY_STOPPED:
+                im.onActivityLifecycleEvent(event);
+                break;
+            default:
+                Log.w(TAG, "notifyIntelligenceManagerIfNeeded(): invalid type " + event);
+        }
+    }
+
     @Override
     protected void attachBaseContext(Context newBase) {
         super.attachBaseContext(newBase);
@@ -1099,11 +1125,7 @@
         mRestoredFromBundle = savedInstanceState != null;
         mCalled = true;
 
-        if (getIntelligenceManager() != null) {
-            //TODO(b/111276913): decide whether the screen_obs session id should be saved / restored
-            // in the activity bundle.
-            mIntelligenceManager.onActivityCreated(mToken, getComponentName());
-        }
+        notifyIntelligenceManagerIfNeeded(ContentCaptureEvent.TYPE_ACTIVITY_CREATED);
     }
 
     /**
@@ -1337,6 +1359,7 @@
         if (mAutoFillResetNeeded) {
             getAutofillManager().onVisibleForAutofill();
         }
+        notifyIntelligenceManagerIfNeeded(ContentCaptureEvent.TYPE_ACTIVITY_STARTED);
     }
 
     /**
@@ -1419,6 +1442,7 @@
                 }
             }
         }
+        notifyIntelligenceManagerIfNeeded(ContentCaptureEvent.TYPE_ACTIVITY_RESUMED);
         mCalled = true;
     }
 
@@ -1812,6 +1836,7 @@
                 mAutoFillIgnoreFirstResumePause = false;
             }
         }
+        notifyIntelligenceManagerIfNeeded(ContentCaptureEvent.TYPE_ACTIVITY_PAUSED);
         mCalled = true;
     }
 
@@ -2000,6 +2025,7 @@
                 getAutofillManager().onPendingSaveUi(AutofillManager.PENDING_UI_OPERATION_CANCEL,
                         mIntent.getIBinderExtra(AutofillManager.EXTRA_RESTORE_SESSION_TOKEN));
             }
+            notifyIntelligenceManagerIfNeeded(ContentCaptureEvent.TYPE_ACTIVITY_STOPPED);
         }
     }
 
@@ -2071,9 +2097,8 @@
 
         getApplication().dispatchActivityDestroyed(this);
 
-        if (getIntelligenceManager() != null) {
-            mIntelligenceManager.onActivityDestroyed();
-        }
+        notifyIntelligenceManagerIfNeeded(ContentCaptureEvent.TYPE_ACTIVITY_DESTROYED);
+
     }
 
     /**
diff --git a/core/java/android/service/intelligence/IIntelligenceService.aidl b/core/java/android/service/intelligence/IIntelligenceService.aidl
index ee93326..bacad8b 100644
--- a/core/java/android/service/intelligence/IIntelligenceService.aidl
+++ b/core/java/android/service/intelligence/IIntelligenceService.aidl
@@ -19,6 +19,11 @@
 import android.service.intelligence.InteractionSessionId;
 import android.service.intelligence.InteractionContext;
 
+import android.view.intelligence.ContentCaptureEvent;
+
+import java.util.List;
+
+
 /**
  * Interface from the system to an intelligence service.
  *
@@ -28,4 +33,7 @@
 
     // Called when session is created (context not null) or destroyed (context null)
     void onSessionLifecycle(in InteractionContext context, in InteractionSessionId sessionId);
+
+    void onContentCaptureEvents(in InteractionSessionId sessionId,
+                                in List<ContentCaptureEvent> events);
 }
diff --git a/core/java/android/service/intelligence/IntelligenceService.java b/core/java/android/service/intelligence/IntelligenceService.java
index ce0a88a..a2b60f0 100644
--- a/core/java/android/service/intelligence/IntelligenceService.java
+++ b/core/java/android/service/intelligence/IntelligenceService.java
@@ -70,6 +70,14 @@
                                 IntelligenceService.this, sessionId));
             }
         }
+        @Override
+        public void onContentCaptureEvents(InteractionSessionId sessionId,
+                List<ContentCaptureEvent> events) {
+            mHandler.sendMessage(
+                    obtainMessage(IntelligenceService::onContentCaptureEvent,
+                            IntelligenceService.this, sessionId, events));
+
+        }
     };
 
     @CallSuper
@@ -105,6 +113,7 @@
      * @param sessionId the session's Id
      * @param events the events
      */
+     // TODO(b/111276913): rename to onContentCaptureEvents
     public abstract void onContentCaptureEvent(@NonNull InteractionSessionId sessionId,
             @NonNull List<ContentCaptureEvent> events);
 
diff --git a/core/java/android/service/intelligence/InteractionSessionId.java b/core/java/android/service/intelligence/InteractionSessionId.java
index ca68f8e..a2971ae 100644
--- a/core/java/android/service/intelligence/InteractionSessionId.java
+++ b/core/java/android/service/intelligence/InteractionSessionId.java
@@ -16,43 +16,85 @@
 
 package android.service.intelligence;
 
+import android.annotation.NonNull;
 import android.annotation.SystemApi;
 import android.os.Parcel;
 import android.os.Parcelable;
 
 import java.io.PrintWriter;
+import java.util.UUID;
 
 // TODO(b/111276913): add javadocs / implement equals/hashcode/string
 /** @hide */
 @SystemApi
 public final class InteractionSessionId implements Parcelable {
 
-    private final int mGlobalId;
+    private final @NonNull String mValue;
 
-    // TODO(b/111276913): remove if not needed
-    private final int mLocalId;
-
-    /** @hide */
-    public InteractionSessionId(int globalId, int localId) {
-        mGlobalId = globalId;
-        mLocalId = localId;
+    /**
+     * Creates a new instance.
+     *
+     * @hide
+     */
+    public InteractionSessionId() {
+        this(UUID.randomUUID().toString());
     }
 
-    /** @hide */
-    public int getGlobalId() {
-        return mGlobalId;
+    /**
+     * Creates a new instance.
+     *
+     * @param value The internal value.
+     *
+     * @hide
+     */
+    public InteractionSessionId(@NonNull String value) {
+        mValue = value;
+    }
+
+    /**
+     * @hide
+     */
+    public String getValue() {
+        return mValue;
+    }
+
+    @Override
+    public int hashCode() {
+        final int prime = 31;
+        int result = 1;
+        result = prime * result + ((mValue == null) ? 0 : mValue.hashCode());
+        return result;
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj) return true;
+        if (obj == null) return false;
+        if (getClass() != obj.getClass()) return false;
+        final InteractionSessionId other = (InteractionSessionId) obj;
+        if (mValue == null) {
+            if (other.mValue != null) return false;
+        } else if (!mValue.equals(other.mValue)) {
+            return false;
+        }
+        return true;
+    }
+
+    /**
+     * @inheritdoc
+     *
+     * <p><b>NOTE: </b>this method is only useful for debugging purposes and is not guaranteed to
+     * be stable, hence it should not be used to identify the session.
+     */
+    @Override
+    public String toString() {
+        return mValue;
     }
 
     /** @hide */
     // TODO(b/111276913): dump to proto as well
     public void dump(PrintWriter pw) {
-        pw.print("globalId="); pw.print(mGlobalId);
-        pw.print("localId="); pw.print(mLocalId);
-    }
-
-    @Override
-    public String toString() {
-        return "SessionId[globalId=" + mGlobalId + ", localId=" + mLocalId + "]";
+        pw.print(mValue);
     }
 
     @Override
@@ -62,8 +104,7 @@
 
     @Override
     public void writeToParcel(Parcel parcel, int flags) {
-        parcel.writeInt(mGlobalId);
-        parcel.writeInt(mLocalId);
+        parcel.writeString(mValue);
     }
 
     public static final Parcelable.Creator<InteractionSessionId> CREATOR =
@@ -71,9 +112,7 @@
 
         @Override
         public InteractionSessionId createFromParcel(Parcel parcel) {
-            final int globalId = parcel.readInt();
-            final int localId = parcel.readInt();
-            return new InteractionSessionId(globalId, localId);
+            return new InteractionSessionId(parcel.readString());
         }
 
         @Override
diff --git a/core/java/android/view/intelligence/ContentCaptureEvent.aidl b/core/java/android/view/intelligence/ContentCaptureEvent.aidl
new file mode 100644
index 0000000..c66a6cb
--- /dev/null
+++ b/core/java/android/view/intelligence/ContentCaptureEvent.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2018 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.view.intelligence;
+
+parcelable ContentCaptureEvent;
diff --git a/core/java/android/view/intelligence/ContentCaptureEvent.java b/core/java/android/view/intelligence/ContentCaptureEvent.java
index d6aec34..2530ae3 100644
--- a/core/java/android/view/intelligence/ContentCaptureEvent.java
+++ b/core/java/android/view/intelligence/ContentCaptureEvent.java
@@ -30,6 +30,11 @@
 @SystemApi
 public final class ContentCaptureEvent implements Parcelable {
 
+    /** @hide */
+    public static final int TYPE_ACTIVITY_DESTROYED = -2;
+    /** @hide */
+    public static final int TYPE_ACTIVITY_CREATED = -1;
+
     /**
      * Called when the activity is started.
      */
@@ -85,10 +90,18 @@
             TYPE_VIEW_TEXT_CHANGED
     })
     @Retention(RetentionPolicy.SOURCE)
-    @interface EventType{}
+    public @interface EventType{}
+
+    private final int mType;
+    private final long mEventTime;
+    private final int mFlags;
+
 
     /** @hide */
-    ContentCaptureEvent() {
+    public ContentCaptureEvent(int type, long eventTime, int flags) {
+        mType = type;
+        mEventTime = eventTime;
+        mFlags = flags;
     }
 
     /**
@@ -99,14 +112,14 @@
      * {@link #TYPE_VIEW_ADDED}, {@link #TYPE_VIEW_REMOVED}, or {@link #TYPE_VIEW_TEXT_CHANGED}.
      */
     public @EventType int getType() {
-        return 42;
+        return mType;
     }
 
     /**
      * Gets when the event was generated, in ms.
      */
     public long getEventTime() {
-        return 48151623;
+        return mEventTime;
     }
 
     /**
@@ -116,7 +129,7 @@
      * {@link android.view.intelligence.IntelligenceManager#FLAG_USER_INPUT}.
      */
     public int getFlags() {
-        return 0;
+        return mFlags;
     }
 
     /**
@@ -150,13 +163,25 @@
     }
 
     @Override
+    public String toString() {
+        final StringBuilder string = new StringBuilder("ContentCaptureEvent[type=")
+                .append(getTypeAsString(mType)).append(", time=").append(mEventTime);
+        if (mFlags > 0) {
+            string.append(", flags=").append(mFlags);
+        }
+        return string.append(']').toString();
+    }
+
+    @Override
     public int describeContents() {
         return 0;
     }
 
     @Override
     public void writeToParcel(Parcel parcel, int flags) {
-        // TODO(b/111276913): implement
+        parcel.writeInt(mType);
+        parcel.writeLong(mEventTime);
+        parcel.writeInt(mFlags);
     }
 
     public static final Parcelable.Creator<ContentCaptureEvent> CREATOR =
@@ -164,8 +189,10 @@
 
         @Override
         public ContentCaptureEvent createFromParcel(Parcel parcel) {
-            // TODO(b/111276913): implement
-            return null;
+            final int type = parcel.readInt();
+            final long eventTime  = parcel.readLong();
+            final int flags = parcel.readInt();
+            return new ContentCaptureEvent(type, eventTime, flags);
         }
 
         @Override
@@ -173,4 +200,27 @@
             return new ContentCaptureEvent[size];
         }
     };
+
+
+    /** @hide */
+    public static String getTypeAsString(@EventType int type) {
+        switch (type) {
+            case TYPE_ACTIVITY_STARTED:
+                return "ACTIVITY_STARTED";
+            case TYPE_ACTIVITY_RESUMED:
+                return "ACTIVITY_RESUMED";
+            case TYPE_ACTIVITY_PAUSED:
+                return "ACTIVITY_PAUSED";
+            case TYPE_ACTIVITY_STOPPED:
+                return "ACTIVITY_STOPPED";
+            case TYPE_VIEW_ADDED:
+                return "VIEW_ADDED";
+            case TYPE_VIEW_REMOVED:
+                return "VIEW_REMOVED";
+            case TYPE_VIEW_TEXT_CHANGED:
+                return "VIEW_TEXT_CHANGED";
+            default:
+                return "UKNOWN_TYPE: " + type;
+        }
+    }
 }
diff --git a/core/java/android/view/intelligence/IIntelligenceManager.aidl b/core/java/android/view/intelligence/IIntelligenceManager.aidl
index f4901c3..2f128de 100644
--- a/core/java/android/view/intelligence/IIntelligenceManager.aidl
+++ b/core/java/android/view/intelligence/IIntelligenceManager.aidl
@@ -18,8 +18,13 @@
 
 import android.content.ComponentName;
 import android.os.IBinder;
+import android.service.intelligence.InteractionSessionId;
+import android.view.intelligence.ContentCaptureEvent;
+
 import com.android.internal.os.IResultReceiver;
 
+import java.util.List;
+
 /**
  * {@hide}
  */
@@ -28,11 +33,16 @@
       * Starts a session, sending the "remote" sessionId to the receiver.
       */
     void startSession(int userId, IBinder activityToken, in ComponentName componentName,
-                      int localSessionId, int flags, in IResultReceiver result);
+                      in InteractionSessionId sessionId, int flags, in IResultReceiver result);
 
     /**
       * Finishes a session.
       */
-    void finishSession(int userId, IBinder activityToken, in ComponentName componentName,
-                       int localSessionId, int globalSessionId);
+    void finishSession(int userId, in InteractionSessionId sessionId);
+
+    /**
+      * Sends a batch of events
+      */
+    void sendEvents(int userId, in InteractionSessionId sessionId,
+                    in List<ContentCaptureEvent> events);
 }
diff --git a/core/java/android/view/intelligence/IntelligenceManager.java b/core/java/android/view/intelligence/IntelligenceManager.java
index b1d06f7..9bf6c2c 100644
--- a/core/java/android/view/intelligence/IntelligenceManager.java
+++ b/core/java/android/view/intelligence/IntelligenceManager.java
@@ -24,13 +24,18 @@
 import android.os.Bundle;
 import android.os.IBinder;
 import android.os.RemoteException;
+import android.os.SystemClock;
+import android.service.intelligence.InteractionSessionId;
 import android.util.Log;
+import android.view.intelligence.ContentCaptureEvent.EventType;
 
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.os.IResultReceiver;
 import com.android.internal.util.Preconditions;
 
 import java.io.PrintWriter;
+import java.util.Arrays;
+import java.util.List;
 import java.util.Set;
 
 /**
@@ -51,9 +56,6 @@
     public static final int FLAG_USER_INPUT = 0x1;
 
 
-    /** @hide */
-    public static final int NO_SESSION = 0;
-
     /**
      * Initial state, when there is no session.
      *
@@ -62,11 +64,11 @@
     public static final int STATE_UNKNOWN = 0;
 
     /**
-     * Service's startSession() was called, but remote session id was not returned yet.
+     * Service's startSession() was called, but server didn't confirm it was created yet.
      *
      * @hide
      */
-    public static final int STATE_WAITING_FOR_SESSION_ID = 1;
+    public static final int STATE_WAITING_FOR_SERVER = 1;
 
     /**
      * Session is active.
@@ -75,8 +77,6 @@
      */
     public static final int STATE_ACTIVE = 2;
 
-    private static int sNextSessionId;
-
     private final Context mContext;
 
     @Nullable
@@ -84,14 +84,9 @@
 
     private final Object mLock = new Object();
 
-    // TODO(b/111276913): localSessionId might be an overkill, perhaps just the global id is enough.
-    // Let's keep both for now, and revisit once we decide whether the session id will be persisted
-    // when the activity's process is killed
+    @Nullable
     @GuardedBy("mLock")
-    private int mLocalSessionId = NO_SESSION;
-
-    @GuardedBy("mLock")
-    private int mRemoteSessionId = NO_SESSION;
+    private InteractionSessionId mId;
 
     @GuardedBy("mLock")
     private int mState = STATE_UNKNOWN;
@@ -120,27 +115,25 @@
                         + getStateAsStringLocked());
                 return;
             }
-            mState = STATE_WAITING_FOR_SESSION_ID;
-            mLocalSessionId = ++sNextSessionId;
-            mRemoteSessionId = NO_SESSION;
+            mState = STATE_WAITING_FOR_SERVER;
+            mId = new InteractionSessionId();
             mApplicationToken = token;
             mComponentName = componentName;
 
             if (VERBOSE) {
                 Log.v(TAG, "onActivityStarted(): token=" + token + ", act=" + componentName
-                        + ", localSessionId=" + mLocalSessionId);
+                        + ", id=" + mId);
             }
             final int flags = 0; // TODO(b/111276913): get proper flags
 
             try {
                 mService.startSession(mContext.getUserId(), mApplicationToken, componentName,
-                        mLocalSessionId, flags, new IResultReceiver.Stub() {
+                        mId, flags, new IResultReceiver.Stub() {
                             @Override
                             public void send(int resultCode, Bundle resultData)
                                     throws RemoteException {
                                 synchronized (mLock) {
                                     if (resultCode > 0) {
-                                        mRemoteSessionId = resultCode;
                                         mState = STATE_ACTIVE;
                                     } else {
                                         // TODO(b/111276913): handle other cases like disabled by
@@ -149,7 +142,7 @@
                                     }
                                     if (VERBOSE) {
                                         Log.v(TAG, "onActivityStarted() result: code=" + resultCode
-                                                + ", remoteSession=" + mRemoteSessionId
+                                                + ", id=" + mId
                                                 + ", state=" + getStateAsStringLocked());
                                     }
                                 }
@@ -161,6 +154,37 @@
         }
     }
 
+    /**
+     * Used for intermediate events (i.e, other than created and destroyed).
+     *
+     * @hide
+     */
+    public void onActivityLifecycleEvent(@EventType int type) {
+        if (!isContentCaptureEnabled()) return;
+
+        //TODO(b/111276913): should buffer event (and call service on handler thread), instead of
+        // calling right away
+        final ContentCaptureEvent event = new ContentCaptureEvent(type, SystemClock.uptimeMillis(),
+                0);
+        final List<ContentCaptureEvent> events = Arrays.asList(event);
+
+        synchronized (mLock) {
+            //TODO(b/111276913): check session state; for example, how to handle if it's waiting for
+            // remote id
+
+            if (VERBOSE) {
+                Log.v(TAG, "onActivityLifecycleEvent() for " + mComponentName.flattenToShortString()
+                        + ": " + ContentCaptureEvent.getTypeAsString(type));
+            }
+
+            try {
+                mService.sendEvents(mContext.getUserId(), mId, events);
+            } catch (RemoteException e) {
+                throw e.rethrowFromSystemServer();
+            }
+        }
+    }
+
     /** @hide */
     public void onActivityDestroyed() {
         if (!isContentCaptureEnabled()) return;
@@ -171,15 +195,13 @@
 
             if (VERBOSE) {
                 Log.v(TAG, "onActivityDestroyed(): state=" + getStateAsStringLocked()
-                        + ", localSessionId=" + mLocalSessionId
-                        + ", mRemoteSessionId=" + mRemoteSessionId);
+                        + ", mId=" + mId);
             }
 
             try {
-                mService.finishSession(mContext.getUserId(), mApplicationToken, mComponentName,
-                        mLocalSessionId, mRemoteSessionId);
+                mService.finishSession(mContext.getUserId(), mId);
                 mState = STATE_UNKNOWN;
-                mLocalSessionId = mRemoteSessionId = NO_SESSION;
+                mId = null;
                 mApplicationToken = null;
                 mComponentName = null;
             } catch (RemoteException e) {
@@ -298,12 +320,11 @@
             pw.print(prefix2); pw.print("mService: "); pw.println(mService);
             pw.print(prefix2); pw.print("user: "); pw.println(mContext.getUserId());
             pw.print(prefix2); pw.print("enabled: "); pw.println(isContentCaptureEnabled());
-            pw.print(prefix2); pw.print("mLocalSessionId: "); pw.println(mLocalSessionId);
-            pw.print(prefix2); pw.print("mRemoteSessionId: "); pw.println(mRemoteSessionId);
-            pw.print(prefix2); pw.print("mState: "); pw.print(mState); pw.print(" (");
+            pw.print(prefix2); pw.print("id: "); pw.println(mId);
+            pw.print(prefix2); pw.print("state: "); pw.print(mState); pw.print(" (");
             pw.print(getStateAsStringLocked()); pw.println(")");
-            pw.print(prefix2); pw.print("mAppToken: "); pw.println(mApplicationToken);
-            pw.print(prefix2); pw.print("mComponentName: "); pw.println(mComponentName);
+            pw.print(prefix2); pw.print("appToken: "); pw.println(mApplicationToken);
+            pw.print(prefix2); pw.print("componentName: "); pw.println(mComponentName);
         }
     }
 
@@ -317,8 +338,8 @@
         switch (state) {
             case STATE_UNKNOWN:
                 return "UNKNOWN";
-            case STATE_WAITING_FOR_SESSION_ID:
-                return "WAITING_FOR_SESSION_ID";
+            case STATE_WAITING_FOR_SERVER:
+                return "WAITING_FOR_SERVER";
             case STATE_ACTIVE:
                 return "ACTIVE";
             default:
diff --git a/media/java/android/media/MediaPlayer2.java b/media/java/android/media/MediaPlayer2.java
index b51caa5..7470192 100644
--- a/media/java/android/media/MediaPlayer2.java
+++ b/media/java/android/media/MediaPlayer2.java
@@ -23,9 +23,7 @@
 import android.annotation.UnsupportedAppUsage;
 import android.content.Context;
 import android.graphics.SurfaceTexture;
-import android.net.Uri;
 import android.os.Handler;
-import android.os.Parcel;
 import android.os.PersistableBundle;
 import android.view.Surface;
 import android.view.SurfaceHolder;
@@ -34,7 +32,6 @@
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
-import java.util.ArrayList;
 import java.util.List;
 import java.util.Map;
 import java.util.UUID;
@@ -241,47 +238,6 @@
         return new MediaPlayer2Impl(context);
     }
 
-    private static final String[] decodeMediaPlayer2Uri(String location) {
-        Uri uri = Uri.parse(location);
-        if (!"mediaplayer2".equals(uri.getScheme())) {
-            return new String[] {location};
-        }
-
-        List<String> uris = uri.getQueryParameters("uri");
-        if (uris.isEmpty()) {
-            return new String[] {location};
-        }
-
-        List<String> keys = uri.getQueryParameters("key");
-        List<String> values = uri.getQueryParameters("value");
-        if (keys.size() != values.size()) {
-            return new String[] {uris.get(0)};
-        }
-
-        List<String> ls = new ArrayList();
-        ls.add(uris.get(0));
-        for (int i = 0; i < keys.size() ; i++) {
-            ls.add(keys.get(i));
-            ls.add(values.get(i));
-        }
-
-        return ls.toArray(new String[ls.size()]);
-    }
-
-    private static final String encodeMediaPlayer2Uri(String uri, String[] keys, String[] values) {
-        Uri.Builder builder = new Uri.Builder();
-        builder.scheme("mediaplayer2").path("/").appendQueryParameter("uri", uri);
-        if (keys == null || values == null || keys.length != values.length) {
-            return builder.build().toString();
-        }
-        for (int i = 0; i < keys.length ; i++) {
-            builder
-                .appendQueryParameter("key", keys[i])
-                .appendQueryParameter("value", values[i]);
-        }
-        return builder.build().toString();
-    }
-
     /**
      * @hide
      */
@@ -291,12 +247,6 @@
     }
 
     /**
-     * Returns a {@link MediaPlayerBase} implementation which runs based on
-     * this MediaPlayer2 instance.
-     */
-    public abstract MediaPlayerBase getMediaPlayerBase();
-
-    /**
      * Releases the resources held by this {@code MediaPlayer2} object.
      *
      * It is considered good practice to call this method when you're
@@ -342,8 +292,7 @@
      * Starts or resumes playback. If playback had previously been paused,
      * playback will continue from where it was paused. If playback had
      * reached end of stream and been paused, or never started before,
-     * playback will start at the beginning. If the source had not been
-     * prepared, the player will prepare the source and play.
+     * playback will start at the beginning.
      *
      * @return a token which can be used to cancel the operation later with {@link #cancel}.
      */
@@ -404,9 +353,8 @@
 
     /**
      * Gets the current buffered media source position received through progressive downloading.
-     * The received buffering percentage indicates how much of the content has been buffered
-     * or played. For example a buffering update of 80 percent when half the content
-     * has already been played indicates that the next 30 percent of the
+     * For example a buffering update of 8000 milliseconds when 5000 milliseconds of the content
+     * has already been played indicates that the next 3000 milliseconds of the
      * content to play has been buffered.
      *
      * @return the current buffered media source position in milliseconds
@@ -416,7 +364,6 @@
     /**
      * MediaPlayer2 has not been prepared or just has been reset.
      * In this state, MediaPlayer2 doesn't fetch data.
-     * @hide
      */
     public static final int PLAYER_STATE_IDLE = 1001;
 
@@ -424,26 +371,23 @@
      * MediaPlayer2 has been just prepared.
      * In this state, MediaPlayer2 just fetches data from media source,
      * but doesn't actively render data.
-     * @hide
      */
     public static final int PLAYER_STATE_PREPARED = 1002;
 
     /**
      * MediaPlayer2 is paused.
-     * In this state, MediaPlayer2 doesn't actively render data.
-     * @hide
+     * In this state, MediaPlayer2 has allocated resources to construct playback
+     * pipeline, but it doesn't actively render data.
      */
     public static final int PLAYER_STATE_PAUSED = 1003;
 
     /**
      * MediaPlayer2 is actively playing back data.
-     * @hide
      */
     public static final int PLAYER_STATE_PLAYING = 1004;
 
     /**
      * MediaPlayer2 has hit some fatal error and cannot continue playback.
-     * @hide
      */
     public static final int PLAYER_STATE_ERROR = 1005;
 
@@ -469,7 +413,7 @@
     /**
      * Sets the audio attributes for this MediaPlayer2.
      * See {@link AudioAttributes} for how to build and configure an instance of this class.
-     * You must call this method before {@link #prepare()} in order
+     * You must call this method before {@link #play()} and {@link #pause()} in order
      * for the audio attributes to become effective thereafter.
      * @param attributes a non-null set of audio attributes
      * @return a token which can be used to cancel the operation later with {@link #cancel}.
@@ -547,7 +491,7 @@
     public abstract Object setPlayerVolume(float volume);
 
     /**
-     * Returns the current volume of this player to this player.
+     * Returns the current volume of this player.
      * Note that it does not take into account the associated stream volume.
      * @return the player volume.
      */
@@ -561,24 +505,9 @@
     }
 
     /**
-     * Create a request parcel which can be routed to the native media
-     * player using {@link #invoke(Parcel, Parcel)}. The Parcel
-     * returned has the proper InterfaceToken set. The caller should
-     * not overwrite that token, i.e it can only append data to the
-     * Parcel.
-     *
-     * @return A parcel suitable to hold a request for the native
-     * player.
-     * {@hide}
-     */
-    public Parcel newRequest() {
-        return null;
-    }
-
-    /**
      * Insert a task in the command queue to help the client to identify whether a batch
      * of commands has been finished. When this command is processed, a notification
-     * {@code EventCallback.onCommandLabelReached} will be fired with the
+     * {@link EventCallback#onCommandLabelReached onCommandLabelReached} will be fired with the
      * given {@code label}.
      *
      * @see EventCallback#onCommandLabelReached
@@ -595,16 +524,13 @@
      * portion of the media.
      *
      * Either a surface holder or surface must be set if a display or video sink
-     * is needed.  Not calling this method or {@link #setSurface(Surface)}
+     * is needed. Not calling this method or {@link #setSurface(Surface)}
      * when playing back a video will result in only the audio track being played.
      * A null surface holder or surface will result in only the audio track being
      * played.
      *
      * @param sh the SurfaceHolder to use for video display
-     * @throws IllegalStateException if the internal player engine has not been
-     * initialized or has been released.
      * @return a token which can be used to cancel the operation later with {@link #cancel}.
-     * @hide
      */
     public abstract Object setDisplay(SurfaceHolder sh);
 
@@ -624,56 +550,44 @@
      *
      * @param surface The {@link Surface} to be used for the video portion of
      * the media.
-     * @throws IllegalStateException if the internal player engine has not been
-     * initialized or has been released.
      * @return a token which can be used to cancel the operation later with {@link #cancel}.
      */
     // This is an asynchronous call.
     public abstract Object setSurface(Surface surface);
 
-    /* Do not change these video scaling mode values below without updating
-     * their counterparts in system/window.h! Please do not forget to update
-     * {@link #isVideoScalingModeSupported} when new video scaling modes
-     * are added.
-     */
     /**
-     * Specifies a video scaling mode. The content is stretched to the
-     * surface rendering area. When the surface has the same aspect ratio
-     * as the content, the aspect ratio of the content is maintained;
-     * otherwise, the aspect ratio of the content is not maintained when video
-     * is being rendered.
-     * There is no content cropping with this video scaling mode.
-     */
-    public static final int VIDEO_SCALING_MODE_SCALE_TO_FIT = 1;
-
-    /**
-     * Specifies a video scaling mode. The content is scaled, maintaining
-     * its aspect ratio. The whole surface area is always used. When the
-     * aspect ratio of the content is the same as the surface, no content
-     * is cropped; otherwise, content is cropped to fit the surface.
-     * @hide
-     */
-    public static final int VIDEO_SCALING_MODE_SCALE_TO_FIT_WITH_CROPPING = 2;
-
-    /**
-     * Sets video scaling mode. To make the target video scaling mode
-     * effective during playback, this method must be called after
-     * data source is set. If not called, the default video
-     * scaling mode is {@link #VIDEO_SCALING_MODE_SCALE_TO_FIT}.
+     * Set the low-level power management behavior for this MediaPlayer2. This
+     * can be used when the MediaPlayer2 is not playing through a SurfaceHolder
+     * set with {@link #setDisplay(SurfaceHolder)} and thus can use the
+     * high-level {@link #setScreenOnWhilePlaying(boolean)} feature.
      *
-     * <p> The supported video scaling modes are:
-     * <ul>
-     * <li> {@link #VIDEO_SCALING_MODE_SCALE_TO_FIT}
-     * </ul>
+     * <p>This function has the MediaPlayer2 access the low-level power manager
+     * service to control the device's power usage while playing is occurring.
+     * The parameter is a combination of {@link android.os.PowerManager} wake flags.
+     * Use of this method requires {@link android.Manifest.permission#WAKE_LOCK}
+     * permission.
+     * By default, no attempt is made to keep the device awake during playback.
      *
-     * @param mode target video scaling mode. Must be one of the supported
-     * video scaling modes; otherwise, IllegalArgumentException will be thrown.
+     * @param context the Context to use
+     * @param mode    the power/wake mode to set
      * @return a token which can be used to cancel the operation later with {@link #cancel}.
-     *
-     * @see MediaPlayer2#VIDEO_SCALING_MODE_SCALE_TO_FIT
-     * @hide
+     * @see android.os.PowerManager
      */
-    public abstract Object setVideoScalingMode(int mode);
+    // This is an asynchronous call.
+    public abstract Object setWakeMode(Context context, int mode);
+
+    /**
+     * Control whether we should use the attached SurfaceHolder to keep the
+     * screen on while video playback is occurring.  This is the preferred
+     * method over {@link #setWakeMode} where possible, since it doesn't
+     * require that the application have permission for low-level wake lock
+     * access.
+     *
+     * @param screenOn Supply true to keep the screen on, false to allow it to turn off.
+     * @return a token which can be used to cancel the operation later with {@link #cancel}.
+     */
+    // This is an asynchronous call.
+    public abstract Object setScreenOnWhilePlaying(boolean screenOn);
 
     /**
      * Cancels a pending command.
@@ -702,7 +616,7 @@
      * @return true if succesful, false if the specified {@link AudioDeviceInfo} is non-null and
      * does not correspond to a valid audio device.
      */
-    // This is an asynchronous call.
+    // This is a synchronous call.
     @Override
     public abstract boolean setPreferredDevice(AudioDeviceInfo deviceInfo);
 
@@ -747,36 +661,6 @@
             AudioRouting.OnRoutingChangedListener listener);
 
     /**
-     * Set the low-level power management behavior for this MediaPlayer2.
-     *
-     * <p>This function has the MediaPlayer2 access the low-level power manager
-     * service to control the device's power usage while playing is occurring.
-     * The parameter is a combination of {@link android.os.PowerManager} wake flags.
-     * Use of this method requires {@link android.Manifest.permission#WAKE_LOCK}
-     * permission.
-     * By default, no attempt is made to keep the device awake during playback.
-     *
-     * @param context the Context to use
-     * @param mode    the power/wake mode to set
-     * @see android.os.PowerManager
-     * @hide
-     */
-    public abstract void setWakeMode(Context context, int mode);
-
-    /**
-     * Control whether we should use the attached SurfaceHolder to keep the
-     * screen on while video playback is occurring.  This is the preferred
-     * method over {@link #setWakeMode} where possible, since it doesn't
-     * require that the application have permission for low-level wake lock
-     * access.
-     *
-     * @param screenOn Supply true to keep the screen on, false to allow it
-     * to turn off.
-     * @hide
-     */
-    public abstract void setScreenOnWhilePlaying(boolean screenOn);
-
-    /**
      * Returns the width of the video.
      *
      * @return the width of the video, or 0 if there is no video,
@@ -1719,18 +1603,21 @@
      */
     public static final int CALL_COMPLETED_SET_BUFFERING_PARAMS = 31;
 
-    /** The player just completed a call {@link #setVideoScalingMode}.
-     * @see EventCallback#onCallCompleted
-     * @hide
-     */
-    public static final int CALL_COMPLETED_SET_VIDEO_SCALING_MODE = 32;
-
     /** The player just completed a call {@link #setDisplay}.
      * @see EventCallback#onCallCompleted
-     * @hide
      */
     public static final int CALL_COMPLETED_SET_DISPLAY = 33;
 
+    /** The player just completed a call {@link #setWakeMode}.
+     * @see EventCallback#onCallCompleted
+     */
+    public static final int CALL_COMPLETED_SET_WAKE_MODE = 34;
+
+    /** The player just completed a call {@link #setScreenOnWhilePlaying}.
+     * @see EventCallback#onCallCompleted
+     */
+    public static final int CALL_COMPLETED_SET_SCREEN_ON_WHILE_PLAYING = 35;
+
     /**
      * The start of the methods which have separate call complete callback.
      * @hide
@@ -1776,8 +1663,9 @@
             CALL_COMPLETED_SKIP_TO_NEXT,
             CALL_COMPLETED_CLEAR_NEXT_DATA_SOURCES,
             CALL_COMPLETED_SET_BUFFERING_PARAMS,
-            CALL_COMPLETED_SET_VIDEO_SCALING_MODE,
             CALL_COMPLETED_SET_DISPLAY,
+            CALL_COMPLETED_SET_WAKE_MODE,
+            CALL_COMPLETED_SET_SCREEN_ON_WHILE_PLAYING,
             CALL_COMPLETED_NOTIFY_WHEN_COMMAND_LABEL_REACHED,
             CALL_COMPLETED_PREPARE_DRM,
     })
diff --git a/media/java/android/media/MediaPlayer2Impl.java b/media/java/android/media/MediaPlayer2Impl.java
index ef8db1d..6697a4f 100644
--- a/media/java/android/media/MediaPlayer2Impl.java
+++ b/media/java/android/media/MediaPlayer2Impl.java
@@ -19,11 +19,11 @@
 import android.annotation.CallbackExecutor;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
-import android.content.ContentProvider;
+import android.app.ActivityManager;
+import android.app.ActivityManager.RunningAppProcessInfo;
 import android.content.ContentResolver;
 import android.content.Context;
 import android.content.res.AssetFileDescriptor;
-import android.graphics.SurfaceTexture;
 import android.graphics.Rect;
 import android.media.MediaPlayer2Proto.PlayerMessage;
 import android.media.MediaPlayer2Proto.Value;
@@ -34,8 +34,6 @@
 import android.os.Message;
 import android.os.PersistableBundle;
 import android.os.PowerManager;
-import android.os.SystemProperties;
-import android.provider.Settings;
 import android.util.ArrayMap;
 import android.util.Log;
 import android.util.Pair;
@@ -78,18 +76,22 @@
         native_init();
     }
 
+    private static final int NEXT_SOURCE_STATE_ERROR = -1;
+    private static final int NEXT_SOURCE_STATE_INIT = 0;
+    private static final int NEXT_SOURCE_STATE_PREPARING = 1;
+    private static final int NEXT_SOURCE_STATE_PREPARED = 2;
+
     private final static String TAG = "MediaPlayer2Impl";
 
     private Context mContext;
 
-    private long mNativeContext; // accessed by native methods
+    private long mNativeContext;  // accessed by native methods
     private long mNativeSurfaceTexture;  // accessed by native methods
-    private int mListenerContext; // accessed by native methods
+    private int mListenerContext;  // accessed by native methods
     private SurfaceHolder mSurfaceHolder;
     private PowerManager.WakeLock mWakeLock = null;
     private boolean mScreenOnWhilePlaying;
     private boolean mStayAwake;
-    private int mStreamType = AudioManager.USE_DEFAULT_STREAM_TYPE;
 
     private final Object mSrcLock = new Object();
     //--- guarded by |mSrcLock| start
@@ -134,7 +136,7 @@
 
     /**
      * Default constructor.
-     * <p>When done with the MediaPlayer2Impl, you should call  {@link #close()},
+     * <p>When done with the MediaPlayer2Impl, you should call {@link #close()},
      * to free the resources. If not released, too many MediaPlayer2Impl instances may
      * result in an exception.</p>
      */
@@ -152,45 +154,11 @@
     }
 
     @Override
-    public MediaPlayerBase getMediaPlayerBase() {
-        return null;
-    }
-
-    /**
-     * Releases the resources held by this {@code MediaPlayer2} object.
-     *
-     * It is considered good practice to call this method when you're
-     * done using the MediaPlayer2. In particular, whenever an Activity
-     * of an application is paused (its onPause() method is called),
-     * or stopped (its onStop() method is called), this method should be
-     * invoked to release the MediaPlayer2 object, unless the application
-     * has a special need to keep the object around. In addition to
-     * unnecessary resources (such as memory and instances of codecs)
-     * being held, failure to call this method immediately if a
-     * MediaPlayer2 object is no longer needed may also lead to
-     * continuous battery consumption for mobile devices, and playback
-     * failure for other applications if no multiple instances of the
-     * same codec are supported on a device. Even if multiple instances
-     * of the same codec are supported, some performance degradation
-     * may be expected when unnecessary multiple instances are used
-     * at the same time.
-     *
-     * {@code close()} may be safely called after a prior {@code close()}.
-     * This class implements the Java {@code AutoCloseable} interface and
-     * may be used with try-with-resources.
-     */
-    @Override
     public void close() {
         super.close();
         release();
     }
 
-    /**
-     * Starts or resumes playback. If playback had previously been paused,
-     * playback will continue from where it was paused. If playback had
-     * been stopped, or never started before, playback will start at the
-     * beginning.
-     */
     @Override
     public Object play() {
         return addTask(new Task(CALL_COMPLETED_PLAY, false) {
@@ -204,14 +172,6 @@
 
     private native void _start() throws IllegalStateException;
 
-    /**
-     * Prepares the player for playback, asynchronously.
-     *
-     * After setting the datasource and the display surface, you need to either
-     * call prepare(). For streams, you should call prepare(),
-     * which returns immediately, rather than blocking until enough data has been
-     * buffered.
-     */
     @Override
     public Object prepare() {
         return addTask(new Task(CALL_COMPLETED_PREPARE, true) {
@@ -224,9 +184,6 @@
 
     public native void _prepare();
 
-    /**
-     * Pauses playback. Call play() to resume.
-     */
     @Override
     public Object pause() {
         return addTask(new Task(CALL_COMPLETED_PAUSE, false) {
@@ -241,11 +198,6 @@
 
     private native void _pause() throws IllegalStateException;
 
-    /**
-     * Tries to play next data source if applicable.
-     *
-     * @throws IllegalStateException if it is called in an invalid state
-     */
     @Override
     public Object skipToNext() {
         return addTask(new Task(CALL_COMPLETED_SKIP_TO_NEXT, false) {
@@ -259,32 +211,12 @@
         });
     }
 
-    /**
-     * Gets the current playback position.
-     *
-     * @return the current position in milliseconds
-     */
     @Override
     public native long getCurrentPosition();
 
-    /**
-     * Gets the duration of the file.
-     *
-     * @return the duration in milliseconds, if no duration is available
-     *         (for example, if streaming live content), -1 is returned.
-     */
     @Override
     public native long getDuration();
 
-    /**
-     * Gets the current buffered media source position received through progressive downloading.
-     * The received buffering percentage indicates how much of the content has been buffered
-     * or played. For example a buffering update of 80 percent when half the content
-     * has already been played indicates that the next 30 percent of the
-     * content to play has been buffered.
-     *
-     * @return the current buffered media source position in milliseconds
-     */
     @Override
     public long getBufferedPosition() {
         // Use cached buffered percent for now.
@@ -298,14 +230,6 @@
 
     private native int native_getState();
 
-    /**
-     * Sets the audio attributes for this MediaPlayer2.
-     * See {@link AudioAttributes} for how to build and configure an instance of this class.
-     * You must call this method before {@link #prepare()} in order
-     * for the audio attributes to become effective thereafter.
-     * @param attributes a non-null set of audio attributes
-     * @throws IllegalArgumentException if the attributes are null or invalid.
-     */
     @Override
     public Object setAudioAttributes(@NonNull AudioAttributes attributes) {
         return addTask(new Task(CALL_COMPLETED_SET_AUDIO_ATTRIBUTES, false) {
@@ -326,11 +250,6 @@
         return attributes;
     }
 
-    /**
-     * Sets the data source as described by a DataSourceDesc.
-     *
-     * @param dsd the descriptor of data source you want to play
-     */
     @Override
     public Object setDataSource(@NonNull DataSourceDesc dsd) {
         return addTask(new Task(CALL_COMPLETED_SET_DATA_SOURCE, false) {
@@ -351,12 +270,6 @@
         });
     }
 
-    /**
-     * Sets a single data source as described by a DataSourceDesc which will be played
-     * after current data source is finished.
-     *
-     * @param dsd the descriptor of data source you want to play after current one
-     */
     @Override
     public Object setNextDataSource(@NonNull DataSourceDesc dsd) {
         return addTask(new Task(CALL_COMPLETED_SET_NEXT_DATA_SOURCE, false) {
@@ -374,11 +287,6 @@
         });
     }
 
-    /**
-     * Sets a list of data sources to be played sequentially after current data source is done.
-     *
-     * @param dsds the list of data sources you want to play after current one
-     */
     @Override
     public Object setNextDataSources(@NonNull List<DataSourceDesc> dsds) {
         return addTask(new Task(CALL_COMPLETED_SET_NEXT_DATA_SOURCES, false) {
@@ -428,16 +336,11 @@
         }
     }
 
-    /**
-     * Configures the player to loop on the current data source.
-     * @param loop true if the current data source is meant to loop.
-     */
     @Override
     public Object loopCurrent(boolean loop) {
         return addTask(new Task(CALL_COMPLETED_LOOP_CURRENT, false) {
             @Override
             void process() {
-                // TODO: set the looping mode, send notification
                 setLooping(loop);
             }
         });
@@ -445,57 +348,29 @@
 
     private native void setLooping(boolean looping);
 
-    /**
-     * Sets the volume of the audio of the media to play, expressed as a linear multiplier
-     * on the audio samples.
-     * Note that this volume is specific to the player, and is separate from stream volume
-     * used across the platform.<br>
-     * A value of 0.0f indicates muting, a value of 1.0f is the nominal unattenuated and unamplified
-     * gain. See {@link #getMaxPlayerVolume()} for the volume range supported by this player.
-     * @param volume a value between 0.0f and {@link #getMaxPlayerVolume()}.
-     */
     @Override
     public Object setPlayerVolume(float volume) {
         return addTask(new Task(CALL_COMPLETED_SET_PLAYER_VOLUME, false) {
             @Override
             void process() {
                 mVolume = volume;
-                _setVolume(volume);
+                native_setVolume(volume);
             }
         });
     }
 
-    private native void _setVolume(float volume);
+    private native void native_setVolume(float volume);
 
-    /**
-     * Returns the current volume of this player to this player.
-     * Note that it does not take into account the associated stream volume.
-     * @return the player volume.
-     */
     @Override
     public float getPlayerVolume() {
         return mVolume;
     }
 
-    /**
-     * @return the maximum volume that can be used in {@link #setPlayerVolume(float)}.
-     */
     @Override
     public float getMaxPlayerVolume() {
         return 1.0f;
     }
 
-    private static final int NEXT_SOURCE_STATE_ERROR = -1;
-    private static final int NEXT_SOURCE_STATE_INIT = 0;
-    private static final int NEXT_SOURCE_STATE_PREPARING = 1;
-    private static final int NEXT_SOURCE_STATE_PREPARED = 2;
-
-    /*
-     * Update the MediaPlayer2Impl SurfaceTexture.
-     * Call after setting a new display surface.
-     */
-    private native void _setVideoSurface(Surface surface);
-
     /* Do not change these values (starting with INVOKE_ID) without updating
      * their counterparts in include/media/mediaplayer2.h!
      */
@@ -504,7 +379,6 @@
     private static final int INVOKE_ID_ADD_EXTERNAL_SOURCE_FD = 3;
     private static final int INVOKE_ID_SELECT_TRACK = 4;
     private static final int INVOKE_ID_DESELECT_TRACK = 5;
-    private static final int INVOKE_ID_SET_VIDEO_SCALE_MODE = 6;
     private static final int INVOKE_ID_GET_SELECTED_TRACK = 7;
 
     /**
@@ -518,7 +392,7 @@
      * native player.
      */
     private PlayerMessage invoke(PlayerMessage msg) {
-        byte[] ret = _invoke(msg.toByteArray());
+        byte[] ret = native_invoke(msg.toByteArray());
         if (ret == null) {
             return null;
         }
@@ -529,7 +403,7 @@
         }
     }
 
-    private native byte[] _invoke(byte[] request);
+    private native byte[] native_invoke(byte[] request);
 
     @Override
     public Object notifyWhenCommandLabelReached(Object label) {
@@ -559,7 +433,7 @@
                 } else {
                     surface = null;
                 }
-                _setVideoSurface(surface);
+                native_setVideoSurface(surface);
                 updateSurfaceScreenOn();
             }
         });
@@ -574,49 +448,13 @@
                     Log.w(TAG, "setScreenOnWhilePlaying(true) is ineffective for Surface");
                 }
                 mSurfaceHolder = null;
-                _setVideoSurface(surface);
+                native_setVideoSurface(surface);
                 updateSurfaceScreenOn();
             }
         });
     }
 
-    /**
-     * Sets video scaling mode. To make the target video scaling mode
-     * effective during playback, this method must be called after
-     * data source is set. If not called, the default video
-     * scaling mode is {@link #VIDEO_SCALING_MODE_SCALE_TO_FIT}.
-     *
-     * <p> The supported video scaling modes are:
-     * <ul>
-     * <li> {@link #VIDEO_SCALING_MODE_SCALE_TO_FIT}
-     * <li> {@link #VIDEO_SCALING_MODE_SCALE_TO_FIT_WITH_CROPPING}
-     * </ul>
-     *
-     * @param mode target video scaling mode. Must be one of the supported
-     * video scaling modes; otherwise, IllegalArgumentException will be thrown.
-     *
-     * @see MediaPlayer2#VIDEO_SCALING_MODE_SCALE_TO_FIT
-     * @see MediaPlayer2#VIDEO_SCALING_MODE_SCALE_TO_FIT_WITH_CROPPING
-     * @hide
-     */
-    @Override
-    public Object setVideoScalingMode(int mode) {
-        return addTask(new Task(CALL_COMPLETED_SET_VIDEO_SCALING_MODE, false) {
-            @Override
-            void process() {
-                if (!isVideoScalingModeSupported(mode)) {
-                    final String msg = "Scaling mode " + mode + " is not supported";
-                    throw new IllegalArgumentException(msg);
-                }
-                PlayerMessage request = PlayerMessage.newBuilder()
-                        .addValues(Value.newBuilder()
-                                .setInt32Value(INVOKE_ID_SET_VIDEO_SCALE_MODE))
-                        .addValues(Value.newBuilder().setInt32Value(mode))
-                        .build();
-                invoke(request);
-            }
-        });
-    }
+    private native void native_setVideoSurface(Surface surface);
 
     @Override
     public boolean cancelCommand(Object token) {
@@ -632,26 +470,6 @@
         }
     }
 
-    private Object addTask(Task task) {
-        synchronized (mTaskLock) {
-            mPendingTasks.add(task);
-            processPendingTask_l();
-        }
-        return task;
-    }
-
-    @GuardedBy("mTaskLock")
-    private void processPendingTask_l() {
-        if (mCurrentTask != null) {
-            return;
-        }
-        if (!mPendingTasks.isEmpty()) {
-            Task task = mPendingTasks.remove(0);
-            mCurrentTask = task;
-            mTaskHandler.post(task);
-        }
-    }
-
     private void handleDataSource(boolean isCurrent, @NonNull DataSourceDesc dsd, long srcId)
             throws IOException {
         checkArgument(dsd != null, "the DataSourceDesc cannot be null");
@@ -875,9 +693,7 @@
             boolean isCurrent, long srcId, Media2DataSource dataSource,
             long startPos, long endPos);
 
-    /**
-     * @return true if there is a next data source, false otherwise.
-     */
+    // return true if there is a next data source, false otherwise.
     // This function should be always called on |mHandlerThread|.
     private boolean prepareNextDataSource() {
         if (Looper.myLooper() != mHandlerThread.getLooper()) {
@@ -975,30 +791,11 @@
 
     private native void nativePlayNextDataSource(long srcId);
 
-
-    private int getAudioStreamType() {
-        if (mStreamType == AudioManager.USE_DEFAULT_STREAM_TYPE) {
-            mStreamType = _getAudioStreamType();
-        }
-        return mStreamType;
-    }
-
-    private native int _getAudioStreamType() throws IllegalStateException;
-
-
     //--------------------------------------------------------------------------
     // Explicit Routing
     //--------------------
     private AudioDeviceInfo mPreferredDevice = null;
 
-    /**
-     * Specifies an audio device (via an {@link AudioDeviceInfo} object) to route
-     * the output from this MediaPlayer2.
-     * @param deviceInfo The {@link AudioDeviceInfo} specifying the audio sink or source.
-     *  If deviceInfo is null, default routing is restored.
-     * @return true if succesful, false if the specified {@link AudioDeviceInfo} is non-null and
-     * does not correspond to a valid audio device.
-     */
     @Override
     public boolean setPreferredDevice(AudioDeviceInfo deviceInfo) {
         if (deviceInfo != null && !deviceInfo.isSink()) {
@@ -1014,10 +811,6 @@
         return status;
     }
 
-    /**
-     * Returns the selected output specified by {@link #setPreferredDevice}. Note that this
-     * is not guaranteed to correspond to the actual device being used for playback.
-     */
     @Override
     public AudioDeviceInfo getPreferredDevice() {
         synchronized (this) {
@@ -1025,12 +818,6 @@
         }
     }
 
-    /**
-     * Returns an {@link AudioDeviceInfo} identifying the current routing of this MediaPlayer2
-     * Note: The query is only valid if the MediaPlayer2 is currently playing.
-     * If the player is not playing, the returned device can be null or correspond to previously
-     * selected device when the player was last active.
-     */
     @Override
     public AudioDeviceInfo getRoutedDevice() {
         int deviceId = native_getRoutedDeviceId();
@@ -1047,9 +834,7 @@
         return null;
     }
 
-    /*
-     * Call BEFORE adding a routing callback handler or AFTER removing a routing callback handler.
-     */
+    // Call BEFORE adding a routing callback handler or AFTER removing a routing callback handler.
     @GuardedBy("mRoutingChangeListeners")
     private void enableNativeRoutingCallbacksLocked(boolean enabled) {
         if (mRoutingChangeListeners.size() == 0) {
@@ -1057,23 +842,12 @@
         }
     }
 
-    /**
-     * The list of AudioRouting.OnRoutingChangedListener interfaces added (with
-     * {@link #addOnRoutingChangedListener(android.media.AudioRouting.OnRoutingChangedListener, Handler)}
-     * by an app to receive (re)routing notifications.
-     */
+    // The list of AudioRouting.OnRoutingChangedListener interfaces added with
+    // addOnRoutingChangedListener by an app to receive (re)routing notifications.
     @GuardedBy("mRoutingChangeListeners")
     private ArrayMap<AudioRouting.OnRoutingChangedListener,
             NativeRoutingEventHandlerDelegate> mRoutingChangeListeners = new ArrayMap<>();
 
-    /**
-     * Adds an {@link AudioRouting.OnRoutingChangedListener} to receive notifications of routing
-     * changes on this MediaPlayer2.
-     * @param listener The {@link AudioRouting.OnRoutingChangedListener} interface to receive
-     * notifications of rerouting events.
-     * @param handler  Specifies the {@link Handler} object for the thread on which to execute
-     * the callback. If <code>null</code>, the handler on the main looper will be used.
-     */
     @Override
     public void addOnRoutingChangedListener(AudioRouting.OnRoutingChangedListener listener,
             Handler handler) {
@@ -1087,12 +861,6 @@
         }
     }
 
-    /**
-     * Removes an {@link AudioRouting.OnRoutingChangedListener} which has been previously added
-     * to receive rerouting notifications.
-     * @param listener The previously added {@link AudioRouting.OnRoutingChangedListener} interface
-     * to remove.
-     */
     @Override
     public void removeOnRoutingChangedListener(AudioRouting.OnRoutingChangedListener listener) {
         synchronized (mRoutingChangeListeners) {
@@ -1107,70 +875,59 @@
     private native final int native_getRoutedDeviceId();
     private native final void native_enableDeviceCallback(boolean enabled);
 
-    /**
-     * Set the low-level power management behavior for this MediaPlayer2.  This
-     * can be used when the MediaPlayer2 is not playing through a SurfaceHolder
-     * set with {@link #setDisplay(SurfaceHolder)} and thus can use the
-     * high-level {@link #setScreenOnWhilePlaying(boolean)} feature.
-     *
-     * <p>This function has the MediaPlayer2 access the low-level power manager
-     * service to control the device's power usage while playing is occurring.
-     * The parameter is a combination of {@link android.os.PowerManager} wake flags.
-     * Use of this method requires {@link android.Manifest.permission#WAKE_LOCK}
-     * permission.
-     * By default, no attempt is made to keep the device awake during playback.
-     *
-     * @param context the Context to use
-     * @param mode    the power/wake mode to set
-     * @see android.os.PowerManager
-     * @hide
-     */
     @Override
-    public void setWakeMode(Context context, int mode) {
-        boolean washeld = false;
+    public Object setWakeMode(Context context, int mode) {
+        return addTask(new Task(CALL_COMPLETED_SET_WAKE_MODE, false) {
+            @Override
+            void process() {
+                boolean washeld = false;
 
-        /* Disable persistant wakelocks in media player based on property */
-        if (SystemProperties.getBoolean("audio.offload.ignore_setawake", false) == true) {
-            Log.w(TAG, "IGNORING setWakeMode " + mode);
-            return;
-        }
+                if (mWakeLock != null) {
+                    if (mWakeLock.isHeld()) {
+                        washeld = true;
+                        mWakeLock.release();
+                    }
+                    mWakeLock = null;
+                }
 
-        if (mWakeLock != null) {
-            if (mWakeLock.isHeld()) {
-                washeld = true;
-                mWakeLock.release();
+                PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
+                ActivityManager am =
+                        (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
+                List<RunningAppProcessInfo> runningAppsProcInfo = am.getRunningAppProcesses();
+                int pid = android.os.Process.myPid();
+                String name = "pid " + String.valueOf(pid);
+                if (runningAppsProcInfo != null) {
+                    for (RunningAppProcessInfo procInfo : runningAppsProcInfo) {
+                        if (procInfo.pid == pid) {
+                            name = procInfo.processName;
+                            break;
+                        }
+                    }
+                }
+                mWakeLock = pm.newWakeLock(mode | PowerManager.ON_AFTER_RELEASE, name);
+                mWakeLock.setReferenceCounted(false);
+                if (washeld) {
+                    mWakeLock.acquire();
+                }
             }
-            mWakeLock = null;
-        }
-
-        PowerManager pm = (PowerManager)context.getSystemService(Context.POWER_SERVICE);
-        mWakeLock = pm.newWakeLock(mode|PowerManager.ON_AFTER_RELEASE, MediaPlayer2Impl.class.getName());
-        mWakeLock.setReferenceCounted(false);
-        if (washeld) {
-            mWakeLock.acquire();
-        }
+        });
     }
 
-    /**
-     * Control whether we should use the attached SurfaceHolder to keep the
-     * screen on while video playback is occurring.  This is the preferred
-     * method over {@link #setWakeMode} where possible, since it doesn't
-     * require that the application have permission for low-level wake lock
-     * access.
-     *
-     * @param screenOn Supply true to keep the screen on, false to allow it
-     * to turn off.
-     * @hide
-     */
     @Override
-    public void setScreenOnWhilePlaying(boolean screenOn) {
-        if (mScreenOnWhilePlaying != screenOn) {
-            if (screenOn && mSurfaceHolder == null) {
-                Log.w(TAG, "setScreenOnWhilePlaying(true) is ineffective without a SurfaceHolder");
+    public Object setScreenOnWhilePlaying(boolean screenOn) {
+        return addTask(new Task(CALL_COMPLETED_SET_SCREEN_ON_WHILE_PLAYING, false) {
+            @Override
+            void process() {
+                if (mScreenOnWhilePlaying != screenOn) {
+                    if (screenOn && mSurfaceHolder == null) {
+                        Log.w(TAG, "setScreenOnWhilePlaying(true) is ineffective"
+                                + " without a SurfaceHolder");
+                    }
+                    mScreenOnWhilePlaying = screenOn;
+                    updateSurfaceScreenOn();
+                }
             }
-            mScreenOnWhilePlaying = screenOn;
-            updateSurfaceScreenOn();
-        }
+        });
     }
 
     private void stayAwake(boolean awake) {
@@ -1191,42 +948,12 @@
         }
     }
 
-    /**
-     * Returns the width of the video.
-     *
-     * @return the width of the video, or 0 if there is no video,
-     * no display surface was set, or the width has not been determined
-     * yet. The {@code EventCallback} can be registered via
-     * {@link #setEventCallback(Executor, EventCallback)} to provide a
-     * notification {@code EventCallback.onVideoSizeChanged} when the width
-     * is available.
-     */
     @Override
     public native int getVideoWidth();
 
-    /**
-     * Returns the height of the video.
-     *
-     * @return the height of the video, or 0 if there is no video,
-     * no display surface was set, or the height has not been determined
-     * yet. The {@code EventCallback} can be registered via
-     * {@link #setEventCallback(Executor, EventCallback)} to provide a
-     * notification {@code EventCallback.onVideoSizeChanged} when the height
-     * is available.
-     */
     @Override
     public native int getVideoHeight();
 
-    /**
-     * Return Metrics data about the current player.
-     *
-     * @return a {@link PersistableBundle} containing the set of attributes and values
-     * available for the media being handled by this instance of MediaPlayer2
-     * The attributes are descibed in {@link MetricsConstants}.
-     *
-     *  Additional vendor-specific fields may also be present in
-     *  the return value.
-     */
     @Override
     public PersistableBundle getMetrics() {
         PersistableBundle bundle = native_getMetrics();
@@ -1235,27 +962,9 @@
 
     private native PersistableBundle native_getMetrics();
 
-    /**
-     * Checks whether the MediaPlayer2 is playing.
-     *
-     * @return true if currently playing, false otherwise
-     * @throws IllegalStateException if the internal player engine has not been
-     * initialized or has been released.
-     * @hide
-     */
     @Override
     public native boolean isPlaying();
 
-    /**
-     * Gets the current buffering management params used by the source component.
-     * Calling it only after {@code setDataSource} has been called.
-     * Each type of data source might have different set of default params.
-     *
-     * @return the current buffering management params used by the source component.
-     * @throws IllegalStateException if the internal player engine has not been
-     * initialized, or {@code setDataSource} has not been called.
-     * @hide
-     */
     @Override
     @NonNull
     public native BufferingParams getBufferingParams();
@@ -3388,13 +3097,6 @@
 
     // Modular DRM end
 
-    /*
-     * Test whether a given video scaling mode is supported.
-     */
-    private boolean isVideoScalingModeSupported(int mode) {
-        return (mode == VIDEO_SCALING_MODE_SCALE_TO_FIT ||
-                mode == VIDEO_SCALING_MODE_SCALE_TO_FIT_WITH_CROPPING);
-    }
 
     private static class TimedTextUtil {
         // These keys must be in sync with the keys in TextDescription2.h
@@ -3450,6 +3152,26 @@
         }
     }
 
+    private Object addTask(Task task) {
+        synchronized (mTaskLock) {
+            mPendingTasks.add(task);
+            processPendingTask_l();
+        }
+        return task;
+    }
+
+    @GuardedBy("mTaskLock")
+    private void processPendingTask_l() {
+        if (mCurrentTask != null) {
+            return;
+        }
+        if (!mPendingTasks.isEmpty()) {
+            Task task = mPendingTasks.remove(0);
+            mCurrentTask = task;
+            mTaskHandler.post(task);
+        }
+    }
+
     private abstract class Task implements Runnable {
         private final int mMediaCallType;
         private final boolean mNeedToWaitForEventToComplete;
diff --git a/media/jni/android_media_MediaDrm.cpp b/media/jni/android_media_MediaDrm.cpp
index 7cf8828..8637ada 100644
--- a/media/jni/android_media_MediaDrm.cpp
+++ b/media/jni/android_media_MediaDrm.cpp
@@ -543,7 +543,7 @@
     import jav.util.Iterator;
 
     HashMap<k, v> hm;
-    Set<Entry<k, v> > s = hm.entrySet();
+    Set<Entry<k, v>> s = hm.entrySet();
     Iterator i = s.iterator();
     Entry e = s.next();
 */
@@ -613,10 +613,10 @@
 }
 
 static jobject ListOfVectorsToArrayListOfByteArray(JNIEnv *env,
-                                                   List<Vector<uint8_t> > list) {
+                                                   List<Vector<uint8_t>> list) {
     jclass clazz = gFields.arraylistClassId;
     jobject arrayList = env->NewObject(clazz, gFields.arraylist.init);
-    List<Vector<uint8_t> >::iterator iter = list.begin();
+    List<Vector<uint8_t>>::iterator iter = list.begin();
     while (iter != list.end()) {
         jbyteArray byteArray = VectorToJByteArray(env, *iter);
         env->CallBooleanMethod(arrayList, gFields.arraylist.add, byteArray);
@@ -1216,7 +1216,7 @@
         return NULL;
     }
 
-    List<Vector<uint8_t> > secureStops;
+    List<Vector<uint8_t>> secureStops;
 
     status_t err = drm->getSecureStops(secureStops);
 
@@ -1237,7 +1237,7 @@
         return NULL;
     }
 
-    List<Vector<uint8_t> > secureStopIds;
+    List<Vector<uint8_t>> secureStopIds;
 
     status_t err = drm->getSecureStopIds(secureStopIds);
 
diff --git a/media/jni/android_media_MediaPlayer2.cpp b/media/jni/android_media_MediaPlayer2.cpp
index 61c28ed..f60e7da 100644
--- a/media/jni/android_media_MediaPlayer2.cpp
+++ b/media/jni/android_media_MediaPlayer2.cpp
@@ -893,20 +893,6 @@
     process_media_player_call( env, thiz, mp->reset(), NULL, NULL );
 }
 
-static jint
-android_media_MediaPlayer2_getAudioStreamType(JNIEnv *env, jobject thiz)
-{
-    sp<MediaPlayer2> mp = getMediaPlayer(env, thiz);
-    if (mp == NULL ) {
-        jniThrowException(env, "java/lang/IllegalStateException", NULL);
-        return 0;
-    }
-    audio_stream_type_t streamtype;
-    process_media_player_call( env, thiz, mp->getAudioStreamType(&streamtype), NULL, NULL );
-    ALOGV("getAudioStreamType: %d (streamtype)", streamtype);
-    return (jint) streamtype;
-}
-
 static jboolean
 android_media_MediaPlayer2_setParameter(JNIEnv *env, jobject thiz, jint key, jobject)
 {
@@ -1467,7 +1453,7 @@
         (void *)android_media_MediaPlayer2_handleDataSourceCallback
     },
     {"nativePlayNextDataSource", "(J)V",                        (void *)android_media_MediaPlayer2_playNextDataSource},
-    {"_setVideoSurface",    "(Landroid/view/Surface;)V",        (void *)android_media_MediaPlayer2_setVideoSurface},
+    {"native_setVideoSurface", "(Landroid/view/Surface;)V",     (void *)android_media_MediaPlayer2_setVideoSurface},
     {"getBufferingParams", "()Landroid/media/BufferingParams;", (void *)android_media_MediaPlayer2_getBufferingParams},
     {"_setBufferingParams", "(Landroid/media/BufferingParams;)V", (void *)android_media_MediaPlayer2_setBufferingParams},
     {"_prepare",            "()V",                              (void *)android_media_MediaPlayer2_prepare},
@@ -1487,13 +1473,12 @@
     {"getDuration",         "()J",                              (void *)android_media_MediaPlayer2_getDuration},
     {"_release",            "()V",                              (void *)android_media_MediaPlayer2_release},
     {"_reset",              "()V",                              (void *)android_media_MediaPlayer2_reset},
-    {"_getAudioStreamType", "()I",                              (void *)android_media_MediaPlayer2_getAudioStreamType},
     {"setParameter",        "(ILjava/lang/Object;)Z",          (void *)android_media_MediaPlayer2_setParameter},
     {"getParameter",        "(I)Ljava/lang/Object;",           (void *)android_media_MediaPlayer2_getParameter},
     {"setLooping",          "(Z)V",                             (void *)android_media_MediaPlayer2_setLooping},
     {"isLooping",           "()Z",                              (void *)android_media_MediaPlayer2_isLooping},
-    {"_setVolume",          "(F)V",                             (void *)android_media_MediaPlayer2_setVolume},
-    {"_invoke",             "([B)[B",                           (void *)android_media_MediaPlayer2_invoke},
+    {"native_setVolume",    "(F)V",                             (void *)android_media_MediaPlayer2_setVolume},
+    {"native_invoke",       "([B)[B",                           (void *)android_media_MediaPlayer2_invoke},
     {"native_init",         "()V",                              (void *)android_media_MediaPlayer2_native_init},
     {"native_setup",        "(Ljava/lang/Object;)V",            (void *)android_media_MediaPlayer2_native_setup},
     {"native_finalize",     "()V",                              (void *)android_media_MediaPlayer2_native_finalize},
diff --git a/services/autofill/java/com/android/server/intelligence/ContentCaptureSession.java b/services/autofill/java/com/android/server/intelligence/ContentCaptureSession.java
index a437a39..9cab1ed 100644
--- a/services/autofill/java/com/android/server/intelligence/ContentCaptureSession.java
+++ b/services/autofill/java/com/android/server/intelligence/ContentCaptureSession.java
@@ -23,12 +23,15 @@
 import android.service.intelligence.InteractionContext;
 import android.service.intelligence.InteractionSessionId;
 import android.util.Slog;
+import android.view.intelligence.ContentCaptureEvent;
 
 import com.android.internal.annotations.GuardedBy;
+import com.android.internal.util.Preconditions;
 import com.android.server.AbstractRemoteService;
 import com.android.server.intelligence.RemoteIntelligenceService.RemoteIntelligenceServiceCallbacks;
 
 import java.io.PrintWriter;
+import java.util.List;
 
 final class ContentCaptureSession implements RemoteIntelligenceServiceCallbacks {
 
@@ -45,15 +48,15 @@
     ContentCaptureSession(@NonNull Context context, int userId, @NonNull Object lock,
             @NonNull IBinder activityToken, @NonNull IntelligencePerUserService service,
             @NonNull ComponentName serviceComponentName, @NonNull ComponentName appComponentName,
-            int taskId, int displayId, int localSessionId, int globalSessionId, int flags,
+            int taskId, int displayId, @NonNull InteractionSessionId sessionId, int flags,
             boolean bindInstantServiceAllowed, boolean verbose) {
         mLock = lock;
         mActivityToken = activityToken;
         mService = service;
+        mId = Preconditions.checkNotNull(sessionId);
         mRemoteService = new RemoteIntelligenceService(context,
                 IntelligenceService.SERVICE_INTERFACE, serviceComponentName, userId, this,
                 bindInstantServiceAllowed, verbose);
-        mId = new InteractionSessionId(globalSessionId, localSessionId);
         mInterationContext = new InteractionContext(appComponentName, taskId, displayId, flags);
     }
 
@@ -66,6 +69,13 @@
     }
 
     /**
+     * Notifies the {@link IntelligenceService} of a batch of events.
+     */
+    public void sendEventsLocked(List<ContentCaptureEvent> events) {
+        mRemoteService.onContentCaptureEventsRequest(mId, events);
+    }
+
+    /**
      * Cleans up the session and remove itself from the service.
      *
      * @param notifyRemoteService whether it should trigger a {@link
@@ -79,7 +89,7 @@
                 mRemoteService.onSessionLifecycleRequest(/* context= */ null, mId);
             }
         } finally {
-            mService.removeSessionLocked(mInterationContext.getActivityComponent());
+            mService.removeSessionLocked(mId);
         }
     }
 
@@ -95,27 +105,25 @@
     }
 
     @Override // from RemoteScreenObservationServiceCallbacks
-    public void onSessionLifecycleRequestFailureOrTimeout(boolean timedOut) {
+    public void onFailureOrTimeout(boolean timedOut) {
         // TODO(b/111276913): log metrics on whether timed out or not
         if (mService.isDebug()) {
-            Slog.d(TAG, "onSessionLifecycleRequestFailure(" + mId + "): timed out=" + timedOut);
+            Slog.d(TAG, "onFailureOrTimeout(" + mId + "): timed out=" + timedOut);
         }
         synchronized (mLock) {
             removeSelfLocked(/* notifyRemoteService= */ false);
         }
     }
 
-    /**
-     * Gets global id, unique per {@link IntelligencePerUserService}.
-     */
-    public int getGlobalSessionId() {
-        return mId.getGlobalId();
-    }
-
     @GuardedBy("mLock")
     public void dumpLocked(@NonNull String prefix, @NonNull PrintWriter pw) {
         pw.print(prefix); pw.print("id: ");  mId.dump(pw); pw.println();
         pw.print(prefix); pw.print("context: ");  mInterationContext.dump(pw); pw.println();
         pw.print(prefix); pw.print("activity token: "); pw.println(mActivityToken);
     }
+
+    @Override
+    public String toString() {
+        return "ContentCaptureSession[id=" + mId.getValue() + ", act=" + mActivityToken + "]";
+    }
 }
diff --git a/services/autofill/java/com/android/server/intelligence/IntelligenceManagerService.java b/services/autofill/java/com/android/server/intelligence/IntelligenceManagerService.java
index 4ea9036..43d4a44 100644
--- a/services/autofill/java/com/android/server/intelligence/IntelligenceManagerService.java
+++ b/services/autofill/java/com/android/server/intelligence/IntelligenceManagerService.java
@@ -24,6 +24,8 @@
 import android.content.Context;
 import android.os.IBinder;
 import android.os.UserManager;
+import android.service.intelligence.InteractionSessionId;
+import android.view.intelligence.ContentCaptureEvent;
 import android.view.intelligence.IIntelligenceManager;
 
 import com.android.internal.annotations.GuardedBy;
@@ -35,6 +37,7 @@
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
+import java.util.List;
 
 /**
  * A service used to observe the contents of the screen.
@@ -85,9 +88,11 @@
 
         @Override
         public void startSession(int userId, @NonNull IBinder activityToken,
-                @NonNull ComponentName componentName, int localSessionId, int flags,
-                @NonNull IResultReceiver result) {
+                @NonNull ComponentName componentName, @NonNull InteractionSessionId sessionId,
+                int flags, @NonNull IResultReceiver result) {
             Preconditions.checkNotNull(activityToken);
+            Preconditions.checkNotNull(componentName);
+            Preconditions.checkNotNull(sessionId);
 
             // TODO(b/111276913): refactor getTaskIdForActivity() to also return ComponentName,
             // so we don't pass it on startSession (same for Autofill)
@@ -99,19 +104,29 @@
             synchronized (mLock) {
                 final IntelligencePerUserService service = getServiceForUserLocked(userId);
                 service.startSessionLocked(activityToken, componentName, taskId, displayId,
-                        localSessionId, flags, result);
+                        sessionId, flags, result);
             }
         }
 
         @Override
-        public void finishSession(int userId, @NonNull IBinder activityToken,
-                @NonNull ComponentName componentName, int localSessionId, int globalSessionId) {
-            Preconditions.checkNotNull(activityToken);
+        public void sendEvents(int userId, @NonNull InteractionSessionId sessionId,
+                @NonNull List<ContentCaptureEvent> events) {
+            Preconditions.checkNotNull(sessionId);
+            Preconditions.checkNotNull(events);
 
             synchronized (mLock) {
                 final IntelligencePerUserService service = getServiceForUserLocked(userId);
-                service.finishSessionLocked(activityToken, componentName, localSessionId,
-                        globalSessionId);
+                service.sendEventsLocked(sessionId, events);
+            }
+        }
+
+        @Override
+        public void finishSession(int userId, @NonNull InteractionSessionId sessionId) {
+            Preconditions.checkNotNull(sessionId);
+
+            synchronized (mLock) {
+                final IntelligencePerUserService service = getServiceForUserLocked(userId);
+                service.finishSessionLocked(sessionId);
             }
         }
 
diff --git a/services/autofill/java/com/android/server/intelligence/IntelligencePerUserService.java b/services/autofill/java/com/android/server/intelligence/IntelligencePerUserService.java
index b62b239..584b872 100644
--- a/services/autofill/java/com/android/server/intelligence/IntelligencePerUserService.java
+++ b/services/autofill/java/com/android/server/intelligence/IntelligencePerUserService.java
@@ -25,14 +25,18 @@
 import android.content.pm.ServiceInfo;
 import android.os.IBinder;
 import android.os.RemoteException;
+import android.service.intelligence.InteractionSessionId;
 import android.util.ArrayMap;
 import android.util.Slog;
+import android.view.intelligence.ContentCaptureEvent;
+import android.view.intelligence.IntelligenceManager;
 
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.os.IResultReceiver;
 import com.android.server.AbstractPerUserSystemService;
 
 import java.io.PrintWriter;
+import java.util.List;
 
 /**
  * Per-user instance of {@link IntelligenceManagerService}.
@@ -42,11 +46,9 @@
 
     private static final String TAG = "IntelligencePerUserService";
 
-    private static int sNextSessionId;
-
-    // TODO(b/111276913): should key by componentName + taskId or ActivityToken
     @GuardedBy("mLock")
-    private final ArrayMap<ComponentName, ContentCaptureSession> mSessions = new ArrayMap<>();
+    private final ArrayMap<InteractionSessionId, ContentCaptureSession> mSessions =
+            new ArrayMap<>();
 
     // TODO(b/111276913): add mechanism to prune stale sessions, similar to Autofill's
 
@@ -83,8 +85,9 @@
     // TODO(b/111276913): log metrics
     @GuardedBy("mLock")
     public void startSessionLocked(@NonNull IBinder activityToken,
-            @NonNull ComponentName componentName, int taskId, int displayId, int localSessionId,
-            int flags, @NonNull IResultReceiver resultReceiver) {
+            @NonNull ComponentName componentName, int taskId, int displayId,
+            @NonNull InteractionSessionId sessionId, int flags,
+            @NonNull IResultReceiver resultReceiver) {
         final ComponentName serviceComponentName = getServiceComponentName();
         if (serviceComponentName == null) {
             // TODO(b/111276913): this happens when the system service is starting, we should
@@ -94,16 +97,16 @@
             return;
         }
 
-        ContentCaptureSession session = mSessions.get(componentName);
+        ContentCaptureSession session = mSessions.get(sessionId);
         if (session != null) {
             if (mMaster.debug) {
-                Slog.d(TAG, "startSession(): reusing session " + session.getGlobalSessionId()
-                        + " for " + componentName);
+                Slog.d(TAG, "startSession(): reusing session " + sessionId + " for "
+                        + componentName);
             }
             // TODO(b/111276913): check if local ids match and decide what to do if they don't
             // TODO(b/111276913): should we call session.notifySessionStartedLocked() again??
             // if not, move notifySessionStartedLocked() into session constructor
-            sendToClient(resultReceiver, session.getGlobalSessionId());
+            sendToClient(resultReceiver, IntelligenceManager.STATE_ACTIVE);
             return;
         }
 
@@ -111,45 +114,53 @@
         final boolean bindInstantServiceAllowed = false;
 
         session = new ContentCaptureSession(getContext(), mUserId, mLock, activityToken,
-                this, serviceComponentName, componentName, taskId, displayId, localSessionId,
-                ++sNextSessionId, flags, bindInstantServiceAllowed, mMaster.verbose);
+                this, serviceComponentName, componentName, taskId, displayId, sessionId, flags,
+                bindInstantServiceAllowed, mMaster.verbose);
         if (mMaster.verbose) {
-            Slog.v(TAG, "startSession(): new session for " + componentName + "; globalId ="
-                    + session.getGlobalSessionId());
+            Slog.v(TAG, "startSession(): new session for " + componentName + " and id "
+                    + sessionId);
         }
-        mSessions.put(componentName, session);
+        mSessions.put(sessionId, session);
         session.notifySessionStartedLocked();
-        sendToClient(resultReceiver, session.getGlobalSessionId());
+        sendToClient(resultReceiver, IntelligenceManager.STATE_ACTIVE);
     }
 
     // TODO(b/111276913): log metrics
     @GuardedBy("mLock")
-    public void finishSessionLocked(@NonNull IBinder activityToken,
-            @NonNull ComponentName componentName, int localSessionId, int globalSessionId) {
-        final ContentCaptureSession session = mSessions.get(componentName);
+    public void finishSessionLocked(@NonNull InteractionSessionId sessionId) {
+        final ContentCaptureSession session = mSessions.get(sessionId);
         if (session == null) {
-            Slog.w(TAG, "finishSession(): no session for " + componentName);
+            Slog.w(TAG, "finishSession(): no session with id" + sessionId);
             return;
         }
         if (mMaster.verbose) {
-            Slog.v(TAG, "finishSession(): comp=" + componentName + "; globalId ="
-                    + session.getGlobalSessionId());
+            Slog.v(TAG, "finishSession(): " + session);
         }
-        // TODO(b/111276913): check if all arguments match existing session and throw exception if
-        // not. Or just use componentName if we change AIDL to pass just ApplicationToken and
-        // retrieve componentName from AMInternal
         session.removeSelfLocked(true);
     }
 
     @GuardedBy("mLock")
-    public void removeSessionLocked(@NonNull ComponentName key) {
-        mSessions.remove(key);
+    public void sendEventsLocked(@NonNull InteractionSessionId sessionId,
+            @NonNull List<ContentCaptureEvent> events) {
+        final ContentCaptureSession session = mSessions.get(sessionId);
+        if (session == null) {
+            Slog.w(TAG, "sendEvents(): no session for " + sessionId);
+            return;
+        }
+        if (mMaster.verbose) {
+            Slog.v(TAG, "sendEvents(): id=" + sessionId + "; events =" + events.size());
+        }
+        session.sendEventsLocked(events);
+    }
+
+    @GuardedBy("mLock")
+    public void removeSessionLocked(@NonNull InteractionSessionId sessionId) {
+        mSessions.remove(sessionId);
     }
 
     @Override
     protected void dumpLocked(String prefix, PrintWriter pw) {
         super.dumpLocked(prefix, pw);
-        pw.print(prefix); pw.print("next id: "); pw.println(sNextSessionId);
         if (mSessions.isEmpty()) {
             pw.print(prefix); pw.println("no sessions");
         } else {
diff --git a/services/autofill/java/com/android/server/intelligence/RemoteIntelligenceService.java b/services/autofill/java/com/android/server/intelligence/RemoteIntelligenceService.java
index ee66b4e..9d241fb 100644
--- a/services/autofill/java/com/android/server/intelligence/RemoteIntelligenceService.java
+++ b/services/autofill/java/com/android/server/intelligence/RemoteIntelligenceService.java
@@ -27,9 +27,12 @@
 import android.service.intelligence.InteractionSessionId;
 import android.text.format.DateUtils;
 import android.util.Slog;
+import android.view.intelligence.ContentCaptureEvent;
 
 import com.android.server.AbstractRemoteService;
 
+import java.util.List;
+
 final class RemoteIntelligenceService extends AbstractRemoteService {
 
     private static final String TAG = "RemoteIntelligenceService";
@@ -79,44 +82,89 @@
         scheduleRequest(new PendingSessionLifecycleRequest(this, context, sessionId));
     }
 
-    private static final class PendingSessionLifecycleRequest
+    /**
+     * Called by {@link ContentCaptureSession} to send a batch of events to the service.
+     */
+    public void onContentCaptureEventsRequest(@NonNull InteractionSessionId sessionId,
+            @NonNull List<ContentCaptureEvent> events) {
+        cancelScheduledUnbind();
+        scheduleRequest(new PendingOnContentCaptureEventsRequest(this, sessionId, events));
+    }
+
+
+    private abstract static class MyPendingRequest
             extends PendingRequest<RemoteIntelligenceService> {
+        protected final InteractionSessionId mSessionId;
 
-        private final InteractionContext mContext;
-        private final InteractionSessionId mSessionId;
-
-        protected PendingSessionLifecycleRequest(@NonNull RemoteIntelligenceService service,
-                @Nullable InteractionContext context, @NonNull InteractionSessionId sessionId) {
+        private MyPendingRequest(@NonNull RemoteIntelligenceService service,
+                @NonNull InteractionSessionId sessionId) {
             super(service);
-            mContext = context;
             mSessionId = sessionId;
         }
 
         @Override // from PendingRequest
-        public void run() {
+        protected final void onTimeout(RemoteIntelligenceService remoteService) {
+            Slog.w(TAG, "timed out handling " + getClass().getSimpleName() + " for "
+                    + mSessionId);
+            remoteService.mCallbacks.onFailureOrTimeout(/* timedOut= */ true);
+        }
+
+        @Override // from PendingRequest
+        public final void run() {
             final RemoteIntelligenceService remoteService = getService();
             if (remoteService != null) {
                 try {
-                    remoteService.mService.onSessionLifecycle(mContext, mSessionId);
+                    myRun(remoteService);
+                    // We don't expect the service to call us back, so we finish right away.
+                    finish();
                 } catch (RemoteException e) {
-                    Slog.w(TAG, "exception handling PendingSessionLifecycleRequest for "
+                    Slog.w(TAG, "exception handling " + getClass().getSimpleName() + " for "
                             + mSessionId + ": " + e);
-                    remoteService.mCallbacks
-                        .onSessionLifecycleRequestFailureOrTimeout(/* timedOut= */ false);
+                    remoteService.mCallbacks.onFailureOrTimeout(/* timedOut= */ false);
                 }
             }
         }
 
-        @Override // from PendingRequest
-        protected void onTimeout(RemoteIntelligenceService remoteService) {
-            Slog.w(TAG, "timed out handling PendingSessionLifecycleRequest for "
-                    + mSessionId);
-            remoteService.mCallbacks
-                .onSessionLifecycleRequestFailureOrTimeout(/* timedOut= */ true);
+        protected abstract void myRun(@NonNull RemoteIntelligenceService service)
+                throws RemoteException;
+
+    }
+
+    private static final class PendingSessionLifecycleRequest extends MyPendingRequest {
+
+        private final InteractionContext mContext;
+
+        protected PendingSessionLifecycleRequest(@NonNull RemoteIntelligenceService service,
+                @Nullable InteractionContext context, @NonNull InteractionSessionId sessionId) {
+            super(service, sessionId);
+            mContext = context;
+        }
+
+        @Override // from MyPendingRequest
+        public void myRun(@NonNull RemoteIntelligenceService remoteService) throws RemoteException {
+            remoteService.mService.onSessionLifecycle(mContext, mSessionId);
+        }
+    }
+
+    private static final class PendingOnContentCaptureEventsRequest extends MyPendingRequest {
+
+        private final List<ContentCaptureEvent> mEvents;
+
+        protected PendingOnContentCaptureEventsRequest(@NonNull RemoteIntelligenceService service,
+                @NonNull InteractionSessionId sessionId,
+                @NonNull List<ContentCaptureEvent> events) {
+            super(service, sessionId);
+            mEvents = events;
+        }
+
+        @Override // from MyPendingRequest
+        public void myRun(@NonNull RemoteIntelligenceService remoteService) throws RemoteException {
+            remoteService.mService.onContentCaptureEvents(mSessionId, mEvents);
         }
     }
 
     public interface RemoteIntelligenceServiceCallbacks extends VultureCallback {
-        void onSessionLifecycleRequestFailureOrTimeout(boolean timedOut);
+        // To keep it simple, we use the same callback for all failures / timeouts.
+        void onFailureOrTimeout(boolean timedOut);
     }
 }
diff --git a/services/core/java/com/android/server/AbstractRemoteService.java b/services/core/java/com/android/server/AbstractRemoteService.java
index 1d3a34c..181d7fd 100644
--- a/services/core/java/com/android/server/AbstractRemoteService.java
+++ b/services/core/java/com/android/server/AbstractRemoteService.java
@@ -366,11 +366,12 @@
                     mCompleted = true;
                 }
 
-                Slog.w(mTag, "timed out");
                 final S remoteService = mWeakService.get();
                 if (remoteService != null) {
-                    Slog.w(mTag, " timed out after " + service.getRemoteRequestMillis() + " ms");
+                    Slog.w(mTag, "timed out after " + service.getRemoteRequestMillis() + " ms");
                     onTimeout(remoteService);
+                } else {
+                    Slog.w(mTag, "timed out (no service)");
                 }
             };
             mServiceHandler.postAtTime(mTimeoutTrigger,
diff --git a/services/core/java/com/android/server/wm/ActivityMetricsLaunchObserver.java b/services/core/java/com/android/server/wm/ActivityMetricsLaunchObserver.java
new file mode 100644
index 0000000..e3133ef
--- /dev/null
+++ b/services/core/java/com/android/server/wm/ActivityMetricsLaunchObserver.java
@@ -0,0 +1,182 @@
+/*
+ * Copyright (C) 2018 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.wm;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.Intent;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * Observe activity manager launch sequences.
+ *
+ * The activity manager can have at most 1 concurrent launch sequences. Calls to this interface
+ * are ordered by a happens-before relation for each defined state transition (see below).
+ *
+ * When a new launch sequence is made, that sequence is in the {@code INTENT_STARTED} state which
+ * is communicated by the {@link #onIntentStarted} callback. This is a transient state.
+ *
+ * The intent can fail to launch the activity, in which case the sequence's state transitions to
+ * {@code INTENT_FAILED} via {@link #onIntentFailed}. This is a terminal state.
+ *
+ * If an activity is successfully started, the launch sequence's state will transition into
+ * {@code STARTED} via {@link #onActivityLaunched}. This is a transient state.
+ *
+ * It must then transition to either {@code CANCELLED} with {@link #onActivityLaunchCancelled}
+ * or into {@code FINISHED} with {@link #onActivityLaunchFinished}. These are terminal states.
+ *
+ * Note that the {@link ActivityRecord} provided as a parameter to some state transitions isn't
+ * necessarily the same within a single launch sequence: it is only the top-most activity at the
+ * time (if any). Trampoline activities coalesce several activity starts into a single launch
+ * sequence.
+ *
+ * Upon reaching a terminal state, it is considered that there are no active launch sequences
+ * until a subsequent transition into {@code INTENT_STARTED} initiates a new launch sequence.
+ *
+ * <pre>
+ *        ┌⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯┐     ┌⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯┐     ╔══════════════════════════╗
+ *    ╴╴▶ ⋮ INTENT_STARTED ⋮ ──▶ ⋮     ACTIVITY_LAUNCHED     ⋮ ──▶ ║ ACTIVITY_LAUNCH_FINISHED ║
+ *        └⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯┘     └⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯┘     ╚══════════════════════════╝
+ *          :                      :
+ *          :                      :
+ *          ▼                      ▼
+ *        ╔════════════════╗     ╔═══════════════════════════╗
+ *        ║ INTENT_FAILED  ║     ║ ACTIVITY_LAUNCH_CANCELLED ║
+ *        ╚════════════════╝     ╚═══════════════════════════╝
+ * </pre>
+ */
+public interface ActivityMetricsLaunchObserver {
+    /**
+     * The 'temperature' at which a launch sequence had started.
+     *
+     * The lower the temperature the more work has to be done during start-up.
+     * A 'cold' temperature means that a new process has been started and likely
+     * nothing is cached.
+     *
+     * A hot temperature means the existing activity is brought to the foreground.
+     * It may need to regenerate some objects as a result of {@code onTrimMemory}.
+     *
+     * A warm temperature is in the middle; an existing process is used, but the activity
+     * has to be created from scratch with {@code #onCreate}.
+     *
+     * @see https://developer.android.com/topic/performance/vitals/launch-time
+     */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef({
+            TEMPERATURE_COLD,
+            TEMPERATURE_WARM,
+            TEMPERATURE_HOT
+    })
+    @interface Temperature {}
+
+    /** Cold launch sequence: a new process has started. */
+    public static final int TEMPERATURE_COLD = 1;
+    /** Warm launch sequence: process reused, but activity has to be created. */
+    public static final int TEMPERATURE_WARM = 2;
+    /** Hot launch sequence: process reused, activity brought-to-top. */
+    public static final int TEMPERATURE_HOT = 3;
+
+    /**
+     * Notifies the observer that a new launch sequence has begun as a result of a new intent.
+     *
+     * Once a launch sequence begins, the resolved activity will either subsequently start with
+     * {@link #onActivityLaunched} or abort early (for example due to a resolution error or due to
+     * a security error) with {@link #onIntentFailed}.
+     *
+     * Multiple calls to this method cannot occur without first terminating the current
+     * launch sequence.
+     */
+    public void onIntentStarted(@NonNull Intent intent);
+
+    /**
+     * Notifies the observer that the current launch sequence has failed to launch an activity.
+     *
+     * This function call terminates the current launch sequence. The next method call, if any,
+     * must be {@link #onIntentStarted}.
+     *
+     * Examples of this happening:
+     *  - Failure to resolve to an activity
+     *  - Calling package did not have the security permissions to call the requested activity
+     *  - Resolved activity was already running and only needed to be brought to the top
+     *
+     * Multiple calls to this method cannot occur without first terminating the current
+     * launch sequence.
+     */
+    public void onIntentFailed();
+
+    /**
+     * Notifies the observer that the current launch sequence had begun starting an activity.
+     *
+     * This is an intermediate state: once an activity begins starting, the entire launch sequence
+     * will later terminate by either finishing or cancelling.
+     *
+     * The initial activity is the first activity to be started as part of a launch sequence:
+     * it is represented by {@param activity} However, it isn't
+     * necessarily the activity which will be considered as displayed when the activity
+     * finishes launching (e.g. {@code activity} in {@link #onActivityLaunchFinished}).
+     *
+     * Multiple calls to this method cannot occur without first terminating the current
+     * launch sequence.
+     */
+    public void onActivityLaunched(@NonNull ActivityRecord activity,
+                                   @Temperature int temperature);
+
+    /**
+     * Notifies the observer that the current launch sequence has been aborted.
+     *
+     * This function call terminates the current launch sequence. The next method call, if any,
+     * must be {@link #onIntentStarted}.
+     *
+     * This can happen for many reasons, for example the user switches away to another app
+     * prior to the launch sequence completing, or the application being killed.
+     *
+     * Multiple calls to this method cannot occur without first terminating the current
+     * launch sequence.
+     *
+     * @param abortingActivity the last activity that had the top-most window during abort
+     *                         (this can be {@code null} in rare situations its unknown).
+     *
+     * @apiNote The aborting activity isn't necessarily the same as the starting activity;
+     *          in the case of a trampoline, multiple activities could've been started
+     *          and only the latest activity is reported here.
+     */
+    public void onActivityLaunchCancelled(@Nullable ActivityRecord abortingActivity);
+
+    /**
+     * Notifies the observer that the current launch sequence has been successfully finished.
+     *
+     * This function call terminates the current launch sequence. The next method call, if any,
+     * must be {@link #onIntentStarted}.
+     *
+     * A launch sequence is considered to be successfully finished when a frame is fully
+     * drawn for the first time: the top-most activity at the time is what's reported here.
+     *
+     * @param finalActivity the top-most activity whose windows were first to fully draw
+     *
+     * Multiple calls to this method cannot occur without first terminating the current
+     * launch sequence.
+     *
+     * @apiNote The finishing activity isn't necessarily the same as the starting activity;
+     *          in the case of a trampoline, multiple activities could've been started
+     *          and only the latest activity that was top-most during first-frame drawn
+     *          is reported here.
+     */
+    public void onActivityLaunchFinished(@NonNull ActivityRecord finalActivity);
+}
diff --git a/services/core/java/com/android/server/wm/ActivityMetricsLogger.java b/services/core/java/com/android/server/wm/ActivityMetricsLogger.java
index 8bde7dd..9b01dfd 100644
--- a/services/core/java/com/android/server/wm/ActivityMetricsLogger.java
+++ b/services/core/java/com/android/server/wm/ActivityMetricsLogger.java
@@ -80,6 +80,7 @@
 import static com.android.server.am.MemoryStatUtil.readMemoryStatFromFilesystem;
 import static com.android.server.wm.ActivityTaskManagerInternal.APP_TRANSITION_TIMEOUT;
 
+import android.app.WindowConfiguration.WindowingMode;
 import android.content.Context;
 import android.content.Intent;
 import android.content.pm.ApplicationInfo;
@@ -141,15 +142,21 @@
     private final Context mContext;
     private final MetricsLogger mMetricsLogger = new MetricsLogger();
 
+    // set to INVALID_START_TIME in reset.
+    // set to valid value in notifyActivityLaunching
     private long mCurrentTransitionStartTime = INVALID_START_TIME;
     private long mLastTransitionStartTime = INVALID_START_TIME;
 
     private int mCurrentTransitionDeviceUptime;
     private int mCurrentTransitionDelayMs;
+
+    /** If the any app transitions have been logged as starting, after the latest reset. */
     private boolean mLoggedTransitionStarting;
 
+    /** Map : @WindowingMode int => WindowingModeTransitionInfo */
     private final SparseArray<WindowingModeTransitionInfo> mWindowingModeTransitionInfo =
             new SparseArray<>();
+    /** Map : @WindowingMode int => WindowingModeTransitionInfo */
     private final SparseArray<WindowingModeTransitionInfo> mLastWindowingModeTransitionInfo =
             new SparseArray<>();
     private final H mHandler;
@@ -157,6 +164,12 @@
     private ArtManagerInternal mArtManagerInternal;
     private final StringBuilder mStringBuilder = new StringBuilder();
 
+    /**
+     * Due to the global single concurrent launch sequence, all calls to this observer must be made
+     * in-order on the same thread to fulfill the "happens-before" guarantee in LaunchObserver.
+     */
+    private final ActivityMetricsLaunchObserver mLaunchObserver = null;
+
     private final class H extends Handler {
 
         public H(Looper looper) {
@@ -175,6 +188,7 @@
     }
 
     private final class WindowingModeTransitionInfo {
+        /** The latest activity to have been launched. */
         private ActivityRecord launchedActivity;
         private int startResult;
         private boolean currentTransitionProcessRunning;
@@ -273,7 +287,7 @@
             return;
         }
 
-        int windowingMode = stack.getWindowingMode();
+        @WindowingMode int windowingMode = stack.getWindowingMode();
         if (windowingMode == WINDOWING_MODE_PINNED) {
             stack = mSupervisor.findStackBehind(stack);
             windowingMode = stack.getWindowingMode();
@@ -301,11 +315,19 @@
      * Notifies the tracker at the earliest possible point when we are starting to launch an
      * activity.
      */
-    void notifyActivityLaunching() {
+    void notifyActivityLaunching(Intent intent) {
+        if (DEBUG_METRICS) {
+            Slog.i(TAG, String.format("notifyActivityLaunching: active:%b, intent:%s",
+                                      isAnyTransitionActive(),
+                                      intent));
+        }
+
         if (!isAnyTransitionActive()) {
-            if (DEBUG_METRICS) Slog.i(TAG, "notifyActivityLaunching");
+
             mCurrentTransitionStartTime = SystemClock.uptimeMillis();
             mLastTransitionStartTime = mCurrentTransitionStartTime;
+
+            launchObserverNotifyIntentStarted(intent);
         }
     }
 
@@ -350,7 +372,9 @@
                 + " processRunning=" + processRunning
                 + " processSwitch=" + processSwitch);
 
-        final int windowingMode = launchedActivity != null
+        // If we are already in an existing transition, only update the activity name, but not the
+        // other attributes.
+        final @WindowingMode int windowingMode = launchedActivity != null
                 ? launchedActivity.getWindowingMode()
                 : WINDOWING_MODE_UNDEFINED;
         final WindowingModeTransitionInfo info = mWindowingModeTransitionInfo.get(windowingMode);
@@ -361,13 +385,15 @@
 
         if (launchedActivity != null && launchedActivity.nowVisible) {
             // Launched activity is already visible. We cannot measure windows drawn delay.
-            reset(true /* abort */, info);
+            reset(true /* abort */, info, "launched activity already visible");
             return;
         }
 
         if (launchedActivity != null && info != null) {
             // If we are already in an existing transition, only update the activity name, but not
             // the other attributes.
+
+            // Coalesce multiple (trampoline) activities from a single sequence together.
             info.launchedActivity = launchedActivity;
             return;
         }
@@ -377,7 +403,7 @@
         if ((!isLoggableResultCode(resultCode) || launchedActivity == null || !processSwitch
                 || windowingMode == WINDOWING_MODE_UNDEFINED) && !otherWindowModesLaunching) {
             // Failed to launch or it was not a process switch, so we don't care about the timing.
-            reset(true /* abort */, info);
+            reset(true /* abort */, info, "failed to launch or not a process switch");
             return;
         } else if (otherWindowModesLaunching) {
             // Don't log this windowing mode but continue with the other windowing modes.
@@ -386,6 +412,8 @@
 
         if (DEBUG_METRICS) Slog.i(TAG, "notifyActivityLaunched successful");
 
+        // A new launch sequence [with the windowingMode] has begun.
+        // Start tracking it.
         final WindowingModeTransitionInfo newInfo = new WindowingModeTransitionInfo();
         newInfo.launchedActivity = launchedActivity;
         newInfo.currentTransitionProcessRunning = processRunning;
@@ -394,6 +422,7 @@
         mLastWindowingModeTransitionInfo.put(windowingMode, newInfo);
         mCurrentTransitionDeviceUptime = (int) (SystemClock.uptimeMillis() / 1000);
         startTraces(newInfo);
+        launchObserverNotifyActivityLaunched(newInfo);
     }
 
     /**
@@ -407,7 +436,8 @@
     /**
      * Notifies the tracker that all windows of the app have been drawn.
      */
-    WindowingModeTransitionInfoSnapshot notifyWindowsDrawn(int windowingMode, long timestamp) {
+    WindowingModeTransitionInfoSnapshot notifyWindowsDrawn(@WindowingMode int windowingMode,
+                                                           long timestamp) {
         if (DEBUG_METRICS) Slog.i(TAG, "notifyWindowsDrawn windowingMode=" + windowingMode);
 
         final WindowingModeTransitionInfo info = mWindowingModeTransitionInfo.get(windowingMode);
@@ -419,7 +449,7 @@
         final WindowingModeTransitionInfoSnapshot infoSnapshot =
                 new WindowingModeTransitionInfoSnapshot(info);
         if (allWindowsDrawn() && mLoggedTransitionStarting) {
-            reset(false /* abort */, info);
+            reset(false /* abort */, info, "notifyWindowsDrawn - all windows drawn");
         }
         return infoSnapshot;
     }
@@ -427,7 +457,7 @@
     /**
      * Notifies the tracker that the starting window was drawn.
      */
-    void notifyStartingWindowDrawn(int windowingMode, long timestamp) {
+    void notifyStartingWindowDrawn(@WindowingMode int windowingMode, long timestamp) {
         final WindowingModeTransitionInfo info = mWindowingModeTransitionInfo.get(windowingMode);
         if (info == null || info.loggedStartingWindowDrawn) {
             return;
@@ -444,22 +474,28 @@
      */
     void notifyTransitionStarting(SparseIntArray windowingModeToReason, long timestamp) {
         if (!isAnyTransitionActive() || mLoggedTransitionStarting) {
+            // Ignore calls to this made after a reset and prior to notifyActivityLaunching.
+
+            // Ignore any subsequent notifyTransitionStarting until the next reset.
             return;
         }
         if (DEBUG_METRICS) Slog.i(TAG, "notifyTransitionStarting");
         mCurrentTransitionDelayMs = calculateDelay(timestamp);
         mLoggedTransitionStarting = true;
+
+        WindowingModeTransitionInfo foundInfo = null;
         for (int index = windowingModeToReason.size() - 1; index >= 0; index--) {
-            final int windowingMode = windowingModeToReason.keyAt(index);
+            final @WindowingMode int windowingMode = windowingModeToReason.keyAt(index);
             final WindowingModeTransitionInfo info = mWindowingModeTransitionInfo.get(
                     windowingMode);
             if (info == null) {
                 continue;
             }
             info.reason = windowingModeToReason.valueAt(index);
+            foundInfo = info;
         }
         if (allWindowsDrawn()) {
-            reset(false /* abort */, null /* WindowingModeTransitionInfo */);
+            reset(false /* abort */, foundInfo, "notifyTransitionStarting - all windows drawn");
         }
     }
 
@@ -498,7 +534,7 @@
                 logAppTransitionCancel(info);
                 mWindowingModeTransitionInfo.remove(r.getWindowingMode());
                 if (mWindowingModeTransitionInfo.size() == 0) {
-                    reset(true /* abort */, info);
+                    reset(true /* abort */, info, "notifyVisibilityChanged to invisible");
                 }
             }
         }
@@ -534,12 +570,25 @@
                 && mWindowingModeTransitionInfo.size() > 0;
     }
 
-    private void reset(boolean abort, WindowingModeTransitionInfo info) {
-        if (DEBUG_METRICS) Slog.i(TAG, "reset abort=" + abort);
+    private void reset(boolean abort, WindowingModeTransitionInfo info, String cause) {
+        if (DEBUG_METRICS) Slog.i(TAG, "reset abort=" + abort + ",cause=" + cause);
         if (!abort && isAnyTransitionActive()) {
             logAppTransitionMultiEvents();
         }
         stopLaunchTrace(info);
+
+        // Ignore reset-after reset.
+        if (isAnyTransitionActive()) {
+            // LaunchObserver callbacks.
+            if (abort) {
+                launchObserverNotifyActivityLaunchCancelled(info);
+            } else {
+                launchObserverNotifyActivityLaunchFinished(info);
+            }
+        } else {
+            launchObserverNotifyIntentFailed();
+        }
+
         mCurrentTransitionStartTime = INVALID_START_TIME;
         mCurrentTransitionDelayMs = INVALID_DELAY;
         mLoggedTransitionStarting = false;
@@ -572,6 +621,13 @@
                 info.launchedActivity.packageName,
                 convertAppStartTransitionType(type),
                 info.launchedActivity.info.name);
+        if (DEBUG_METRICS) {
+            Slog.i(TAG, String.format("APP_START_CANCELED(%s, %s, %s, %s)",
+                    info.launchedActivity.appInfo.uid,
+                    info.launchedActivity.packageName,
+                    convertAppStartTransitionType(type),
+                    info.launchedActivity.info.name));
+        }
     }
 
     private void logAppTransitionMultiEvents() {
@@ -656,6 +712,17 @@
                 launchToken,
                 packageOptimizationInfo.getCompilationReason(),
                 packageOptimizationInfo.getCompilationFilter());
+
+        if (DEBUG_METRICS) {
+            Slog.i(TAG, String.format("APP_START_OCCURRED(%s, %s, %s, %s, %s)",
+                    info.applicationInfo.uid,
+                    info.packageName,
+                    convertAppStartTransitionType(info.type),
+                    info.launchedActivityName,
+                    info.launchedActivityLaunchedFromPackage));
+        }
+
+
         logAppStartMemoryStateCapture(info);
     }
 
@@ -923,4 +990,76 @@
             info.launchTraceActive = false;
         }
     }
+
+    /** Notify the {@link ActivityMetricsLaunchObserver} that a new launch sequence has begun. */
+    private void launchObserverNotifyIntentStarted(Intent intent) {
+        if (mLaunchObserver != null) {
+            // Beginning a launch is timing sensitive and so should be observed as soon as possible.
+            mLaunchObserver.onIntentStarted(intent);
+        }
+    }
+
+    /**
+     * Notify the {@link ActivityMetricsLaunchObserver} that the previous launch sequence has
+     * aborted due to intent failure (e.g. intent resolve failed or security error, etc) or
+     * intent being delivered to the top running activity.
+     */
+    private void launchObserverNotifyIntentFailed() {
+        if (mLaunchObserver != null) {
+            mLaunchObserver.onIntentFailed();
+        }
+    }
+
+    /**
+     * Notify the {@link ActivityMetricsLaunchObserver} that the current launch sequence's activity
+     * has started.
+     */
+    private void launchObserverNotifyActivityLaunched(WindowingModeTransitionInfo info) {
+        @ActivityMetricsLaunchObserver.Temperature int temperature =
+                convertTransitionTypeToLaunchObserverTemperature(getTransitionType(info));
+
+        if (mLaunchObserver != null) {
+            // Beginning a launch is timing sensitive and so should be observed as soon as possible.
+            mLaunchObserver.onActivityLaunched(info.launchedActivity,
+                                               temperature);
+        }
+    }
+
+    /**
+     * Notify the {@link ActivityMetricsLaunchObserver} that the current launch sequence is
+     * cancelled.
+     */
+    private void launchObserverNotifyActivityLaunchCancelled(WindowingModeTransitionInfo info) {
+        final ActivityRecord launchedActivity = info != null ? info.launchedActivity : null;
+
+        if (mLaunchObserver != null) {
+            mLaunchObserver.onActivityLaunchCancelled(launchedActivity);
+        }
+    }
+
+    /**
+     * Notify the {@link ActivityMetricsLaunchObserver} that the current launch sequence's activity
+     * has fully finished (successfully).
+     */
+    private void launchObserverNotifyActivityLaunchFinished(WindowingModeTransitionInfo info) {
+        final ActivityRecord launchedActivity = info.launchedActivity;
+
+        if (mLaunchObserver != null) {
+            mLaunchObserver.onActivityLaunchFinished(launchedActivity);
+        }
+    }
+
+    private static @ActivityMetricsLaunchObserver.Temperature int
+            convertTransitionTypeToLaunchObserverTemperature(int transitionType) {
+        switch (transitionType) {
+            case TYPE_TRANSITION_WARM_LAUNCH:
+                return ActivityMetricsLaunchObserver.TEMPERATURE_WARM;
+            case TYPE_TRANSITION_HOT_LAUNCH:
+                return ActivityMetricsLaunchObserver.TEMPERATURE_HOT;
+            case TYPE_TRANSITION_COLD_LAUNCH:
+                return ActivityMetricsLaunchObserver.TEMPERATURE_COLD;
+            default:
+                return -1;
+        }
+    }
 }
diff --git a/services/core/java/com/android/server/wm/ActivityStackSupervisor.java b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
index 77b331e..de203c9 100644
--- a/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
@@ -4745,7 +4745,7 @@
                 final ActivityRecord targetActivity = task.getTopActivity();
 
                 sendPowerHintForLaunchStartIfNeeded(true /* forceSend */, targetActivity);
-                mActivityMetricsLogger.notifyActivityLaunching();
+                mActivityMetricsLogger.notifyActivityLaunching(task.intent);
                 try {
                     mService.moveTaskToFrontLocked(task.taskId, 0, options,
                             true /* fromRecents */);
diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java
index e43a79a..afc946b 100644
--- a/services/core/java/com/android/server/wm/ActivityStarter.java
+++ b/services/core/java/com/android/server/wm/ActivityStarter.java
@@ -996,7 +996,7 @@
         if (intent != null && intent.hasFileDescriptors()) {
             throw new IllegalArgumentException("File descriptors passed in Intent");
         }
-        mSupervisor.getActivityMetricsLogger().notifyActivityLaunching();
+        mSupervisor.getActivityMetricsLogger().notifyActivityLaunching(intent);
         boolean componentSpecified = intent.getComponent() != null;
 
         final int realCallingPid = Binder.getCallingPid();
diff --git a/services/core/java/com/android/server/wm/RecentsAnimation.java b/services/core/java/com/android/server/wm/RecentsAnimation.java
index 7a1ebf2..9aeb025 100644
--- a/services/core/java/com/android/server/wm/RecentsAnimation.java
+++ b/services/core/java/com/android/server/wm/RecentsAnimation.java
@@ -122,7 +122,7 @@
                     targetActivity);
         }
 
-        mStackSupervisor.getActivityMetricsLogger().notifyActivityLaunching();
+        mStackSupervisor.getActivityMetricsLogger().notifyActivityLaunching(intent);
 
         mService.mAmInternal.setRunningRemoteAnimation(mCallingPid, true);
 
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeFilteringTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeFilteringTest.java
index ae0e35b..4018eef 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeFilteringTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeFilteringTest.java
@@ -70,7 +70,8 @@
 
     private NotificationRecord getNotificationRecord(NotificationChannel c) {
         StatusBarNotification sbn = mock(StatusBarNotification.class);
-        when(sbn.getNotification()).thenReturn(mock(Notification.class));
+        Notification notification = mock(Notification.class);
+        when(sbn.getNotification()).thenReturn(notification);
         return new NotificationRecord(mContext, sbn, c);
     }
 
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityMetricsLaunchObserverTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityMetricsLaunchObserverTests.java
new file mode 100644
index 0000000..215c51d
--- /dev/null
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityMetricsLaunchObserverTests.java
@@ -0,0 +1,190 @@
+/*
+ * Copyright (C) 2018 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.wm;
+
+import static android.app.ActivityManager.START_SUCCESS;
+import static android.app.ActivityManager.START_TASK_TO_FRONT;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
+
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
+
+import android.content.Intent;
+import android.os.SystemClock;
+import android.platform.test.annotations.Presubmit;
+import android.util.SparseIntArray;
+
+import androidx.test.filters.FlakyTest;
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.Test;
+
+/**
+ * Tests for the {@link ActivityMetricsLaunchObserver} class.
+ *
+ * Build/Install/Run:
+ *  atest WmTests:ActivityMetricsLaunchObserverTests
+ */
+@SmallTest
+@Presubmit
+@FlakyTest(detail="promote once confirmed non-flaky")
+public class ActivityMetricsLaunchObserverTests extends ActivityTestsBase {
+    private ActivityMetricsLogger mActivityMetricsLogger;
+    private ActivityMetricsLaunchObserver mLaunchObserver;
+
+    private TestActivityStack mStack;
+    private TaskRecord mTask;
+    private ActivityRecord mActivityRecord;
+    private ActivityRecord mActivityRecordTrampoline;
+
+    @Before
+    public void setUpAMLO() throws Exception {
+        setupActivityTaskManagerService();
+
+        mActivityMetricsLogger =
+                new ActivityMetricsLogger(mSupervisor, mService.mContext, mService.mH.getLooper());
+
+        mLaunchObserver = mock(ActivityMetricsLaunchObserver.class);
+
+        // TODO: Use ActivityMetricsLaunchObserverRegistry .
+        java.lang.reflect.Field f =
+                mActivityMetricsLogger.getClass().getDeclaredField("mLaunchObserver");
+        f.setAccessible(true);
+        f.set(mActivityMetricsLogger, mLaunchObserver);
+
+        // Sometimes we need an ActivityRecord for ActivityMetricsLogger to do anything useful.
+        // This seems to be the easiest way to create an ActivityRecord.
+        mStack = mSupervisor.getDefaultDisplay().createStack(
+                WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
+        mTask = new TaskBuilder(mSupervisor).setStack(mStack).build();
+        mActivityRecord = new ActivityBuilder(mService).setTask(mTask).build();
+        mActivityRecordTrampoline = new ActivityBuilder(mService).setTask(mTask).build();
+    }
+
+    @Test
+    public void testOnIntentStarted() throws Exception {
+        Intent intent = new Intent("action 1");
+
+        mActivityMetricsLogger.notifyActivityLaunching(intent);
+
+        verify(mLaunchObserver).onIntentStarted(eq(intent));
+        verifyNoMoreInteractions(mLaunchObserver);
+    }
+
+    @Test
+    public void testOnIntentFailed() throws Exception {
+        testOnIntentStarted();
+
+        ActivityRecord activityRecord = null;
+
+        // Bringing an intent that's already running 'to front' is not considered
+        // as an ACTIVITY_LAUNCHED state transition.
+        mActivityMetricsLogger.notifyActivityLaunched(START_TASK_TO_FRONT,
+                activityRecord);
+
+        verify(mLaunchObserver).onIntentFailed();
+        verifyNoMoreInteractions(mLaunchObserver);
+    }
+
+    @Test
+    public void testOnActivityLaunched() throws Exception {
+        testOnIntentStarted();
+
+        mActivityMetricsLogger.notifyActivityLaunched(START_SUCCESS,
+                mActivityRecord);
+
+        verify(mLaunchObserver).onActivityLaunched(eq(mActivityRecord), anyInt());
+        verifyNoMoreInteractions(mLaunchObserver);
+    }
+
+    @Test
+    public void testOnActivityLaunchFinished() throws Exception {
+       testOnActivityLaunched();
+
+       mActivityMetricsLogger.notifyTransitionStarting(new SparseIntArray(),
+               SystemClock.uptimeMillis());
+
+       mActivityMetricsLogger.notifyWindowsDrawn(mActivityRecord.getWindowingMode(),
+               SystemClock.uptimeMillis());
+
+       verify(mLaunchObserver).onActivityLaunchFinished(eq(mActivityRecord));
+       verifyNoMoreInteractions(mLaunchObserver);
+    }
+
+    @Test
+    public void testOnActivityLaunchCancelled() throws Exception {
+       testOnActivityLaunched();
+
+       mActivityRecord.nowVisible = true;
+
+       // Cannot time already-visible activities.
+       mActivityMetricsLogger.notifyActivityLaunched(START_TASK_TO_FRONT, mActivityRecord);
+
+       verify(mLaunchObserver).onActivityLaunchCancelled(eq(mActivityRecord));
+       verifyNoMoreInteractions(mLaunchObserver);
+    }
+
+    @Test
+    public void testOnActivityLaunchedTrampoline() throws Exception {
+        testOnIntentStarted();
+
+        mActivityMetricsLogger.notifyActivityLaunched(START_SUCCESS,
+                mActivityRecord);
+
+        verify(mLaunchObserver).onActivityLaunched(eq(mActivityRecord), anyInt());
+
+        // A second, distinct, activity launch is coalesced into the the current app launch sequence
+        mActivityMetricsLogger.notifyActivityLaunched(START_SUCCESS,
+                mActivityRecordTrampoline);
+
+        verifyNoMoreInteractions(mLaunchObserver);
+    }
+
+    @Test
+    public void testOnActivityLaunchFinishedTrampoline() throws Exception {
+       testOnActivityLaunchedTrampoline();
+
+       mActivityMetricsLogger.notifyTransitionStarting(new SparseIntArray(),
+               SystemClock.uptimeMillis());
+
+       mActivityMetricsLogger.notifyWindowsDrawn(mActivityRecordTrampoline.getWindowingMode(),
+               SystemClock.uptimeMillis());
+
+       verify(mLaunchObserver).onActivityLaunchFinished(eq(mActivityRecordTrampoline));
+       verifyNoMoreInteractions(mLaunchObserver);
+    }
+
+    @Test
+    public void testOnActivityLaunchCancelledTrampoline() throws Exception {
+       testOnActivityLaunchedTrampoline();
+
+       mActivityRecordTrampoline.nowVisible = true;
+
+       // Cannot time already-visible activities.
+       mActivityMetricsLogger.notifyActivityLaunched(START_TASK_TO_FRONT,
+               mActivityRecordTrampoline);
+
+       verify(mLaunchObserver).onActivityLaunchCancelled(eq(mActivityRecordTrampoline));
+       verifyNoMoreInteractions(mLaunchObserver);
+    }
+}
diff --git a/telephony/java/android/telephony/DisconnectCause.java b/telephony/java/android/telephony/DisconnectCause.java
index d7169b2..c6887ab 100644
--- a/telephony/java/android/telephony/DisconnectCause.java
+++ b/telephony/java/android/telephony/DisconnectCause.java
@@ -342,6 +342,12 @@
      */
     public static final int TOO_MANY_ONGOING_CALLS = 75;
 
+    /**
+     * Indicates that a new outgoing call cannot be placed because OTASP provisioning is currently
+     * in process.
+     */
+    public static final int OTASP_PROVISIONING_IN_PROCESS = 76;
+
     //*********************************************************************************************
     // When adding a disconnect type:
     // 1) Update toString() with the newly added disconnect type.
@@ -505,6 +511,8 @@
             return "CALLING_DISABLED";
         case TOO_MANY_ONGOING_CALLS:
             return "TOO_MANY_ONGOING_CALLS";
+        case OTASP_PROVISIONING_IN_PROCESS:
+            return "OTASP_PROVISIONING_IN_PROCESS";
         default:
             return "INVALID: " + cause;
         }
diff --git a/telephony/java/android/telephony/ims/ImsCallProfile.java b/telephony/java/android/telephony/ims/ImsCallProfile.java
index 89ef339..f73036e 100644
--- a/telephony/java/android/telephony/ims/ImsCallProfile.java
+++ b/telephony/java/android/telephony/ims/ImsCallProfile.java
@@ -16,17 +16,20 @@
 
 package android.telephony.ims;
 
+import android.annotation.IntDef;
 import android.annotation.SystemApi;
 import android.annotation.UnsupportedAppUsage;
 import android.os.Bundle;
 import android.os.Parcel;
 import android.os.Parcelable;
-import android.os.PersistableBundle;
 import android.telecom.VideoProfile;
 import android.util.Log;
 
 import com.android.internal.telephony.PhoneConstants;
 
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
 /**
  * Parcelable object to handle IMS call profile.
  * It is created from GSMA IR.92/IR.94, 3GPP TS 24.229/TS 26.114/TS26.111.
@@ -206,17 +209,36 @@
     public static final int DIALSTRING_USSD = 2;
 
     /**
-     * Values for causes that restrict call types
+     * Call is not restricted on peer side and High Definition media is supported
      */
-    // Default cause not restricted at peer and HD is supported
     public static final int CALL_RESTRICT_CAUSE_NONE = 0;
-    // Service not supported by RAT at peer
+
+    /**
+     * High Definition media is not supported on the peer side due to the Radio Access Technology
+     * (RAT) it is are connected to.
+     */
     public static final int CALL_RESTRICT_CAUSE_RAT = 1;
-    // Service Disabled at peer
+
+    /**
+     * The service has been disabled on the peer side.
+     */
     public static final int CALL_RESTRICT_CAUSE_DISABLED = 2;
-    // HD is not supported
+
+    /**
+     * High definition media is not currently supported.
+     */
     public static final int CALL_RESTRICT_CAUSE_HD = 3;
 
+    /**@hide*/
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(prefix = "CALL_RESTRICT_CAUSE_", value = {
+            CALL_RESTRICT_CAUSE_NONE,
+            CALL_RESTRICT_CAUSE_RAT,
+            CALL_RESTRICT_CAUSE_DISABLED,
+            CALL_RESTRICT_CAUSE_HD
+    })
+    public @interface CallRestrictCause {}
+
     /**
      * String extra properties
      *  oi : Originating identity (number), MT only
@@ -270,7 +292,7 @@
     public int mCallType;
     /** @hide */
     @UnsupportedAppUsage
-    public int mRestrictCause = CALL_RESTRICT_CAUSE_NONE;
+    public @CallRestrictCause int mRestrictCause = CALL_RESTRICT_CAUSE_NONE;
 
     /**
      * Extras associated with this {@link ImsCallProfile}.
@@ -285,7 +307,7 @@
      *     <li>{@code long[]}</li>
      *     <li>{@code double[]}</li>
      *     <li>{@code String[]}</li>
-     *     <li>{@link PersistableBundle}</li>
+     *     <li>{@link android.os.PersistableBundle}</li>
      *     <li>{@link Boolean} (and boolean)</li>
      *     <li>{@code boolean[]}</li>
      *     <li>Other {@link Parcelable} classes in the {@code android.*} namespace.</li>
@@ -426,6 +448,14 @@
         }
     }
 
+    /**
+     * Set the call restrict cause, which provides the reason why a call has been restricted from
+     * using High Definition media.
+     */
+    public void setCallRestrictCause(@CallRestrictCause int cause) {
+        mRestrictCause = cause;
+    }
+
     public void updateCallType(ImsCallProfile profile) {
         mCallType = profile.mCallType;
     }
@@ -494,7 +524,11 @@
         return mCallType;
     }
 
-    public int getRestrictCause() {
+    /**
+     * @return The call restrict cause, which provides the reason why a call has been restricted
+     * from using High Definition media.
+     */
+    public @CallRestrictCause int getRestrictCause() {
         return mRestrictCause;
     }