Merge "Refactor onProviderStateChangedOnHandler" into rvc-dev
diff --git a/apex/media/framework/java/android/media/MediaParser.java b/apex/media/framework/java/android/media/MediaParser.java
index edf25ae..e533b7a 100644
--- a/apex/media/framework/java/android/media/MediaParser.java
+++ b/apex/media/framework/java/android/media/MediaParser.java
@@ -499,20 +499,58 @@
             })
     public @interface ParserName {}
 
+    /** Parser name returned by {@link #getParserName()} when no parser has been selected yet. */
     public static final String PARSER_NAME_UNKNOWN = "android.media.mediaparser.UNKNOWN";
+    /**
+     * Parser for the Matroska container format, as defined in the <a
+     * href="https://matroska.org/technical/specs/">spec</a>.
+     */
     public static final String PARSER_NAME_MATROSKA = "android.media.mediaparser.MatroskaParser";
+    /**
+     * Parser for fragmented files using the MP4 container format, as defined in ISO/IEC 14496-12.
+     */
     public static final String PARSER_NAME_FMP4 = "android.media.mediaparser.FragmentedMp4Parser";
+    /**
+     * Parser for non-fragmented files using the MP4 container format, as defined in ISO/IEC
+     * 14496-12.
+     */
     public static final String PARSER_NAME_MP4 = "android.media.mediaparser.Mp4Parser";
+    /** Parser for the MP3 container format, as defined in ISO/IEC 11172-3. */
     public static final String PARSER_NAME_MP3 = "android.media.mediaparser.Mp3Parser";
+    /** Parser for the ADTS container format, as defined in ISO/IEC 13818-7. */
     public static final String PARSER_NAME_ADTS = "android.media.mediaparser.AdtsParser";
+    /**
+     * Parser for the AC-3 container format, as defined in Digital Audio Compression Standard
+     * (AC-3).
+     */
     public static final String PARSER_NAME_AC3 = "android.media.mediaparser.Ac3Parser";
+    /** Parser for the TS container format, as defined in ISO/IEC 13818-1. */
     public static final String PARSER_NAME_TS = "android.media.mediaparser.TsParser";
+    /**
+     * Parser for the FLV container format, as defined in Adobe Flash Video File Format
+     * Specification.
+     */
     public static final String PARSER_NAME_FLV = "android.media.mediaparser.FlvParser";
+    /** Parser for the OGG container format, as defined in RFC 3533. */
     public static final String PARSER_NAME_OGG = "android.media.mediaparser.OggParser";
+    /** Parser for the PS container format, as defined in ISO/IEC 11172-1. */
     public static final String PARSER_NAME_PS = "android.media.mediaparser.PsParser";
+    /**
+     * Parser for the WAV container format, as defined in Multimedia Programming Interface and Data
+     * Specifications.
+     */
     public static final String PARSER_NAME_WAV = "android.media.mediaparser.WavParser";
+    /** Parser for the AMR container format, as defined in RFC 4867. */
     public static final String PARSER_NAME_AMR = "android.media.mediaparser.AmrParser";
+    /**
+     * Parser for the AC-4 container format, as defined by Dolby AC-4: Audio delivery for
+     * Next-Generation Entertainment Services.
+     */
     public static final String PARSER_NAME_AC4 = "android.media.mediaparser.Ac4Parser";
+    /**
+     * Parser for the FLAC container format, as defined in the <a
+     * href="https://xiph.org/flac/">spec</a>.
+     */
     public static final String PARSER_NAME_FLAC = "android.media.mediaparser.FlacParser";
 
     // MediaParser parameters.
diff --git a/api/current.txt b/api/current.txt
index 1c1d142..ed4d9eb 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -30284,6 +30284,7 @@
     field public static final int NET_CAPABILITY_NOT_VPN = 15; // 0xf
     field public static final int NET_CAPABILITY_RCS = 8; // 0x8
     field public static final int NET_CAPABILITY_SUPL = 1; // 0x1
+    field public static final int NET_CAPABILITY_TEMPORARILY_NOT_METERED = 25; // 0x19
     field public static final int NET_CAPABILITY_TRUSTED = 14; // 0xe
     field public static final int NET_CAPABILITY_VALIDATED = 16; // 0x10
     field public static final int NET_CAPABILITY_WIFI_P2P = 6; // 0x6
diff --git a/cmds/statsd/src/external/StatsPullerManager.cpp b/cmds/statsd/src/external/StatsPullerManager.cpp
index a3701a7..79315eb 100644
--- a/cmds/statsd/src/external/StatsPullerManager.cpp
+++ b/cmds/statsd/src/external/StatsPullerManager.cpp
@@ -41,6 +41,40 @@
 namespace os {
 namespace statsd {
 
+// Stores the puller as a wp to avoid holding a reference in case it is unregistered and
+// pullAtomCallbackDied is never called.
+struct PullAtomCallbackDeathCookie {
+    PullAtomCallbackDeathCookie(sp<StatsPullerManager> pullerManager, const PullerKey& pullerKey,
+                                const wp<StatsPuller>& puller)
+        : mPullerManager(pullerManager), mPullerKey(pullerKey), mPuller(puller) {
+    }
+
+    sp<StatsPullerManager> mPullerManager;
+    PullerKey mPullerKey;
+    wp<StatsPuller> mPuller;
+};
+
+void StatsPullerManager::pullAtomCallbackDied(void* cookie) {
+    PullAtomCallbackDeathCookie* cookie_ = static_cast<PullAtomCallbackDeathCookie*>(cookie);
+    sp<StatsPullerManager>& thiz = cookie_->mPullerManager;
+    const PullerKey& pullerKey = cookie_->mPullerKey;
+    wp<StatsPuller> puller = cookie_->mPuller;
+
+    // Erase the mapping from the puller key to the puller if the mapping still exists.
+    // Note that we are removing the StatsPuller object, which internally holds the binder
+    // IPullAtomCallback. However, each new registration creates a new StatsPuller, so this works.
+    lock_guard<mutex> lock(thiz->mLock);
+    const auto& it = thiz->kAllPullAtomInfo.find(pullerKey);
+    if (it != thiz->kAllPullAtomInfo.end() && puller != nullptr && puller == it->second) {
+        StatsdStats::getInstance().notePullerCallbackRegistrationChanged(pullerKey.atomTag,
+                                                                         /*registered=*/false);
+        thiz->kAllPullAtomInfo.erase(pullerKey);
+    }
+    // The death recipient corresponding to this specific IPullAtomCallback can never
+    // be triggered again, so free up resources.
+    delete cookie_;
+}
+
 // Values smaller than this may require to update the alarm.
 const int64_t NO_ALARM_UPDATE = INT64_MAX;
 
@@ -49,7 +83,8 @@
               // TrainInfo.
               {{.atomTag = util::TRAIN_INFO, .uid = -1}, new TrainInfoPuller()},
       }),
-      mNextPullTimeNs(NO_ALARM_UPDATE) {
+      mNextPullTimeNs(NO_ALARM_UPDATE),
+      mPullAtomCallbackDeathRecipient(AIBinder_DeathRecipient_new(pullAtomCallbackDied)) {
 }
 
 bool StatsPullerManager::Pull(int tagId, const ConfigKey& configKey,
@@ -310,19 +345,28 @@
                                                   bool useUid) {
     std::lock_guard<std::mutex> _l(mLock);
     VLOG("RegisterPullerCallback: adding puller for tag %d", atomTag);
-    // TODO(b/146439412): linkToDeath with the callback so that we can remove it
-    // and delete the puller.
+
     StatsdStats::getInstance().notePullerCallbackRegistrationChanged(atomTag, /*registered=*/true);
     int64_t actualCoolDownNs = coolDownNs < kMinCoolDownNs ? kMinCoolDownNs : coolDownNs;
     int64_t actualTimeoutNs = timeoutNs > kMaxTimeoutNs ? kMaxTimeoutNs : timeoutNs;
-    kAllPullAtomInfo[{.atomTag = atomTag, .uid = useUid ? uid : -1}] = new StatsCallbackPuller(
-            atomTag, callback, actualCoolDownNs, actualTimeoutNs, additiveFields);
+
+    sp<StatsCallbackPuller> puller = new StatsCallbackPuller(atomTag, callback, actualCoolDownNs,
+                                                             actualTimeoutNs, additiveFields);
+    PullerKey key = {.atomTag = atomTag, .uid = useUid ? uid : -1};
+    AIBinder_linkToDeath(callback->asBinder().get(), mPullAtomCallbackDeathRecipient.get(),
+                         new PullAtomCallbackDeathCookie(this, key, puller));
+    kAllPullAtomInfo[key] = puller;
 }
 
-void StatsPullerManager::UnregisterPullAtomCallback(const int uid, const int32_t atomTag) {
+void StatsPullerManager::UnregisterPullAtomCallback(const int uid, const int32_t atomTag,
+                                                    bool useUids) {
     std::lock_guard<std::mutex> _l(mLock);
-    StatsdStats::getInstance().notePullerCallbackRegistrationChanged(atomTag, /*registered=*/false);
-    kAllPullAtomInfo.erase({.atomTag = atomTag});
+    PullerKey key = {.atomTag = atomTag, .uid = useUids ? uid : -1};
+    if (kAllPullAtomInfo.find(key) != kAllPullAtomInfo.end()) {
+        StatsdStats::getInstance().notePullerCallbackRegistrationChanged(atomTag,
+                                                                         /*registered=*/false);
+        kAllPullAtomInfo.erase(key);
+    }
 }
 
 }  // namespace statsd
diff --git a/cmds/statsd/src/external/StatsPullerManager.h b/cmds/statsd/src/external/StatsPullerManager.h
index c5824a8..e679837 100644
--- a/cmds/statsd/src/external/StatsPullerManager.h
+++ b/cmds/statsd/src/external/StatsPullerManager.h
@@ -120,7 +120,7 @@
                                   const shared_ptr<IPullAtomCallback>& callback,
                                   bool useUid = false);
 
-    void UnregisterPullAtomCallback(const int uid, const int32_t atomTag);
+    void UnregisterPullAtomCallback(const int uid, const int32_t atomTag, bool useUids = false);
 
     std::map<const PullerKey, sp<StatsPuller>> kAllPullAtomInfo;
 
@@ -164,6 +164,15 @@
 
     int64_t mNextPullTimeNs;
 
+    // Death recipient that is triggered when the process holding the IPullAtomCallback has died.
+    ::ndk::ScopedAIBinder_DeathRecipient mPullAtomCallbackDeathRecipient;
+
+    /**
+     * Death recipient callback that is called when a pull atom callback dies.
+     * The cookie is a pointer to a PullAtomCallbackDeathCookie.
+     */
+    static void pullAtomCallbackDied(void* cookie);
+
     FRIEND_TEST(GaugeMetricE2eTest, TestRandomSamplePulledEvents);
     FRIEND_TEST(GaugeMetricE2eTest, TestRandomSamplePulledEvent_LateAlarm);
     FRIEND_TEST(GaugeMetricE2eTest, TestRandomSamplePulledEventsWithActivation);
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index 2531c89..b6d519a 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -73,8 +73,8 @@
 import android.util.DisplayMetrics;
 import android.util.Singleton;
 import android.util.Size;
-import android.window.WindowContainerToken;
 import android.view.Surface;
+import android.window.WindowContainerToken;
 
 import com.android.internal.app.LocalePicker;
 import com.android.internal.app.procstats.ProcessStats;
@@ -3632,7 +3632,8 @@
      * Set custom state data for this process. It will be included in the record of
      * {@link ApplicationExitInfo} on the death of the current calling process; the new process
      * of the app can retrieve this state data by calling
-     * {@link ApplicationExitInfo#getProcessStateSummary} on the record returned by
+     * {@link android.app.ApplicationExitInfo#getProcessStateSummary()
+     * ApplicationExitInfo.getProcessStateSummary()} on the record returned by
      * {@link #getHistoricalProcessExitReasons}.
      *
      * <p> This would be useful for the calling app to save its stateful data: if it's
@@ -3657,7 +3658,7 @@
         }
     }
 
-    /*
+    /**
      * @return Whether or not the low memory kill will be reported in
      * {@link #getHistoricalProcessExitReasons}.
      *
diff --git a/core/java/android/app/ApplicationExitInfo.java b/core/java/android/app/ApplicationExitInfo.java
index 0ecc003..cfe0aff 100644
--- a/core/java/android/app/ApplicationExitInfo.java
+++ b/core/java/android/app/ApplicationExitInfo.java
@@ -90,7 +90,8 @@
      * {@link #REASON_SIGNALED} and {@link #getStatus} will return
      * the value {@link android.system.OsConstants#SIGKILL}.
      *
-     * Application should use {@link ActivityManager#isLowMemoryKillReportSupported} to check
+     * Application should use {@link android.app.ActivityManager#isLowMemoryKillReportSupported()
+     * ActivityManager.isLowMemoryKillReportSupported()} to check
      * if the device supports reporting {@link #REASON_LOW_MEMORY} or not.
      * </p>
      */
@@ -523,7 +524,7 @@
         return mReason;
     }
 
-    /*
+    /**
      * The exit status argument of exit() if the application calls it, or the signal
      * number if the application is signaled.
      */
@@ -538,7 +539,7 @@
         return mImportance;
     }
 
-    /*
+    /**
      * Last proportional set size of the memory that the process had used in kB.
      *
      * <p class="note">Note: This is the value from last sampling on the process,
@@ -562,7 +563,7 @@
 
     /**
      * The timestamp of the process's death, in milliseconds since the epoch,
-     * as returned by {@link System#currentTimeMillis System.currentTimeMillis()}.
+     * as returned by {@link java.lang.System#currentTimeMillis() System.currentTimeMillis()}.
      */
     public @CurrentTimeMillisLong long getTimestamp() {
         return mTimestamp;
@@ -586,8 +587,9 @@
     }
 
     /**
-     * Return the state data set by calling {@link ActivityManager#setProcessStateSummary}
-     * from the process before its death.
+     * Return the state data set by calling
+     * {@link android.app.ActivityManager#setProcessStateSummary(byte[])
+     * ActivityManager.setProcessStateSummary(byte[])} from the process before its death.
      *
      * @return The process-customized data
      * @see ActivityManager#setProcessStateSummary(byte[])
diff --git a/core/java/android/app/TEST_MAPPING b/core/java/android/app/TEST_MAPPING
index 7b45b72..ab86860 100644
--- a/core/java/android/app/TEST_MAPPING
+++ b/core/java/android/app/TEST_MAPPING
@@ -44,7 +44,7 @@
             "name": "CtsWindowManagerDeviceTestCases",
             "options": [
                 {
-                    "include-filter": "android.server.wm.ToastTest"
+                    "include-filter": "android.server.wm.ToastWindowTest"
                 }
             ],
             "file_patterns": ["INotificationManager\\.aidl"]
diff --git a/core/java/android/content/pm/parsing/ParsingPackageImpl.java b/core/java/android/content/pm/parsing/ParsingPackageImpl.java
index 894ad55..be1817d 100644
--- a/core/java/android/content/pm/parsing/ParsingPackageImpl.java
+++ b/core/java/android/content/pm/parsing/ParsingPackageImpl.java
@@ -322,7 +322,12 @@
     private String className;
     private int compatibleWidthLimitDp;
     private int descriptionRes;
-    private boolean enabled;
+
+    // Usually there's code to set this to true during parsing, but it's possible to install an APK
+    // targeting <R that doesn't contain an <application> tag. That code would be skipped and never
+    // assign this, so initialize this to true for those cases.
+    private boolean enabled = true;
+
     private boolean crossProfile;
     private int fullBackupContent;
     private int iconRes;
diff --git a/core/java/android/inputmethodservice/IInputMethodWrapper.java b/core/java/android/inputmethodservice/IInputMethodWrapper.java
index b52b437..a298c85 100644
--- a/core/java/android/inputmethodservice/IInputMethodWrapper.java
+++ b/core/java/android/inputmethodservice/IInputMethodWrapper.java
@@ -18,6 +18,7 @@
 
 import android.annotation.BinderThread;
 import android.annotation.MainThread;
+import android.annotation.Nullable;
 import android.compat.annotation.UnsupportedAppUsage;
 import android.content.Context;
 import android.content.pm.PackageManager;
@@ -37,6 +38,7 @@
 import android.view.inputmethod.InputMethodSubtype;
 
 import com.android.internal.inputmethod.IInputMethodPrivilegedOperations;
+import com.android.internal.inputmethod.CancellationGroup;
 import com.android.internal.os.HandlerCaller;
 import com.android.internal.os.SomeArgs;
 import com.android.internal.view.IInlineSuggestionsRequestCallback;
@@ -52,7 +54,6 @@
 import java.lang.ref.WeakReference;
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.TimeUnit;
-import java.util.concurrent.atomic.AtomicBoolean;
 
 /**
  * Implements the internal IInputMethod interface to convert incoming calls
@@ -90,12 +91,13 @@
      *
      * <p>This field must be set and cleared only from the binder thread(s), where the system
      * guarantees that {@link #bindInput(InputBinding)},
-     * {@link #startInput(IBinder, IInputContext, int, EditorInfo, boolean)}, and
+     * {@link #startInput(IBinder, IInputContext, int, EditorInfo, boolean, boolean)}, and
      * {@link #unbindInput()} are called with the same order as the original calls
      * in {@link com.android.server.inputmethod.InputMethodManagerService}.
      * See {@link IBinder#FLAG_ONEWAY} for detailed semantics.</p>
      */
-    AtomicBoolean mIsUnbindIssued = null;
+    @Nullable
+    CancellationGroup mCancellationGroup = null;
 
     // NOTE: we should have a cache of these.
     static final class InputMethodSessionCallbackWrapper implements InputMethod.SessionCallback {
@@ -187,11 +189,11 @@
                 final IBinder startInputToken = (IBinder) args.arg1;
                 final IInputContext inputContext = (IInputContext) args.arg2;
                 final EditorInfo info = (EditorInfo) args.arg3;
-                final AtomicBoolean isUnbindIssued = (AtomicBoolean) args.arg4;
+                final CancellationGroup cancellationGroup = (CancellationGroup) args.arg4;
                 SomeArgs moreArgs = (SomeArgs) args.arg5;
                 final InputConnection ic = inputContext != null
                         ? new InputConnectionWrapper(
-                                mTarget, inputContext, moreArgs.argi3, isUnbindIssued)
+                                mTarget, inputContext, moreArgs.argi3, cancellationGroup)
                         : null;
                 info.makeCompatible(mTargetSdkVersion);
                 inputMethod.dispatchStartInputWithToken(
@@ -295,15 +297,15 @@
     @BinderThread
     @Override
     public void bindInput(InputBinding binding) {
-        if (mIsUnbindIssued != null) {
+        if (mCancellationGroup != null) {
             Log.e(TAG, "bindInput must be paired with unbindInput.");
         }
-        mIsUnbindIssued = new AtomicBoolean();
+        mCancellationGroup = new CancellationGroup();
         // This IInputContext is guaranteed to implement all the methods.
         final int missingMethodFlags = 0;
         InputConnection ic = new InputConnectionWrapper(mTarget,
                 IInputContext.Stub.asInterface(binding.getConnectionToken()), missingMethodFlags,
-                mIsUnbindIssued);
+                mCancellationGroup);
         InputBinding nu = new InputBinding(ic, binding);
         mCaller.executeOrSendMessage(mCaller.obtainMessageO(DO_SET_INPUT_CONTEXT, nu));
     }
@@ -311,10 +313,10 @@
     @BinderThread
     @Override
     public void unbindInput() {
-        if (mIsUnbindIssued != null) {
+        if (mCancellationGroup != null) {
             // Signal the flag then forget it.
-            mIsUnbindIssued.set(true);
-            mIsUnbindIssued = null;
+            mCancellationGroup.cancelAll();
+            mCancellationGroup = null;
         } else {
             Log.e(TAG, "unbindInput must be paired with bindInput.");
         }
@@ -326,16 +328,16 @@
     public void startInput(IBinder startInputToken, IInputContext inputContext,
             @InputConnectionInspector.MissingMethodFlags final int missingMethods,
             EditorInfo attribute, boolean restarting, boolean shouldPreRenderIme) {
-        if (mIsUnbindIssued == null) {
+        if (mCancellationGroup == null) {
             Log.e(TAG, "startInput must be called after bindInput.");
-            mIsUnbindIssued = new AtomicBoolean();
+            mCancellationGroup = new CancellationGroup();
         }
         SomeArgs args = SomeArgs.obtain();
         args.argi1 = restarting ? 1 : 0;
         args.argi2 = shouldPreRenderIme ? 1 : 0;
         args.argi3 = missingMethods;
-        mCaller.executeOrSendMessage(mCaller.obtainMessageOOOOO(
-                DO_START_INPUT, startInputToken, inputContext, attribute, mIsUnbindIssued, args));
+        mCaller.executeOrSendMessage(mCaller.obtainMessageOOOOO(DO_START_INPUT, startInputToken,
+                inputContext, attribute, mCancellationGroup, args));
     }
 
     @BinderThread
diff --git a/core/java/android/inputmethodservice/MultiClientInputMethodClientCallbackAdaptor.java b/core/java/android/inputmethodservice/MultiClientInputMethodClientCallbackAdaptor.java
index ef138a0..dbb669b 100644
--- a/core/java/android/inputmethodservice/MultiClientInputMethodClientCallbackAdaptor.java
+++ b/core/java/android/inputmethodservice/MultiClientInputMethodClientCallbackAdaptor.java
@@ -39,6 +39,7 @@
 
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.inputmethod.IMultiClientInputMethodSession;
+import com.android.internal.inputmethod.CancellationGroup;
 import com.android.internal.os.SomeArgs;
 import com.android.internal.util.function.pooled.PooledLambda;
 import com.android.internal.view.IInputContext;
@@ -46,7 +47,6 @@
 import com.android.internal.view.InputConnectionWrapper;
 
 import java.lang.ref.WeakReference;
-import java.util.concurrent.atomic.AtomicBoolean;
 
 /**
  * Re-dispatches all the incoming per-client events to the specified {@link Looper} thread.
@@ -80,19 +80,19 @@
     @Nullable
     InputEventReceiver mInputEventReceiver;
 
-    private final AtomicBoolean mFinished = new AtomicBoolean(false);
+    private final CancellationGroup mCancellationGroup = new CancellationGroup();
 
     IInputMethodSession.Stub createIInputMethodSession() {
         synchronized (mSessionLock) {
             return new InputMethodSessionImpl(
-                    mSessionLock, mCallbackImpl, mHandler, mFinished);
+                    mSessionLock, mCallbackImpl, mHandler, mCancellationGroup);
         }
     }
 
     IMultiClientInputMethodSession.Stub createIMultiClientInputMethodSession() {
         synchronized (mSessionLock) {
             return new MultiClientInputMethodSessionImpl(
-                    mSessionLock, mCallbackImpl, mHandler, mFinished);
+                    mSessionLock, mCallbackImpl, mHandler, mCancellationGroup);
         }
     }
 
@@ -105,7 +105,7 @@
             mHandler = new Handler(looper, null, true);
             mReadChannel = readChannel;
             mInputEventReceiver = new ImeInputEventReceiver(mReadChannel, mHandler.getLooper(),
-                    mFinished, mDispatcherState, mCallbackImpl.mOriginalCallback);
+                    mCancellationGroup, mDispatcherState, mCallbackImpl.mOriginalCallback);
         }
     }
 
@@ -139,16 +139,17 @@
     }
 
     private static final class ImeInputEventReceiver extends InputEventReceiver {
-        private final AtomicBoolean mFinished;
+        private final CancellationGroup mCancellationGroupOnFinishSession;
         private final KeyEvent.DispatcherState mDispatcherState;
         private final MultiClientInputMethodServiceDelegate.ClientCallback mClientCallback;
         private final KeyEventCallbackAdaptor mKeyEventCallbackAdaptor;
 
-        ImeInputEventReceiver(InputChannel readChannel, Looper looper, AtomicBoolean finished,
+        ImeInputEventReceiver(InputChannel readChannel, Looper looper,
+                CancellationGroup cancellationGroupOnFinishSession,
                 KeyEvent.DispatcherState dispatcherState,
                 MultiClientInputMethodServiceDelegate.ClientCallback callback) {
             super(readChannel, looper);
-            mFinished = finished;
+            mCancellationGroupOnFinishSession = cancellationGroupOnFinishSession;
             mDispatcherState = dispatcherState;
             mClientCallback = callback;
             mKeyEventCallbackAdaptor = new KeyEventCallbackAdaptor(callback);
@@ -156,7 +157,7 @@
 
         @Override
         public void onInputEvent(InputEvent event) {
-            if (mFinished.get()) {
+            if (mCancellationGroupOnFinishSession.isCanceled()) {
                 // The session has been finished.
                 finishInputEvent(event, false);
                 return;
@@ -187,14 +188,14 @@
         private CallbackImpl mCallbackImpl;
         @GuardedBy("mSessionLock")
         private Handler mHandler;
-        private final AtomicBoolean mSessionFinished;
+        private final CancellationGroup mCancellationGroupOnFinishSession;
 
         InputMethodSessionImpl(Object lock, CallbackImpl callback, Handler handler,
-                AtomicBoolean sessionFinished) {
+                CancellationGroup cancellationGroupOnFinishSession) {
             mSessionLock = lock;
             mCallbackImpl = callback;
             mHandler = handler;
-            mSessionFinished = sessionFinished;
+            mCancellationGroupOnFinishSession = cancellationGroupOnFinishSession;
         }
 
         @Override
@@ -272,7 +273,7 @@
                 if (mCallbackImpl == null || mHandler == null) {
                     return;
                 }
-                mSessionFinished.set(true);
+                mCancellationGroupOnFinishSession.cancelAll();
                 mHandler.sendMessage(PooledLambda.obtainMessage(
                         CallbackImpl::finishSession, mCallbackImpl));
                 mCallbackImpl = null;
@@ -311,14 +312,14 @@
         private CallbackImpl mCallbackImpl;
         @GuardedBy("mSessionLock")
         private Handler mHandler;
-        private final AtomicBoolean mSessionFinished;
+        private final CancellationGroup mCancellationGroupOnFinishSession;
 
         MultiClientInputMethodSessionImpl(Object lock, CallbackImpl callback,
-                Handler handler, AtomicBoolean sessionFinished) {
+                Handler handler, CancellationGroup cancellationGroupOnFinishSession) {
             mSessionLock = lock;
             mCallbackImpl = callback;
             mHandler = handler;
-            mSessionFinished = sessionFinished;
+            mCancellationGroupOnFinishSession = cancellationGroupOnFinishSession;
         }
 
         @Override
@@ -335,7 +336,7 @@
                         new WeakReference<>(null);
                 args.arg1 = (inputContext == null) ? null
                         : new InputConnectionWrapper(fakeIMS, inputContext, missingMethods,
-                                mSessionFinished);
+                                mCancellationGroupOnFinishSession);
                 args.arg2 = editorInfo;
                 args.argi1 = controlFlags;
                 args.argi2 = softInputMode;
diff --git a/core/java/android/net/NetworkCapabilities.java b/core/java/android/net/NetworkCapabilities.java
index 9ff7ebe..73c6b3d 100644
--- a/core/java/android/net/NetworkCapabilities.java
+++ b/core/java/android/net/NetworkCapabilities.java
@@ -169,6 +169,7 @@
             NET_CAPABILITY_OEM_PAID,
             NET_CAPABILITY_MCX,
             NET_CAPABILITY_PARTIAL_CONNECTIVITY,
+            NET_CAPABILITY_TEMPORARILY_NOT_METERED,
     })
     public @interface NetCapability { }
 
@@ -336,8 +337,16 @@
     @SystemApi
     public static final int NET_CAPABILITY_PARTIAL_CONNECTIVITY = 24;
 
+    /**
+     * This capability will be set for networks that are generally metered, but are currently
+     * unmetered, e.g., because the user is in a particular area. This capability can be changed at
+     * any time. When it is removed, applications are responsible for stopping any data transfer
+     * that should not occur on a metered network.
+     */
+    public static final int NET_CAPABILITY_TEMPORARILY_NOT_METERED = 25;
+
     private static final int MIN_NET_CAPABILITY = NET_CAPABILITY_MMS;
-    private static final int MAX_NET_CAPABILITY = NET_CAPABILITY_PARTIAL_CONNECTIVITY;
+    private static final int MAX_NET_CAPABILITY = NET_CAPABILITY_TEMPORARILY_NOT_METERED;
 
     /**
      * Network capabilities that are expected to be mutable, i.e., can change while a particular
@@ -353,7 +362,8 @@
             | (1 << NET_CAPABILITY_FOREGROUND)
             | (1 << NET_CAPABILITY_NOT_CONGESTED)
             | (1 << NET_CAPABILITY_NOT_SUSPENDED)
-            | (1 << NET_CAPABILITY_PARTIAL_CONNECTIVITY);
+            | (1 << NET_CAPABILITY_PARTIAL_CONNECTIVITY
+            | (1 << NET_CAPABILITY_TEMPORARILY_NOT_METERED));
 
     /**
      * Network capabilities that are not allowed in NetworkRequests. This exists because the
@@ -424,6 +434,7 @@
      */
     private static final long TEST_NETWORKS_ALLOWED_CAPABILITIES =
             (1 << NET_CAPABILITY_NOT_METERED)
+            | (1 << NET_CAPABILITY_TEMPORARILY_NOT_METERED)
             | (1 << NET_CAPABILITY_NOT_RESTRICTED)
             | (1 << NET_CAPABILITY_NOT_VPN)
             | (1 << NET_CAPABILITY_NOT_ROAMING)
@@ -1864,6 +1875,7 @@
             case NET_CAPABILITY_OEM_PAID:             return "OEM_PAID";
             case NET_CAPABILITY_MCX:                  return "MCX";
             case NET_CAPABILITY_PARTIAL_CONNECTIVITY: return "PARTIAL_CONNECTIVITY";
+            case NET_CAPABILITY_TEMPORARILY_NOT_METERED:    return "TEMPORARILY_NOT_METERED";
             default:                                  return Integer.toString(capability);
         }
     }
diff --git a/core/java/android/provider/DocumentsProvider.java b/core/java/android/provider/DocumentsProvider.java
index 327bca2..2e00c0c 100644
--- a/core/java/android/provider/DocumentsProvider.java
+++ b/core/java/android/provider/DocumentsProvider.java
@@ -1274,6 +1274,8 @@
 
             out.putParcelable(DocumentsContract.EXTRA_RESULT, path);
         } else if (METHOD_GET_DOCUMENT_METADATA.equals(method)) {
+            enforceReadPermissionInner(documentUri, getCallingPackage(),
+                    getCallingAttributionTag(), null);
             return getDocumentMetadata(documentId);
         } else {
             throw new UnsupportedOperationException("Method not supported " + method);
diff --git a/core/java/android/widget/TEST_MAPPING b/core/java/android/widget/TEST_MAPPING
index d0b8dbc..f089f48 100644
--- a/core/java/android/widget/TEST_MAPPING
+++ b/core/java/android/widget/TEST_MAPPING
@@ -13,7 +13,7 @@
       "name": "CtsWindowManagerDeviceTestCases",
       "options": [
         {
-          "include-filter": "android.server.wm.ToastTest"
+          "include-filter": "android.server.wm.ToastWindowTest"
         }
       ],
       "file_patterns": ["Toast\\.java"]
diff --git a/core/java/com/android/internal/app/ChooserActivity.java b/core/java/com/android/internal/app/ChooserActivity.java
index dca682e..10c7428 100644
--- a/core/java/com/android/internal/app/ChooserActivity.java
+++ b/core/java/com/android/internal/app/ChooserActivity.java
@@ -124,6 +124,7 @@
 import com.android.internal.content.PackageMonitor;
 import com.android.internal.logging.MetricsLogger;
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
+import com.android.internal.util.FrameworkStatsLog;
 import com.android.internal.widget.GridLayoutManager;
 import com.android.internal.widget.RecyclerView;
 import com.android.internal.widget.ResolverDrawerLayout;
@@ -178,7 +179,7 @@
     private static final boolean USE_PREDICTION_MANAGER_FOR_SHARE_ACTIVITIES = true;
     // TODO(b/123088566) Share these in a better way.
     private static final String APP_PREDICTION_SHARE_UI_SURFACE = "share";
-    public static final String LAUNCH_LOCATON_DIRECT_SHARE = "direct_share";
+    public static final String LAUNCH_LOCATION_DIRECT_SHARE = "direct_share";
     private static final int APP_PREDICTION_SHARE_TARGET_QUERY_PACKAGE_LIMIT = 20;
     public static final String APP_PREDICTION_INTENT_FILTER_KEY = "intent_filter";
 
@@ -194,6 +195,14 @@
     public static final int TARGET_TYPE_SHORTCUTS_FROM_SHORTCUT_MANAGER = 2;
     public static final int TARGET_TYPE_SHORTCUTS_FROM_PREDICTION_SERVICE = 3;
 
+    public static final int SELECTION_TYPE_SERVICE = 1;
+    public static final int SELECTION_TYPE_APP = 2;
+    public static final int SELECTION_TYPE_STANDARD = 3;
+    public static final int SELECTION_TYPE_COPY = 4;
+
+    // statsd logger wrapper
+    protected ChooserActivityLogger mChooserActivityLogger;
+
     private static final boolean USE_CHOOSER_TARGET_SERVICE_FOR_DIRECT_TARGETS = true;
 
     @IntDef(flag = false, prefix = { "TARGET_TYPE_" }, value = {
@@ -270,9 +279,9 @@
 
     // Starting at 1 since 0 is considered "undefined" for some of the database transformations
     // of tron logs.
-    private static final int CONTENT_PREVIEW_IMAGE = 1;
-    private static final int CONTENT_PREVIEW_FILE = 2;
-    private static final int CONTENT_PREVIEW_TEXT = 3;
+    protected static final int CONTENT_PREVIEW_IMAGE = 1;
+    protected static final int CONTENT_PREVIEW_FILE = 2;
+    protected static final int CONTENT_PREVIEW_TEXT = 3;
     protected MetricsLogger mMetricsLogger;
 
     private ContentPreviewCoordinator mPreviewCoord;
@@ -500,6 +509,9 @@
 
                 case CHOOSER_TARGET_SERVICE_WATCHDOG_MAX_TIMEOUT:
                     mMinTimeoutPassed = true;
+                    if (!mServiceConnections.isEmpty()) {
+                        getChooserActivityLogger().logSharesheetDirectLoadTimeout();
+                    }
                     unbindRemainingServices();
                     maybeStopServiceRequestTimer();
                     break;
@@ -533,6 +545,7 @@
                     logDirectShareTargetReceived(
                             MetricsEvent.ACTION_DIRECT_SHARE_TARGETS_LOADED_SHORTCUT_MANAGER);
                     sendVoiceChoicesIfNeeded();
+                    getChooserActivityLogger().logSharesheetDirectLoadComplete();
                     break;
 
                 default:
@@ -544,6 +557,7 @@
     @Override
     protected void onCreate(Bundle savedInstanceState) {
         final long intentReceivedTime = System.currentTimeMillis();
+        getChooserActivityLogger().logSharesheetTriggered();
         // This is the only place this value is being set. Effectively final.
         mIsAppPredictorComponentAvailable = isAppPredictionServiceAvailable();
 
@@ -707,6 +721,8 @@
                                 incrementNumSheetExpansions();
                                 mWrittenOnce = true;
                             }
+                            getChooserActivityLogger()
+                                    .logSharesheetExpansionChanged(isCollapsed);
                         }
                     });
         }
@@ -715,6 +731,16 @@
             Log.d(TAG, "System Time Cost is " + systemCost);
         }
 
+        getChooserActivityLogger().logShareStarted(
+                FrameworkStatsLog.SHARESHEET_STARTED,
+                getReferrerPackageName(),
+                target.getType(),
+                initialIntents == null ? 0 : initialIntents.length,
+                mCallerChooserTargets == null ? 0 : mCallerChooserTargets.length,
+                isWorkProfile(),
+                findPreferredContentPreview(getTargetIntent(), getContentResolver()),
+                target.getAction()
+        );
         mDirectShareShortcutInfoCache = new HashMap<>();
     }
 
@@ -969,6 +995,10 @@
             LogMaker targetLogMaker = new LogMaker(
                     MetricsEvent.ACTION_ACTIVITY_CHOOSER_PICKED_SYSTEM_TARGET).setSubtype(1);
             getMetricsLogger().write(targetLogMaker);
+            getChooserActivityLogger().logShareTargetSelected(
+                    SELECTION_TYPE_COPY,
+                    "",
+                    -1);
 
             finish();
         }
@@ -1644,18 +1674,33 @@
                     if (mCallerChooserTargets != null) {
                         numCallerProvided = mCallerChooserTargets.length;
                     }
+                    getChooserActivityLogger().logShareTargetSelected(
+                            SELECTION_TYPE_SERVICE,
+                            targetInfo.getResolveInfo().activityInfo.processName,
+                            value
+                    );
                     break;
                 case ChooserListAdapter.TARGET_CALLER:
                 case ChooserListAdapter.TARGET_STANDARD:
                     cat = MetricsEvent.ACTION_ACTIVITY_CHOOSER_PICKED_APP_TARGET;
                     value -= currentListAdapter.getSelectableServiceTargetCount();
                     numCallerProvided = currentListAdapter.getCallerTargetCount();
+                    getChooserActivityLogger().logShareTargetSelected(
+                            SELECTION_TYPE_APP,
+                            targetInfo.getResolveInfo().activityInfo.processName,
+                            value
+                    );
                     break;
                 case ChooserListAdapter.TARGET_STANDARD_AZ:
                     // A-Z targets are unranked standard targets; we use -1 to mark that they
                     // are from the alphabetical pool.
                     value = -1;
                     cat = MetricsEvent.ACTION_ACTIVITY_CHOOSER_PICKED_STANDARD_TARGET;
+                    getChooserActivityLogger().logShareTargetSelected(
+                            SELECTION_TYPE_STANDARD,
+                            targetInfo.getResolveInfo().activityInfo.processName,
+                            value
+                    );
                     break;
             }
 
@@ -2131,7 +2176,7 @@
         if (appTarget != null) {
             directShareAppPredictor.notifyAppTargetEvent(
                     new AppTargetEvent.Builder(appTarget, AppTargetEvent.ACTION_LAUNCH)
-                        .setLaunchLocation(LAUNCH_LOCATON_DIRECT_SHARE)
+                        .setLaunchLocation(LAUNCH_LOCATION_DIRECT_SHARE)
                         .build());
         }
     }
@@ -2290,6 +2335,13 @@
         return mMetricsLogger;
     }
 
+    protected ChooserActivityLogger getChooserActivityLogger() {
+        if (mChooserActivityLogger == null) {
+            mChooserActivityLogger = new ChooserActivityLoggerImpl();
+        }
+        return mChooserActivityLogger;
+    }
+
     public class ChooserListController extends ResolverListController {
         public ChooserListController(Context context,
                 PackageManager pm,
@@ -2601,6 +2653,7 @@
 
         // don't support direct share on low ram devices
         if (ActivityManager.isLowRamDeviceStatic()) {
+            getChooserActivityLogger().logSharesheetAppLoadComplete();
             return;
         }
 
@@ -2619,6 +2672,8 @@
 
             queryTargetServices(chooserListAdapter);
         }
+
+        getChooserActivityLogger().logSharesheetAppLoadComplete();
     }
 
     private void setupScrollListener() {
@@ -3777,4 +3832,9 @@
             canvas.drawRoundRect(x, y, width, height, mRadius, mRadius, mRoundRectPaint);
         }
     }
+
+    @Override
+    protected void maybeLogProfileChange() {
+        getChooserActivityLogger().logShareheetProfileChanged();
+    }
 }
diff --git a/core/java/com/android/internal/app/ChooserActivityLogger.java b/core/java/com/android/internal/app/ChooserActivityLogger.java
new file mode 100644
index 0000000..dc48244
--- /dev/null
+++ b/core/java/com/android/internal/app/ChooserActivityLogger.java
@@ -0,0 +1,215 @@
+/*
+ * Copyright (C) 2020 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.internal.app;
+
+import android.content.Intent;
+import android.provider.MediaStore;
+
+import com.android.internal.logging.InstanceId;
+import com.android.internal.logging.UiEvent;
+import com.android.internal.logging.UiEventLogger;
+import com.android.internal.util.FrameworkStatsLog;
+
+/**
+ * Interface for writing Sharesheet atoms to statsd log.
+ * @hide
+ */
+public interface ChooserActivityLogger {
+    /** Logs a UiEventReported event for the system sharesheet completing initial start-up. */
+    void logShareStarted(int eventId, String packageName, String mimeType, int appProvidedDirect,
+            int appProvidedApp, boolean isWorkprofile, int previewType, String intent);
+
+    /** Logs a UiEventReported event for the system sharesheet when the user selects a target. */
+    void logShareTargetSelected(int targetType, String packageName, int positionPicked);
+
+    /** Logs a UiEventReported event for the system sharesheet being triggered by the user. */
+    default void logSharesheetTriggered() {
+        log(SharesheetStandardEvent.SHARESHEET_TRIGGERED, getInstanceId());
+    }
+
+    /** Logs a UiEventReported event for the system sharesheet completing loading app targets. */
+    default void logSharesheetAppLoadComplete() {
+        log(SharesheetStandardEvent.SHARESHEET_APP_LOAD_COMPLETE, getInstanceId());
+    }
+
+    /**
+     * Logs a UiEventReported event for the system sharesheet completing loading service targets.
+     */
+    default void logSharesheetDirectLoadComplete() {
+        log(SharesheetStandardEvent.SHARESHEET_DIRECT_LOAD_COMPLETE, getInstanceId());
+    }
+
+    /**
+     * Logs a UiEventReported event for the system sharesheet timing out loading service targets.
+     */
+    default void logSharesheetDirectLoadTimeout() {
+        log(SharesheetStandardEvent.SHARESHEET_DIRECT_LOAD_TIMEOUT, getInstanceId());
+    }
+
+    /**
+     * Logs a UiEventReported event for the system sharesheet switching
+     * between work and main profile.
+     */
+    default void logShareheetProfileChanged() {
+        log(SharesheetStandardEvent.SHARESHEET_PROFILE_CHANGED, getInstanceId());
+    }
+
+    /** Logs a UiEventReported event for the system sharesheet getting expanded or collapsed. */
+    default void logSharesheetExpansionChanged(boolean isCollapsed) {
+        log(isCollapsed ? SharesheetStandardEvent.SHARESHEET_COLLAPSED :
+                SharesheetStandardEvent.SHARESHEET_EXPANDED, getInstanceId());
+    }
+
+    /**
+     * Logs a UiEventReported event for a given share activity
+     * @param event
+     * @param instanceId
+     */
+    void log(UiEventLogger.UiEventEnum event, InstanceId instanceId);
+
+    /**
+     *
+     * @return
+     */
+    InstanceId getInstanceId();
+
+    /**
+     * The UiEvent enums that this class can log.
+     */
+    enum SharesheetStartedEvent implements UiEventLogger.UiEventEnum {
+        @UiEvent(doc = "Basic system Sharesheet has started and is visible.")
+        SHARE_STARTED(228);
+
+        private final int mId;
+        SharesheetStartedEvent(int id) {
+            mId = id;
+        }
+        @Override
+        public int getId() {
+            return mId;
+        }
+    }
+
+    /**
+     * The UiEvent enums that this class can log.
+     */
+    enum SharesheetTargetSelectedEvent implements UiEventLogger.UiEventEnum {
+        INVALID(0),
+        @UiEvent(doc = "User selected a service target.")
+        SHARESHEET_SERVICE_TARGET_SELECTED(232),
+        @UiEvent(doc = "User selected an app target.")
+        SHARESHEET_APP_TARGET_SELECTED(233),
+        @UiEvent(doc = "User selected a standard target.")
+        SHARESHEET_STANDARD_TARGET_SELECTED(234),
+        @UiEvent(doc = "User selected the copy target.")
+        SHARESHEET_COPY_TARGET_SELECTED(235);
+
+        private final int mId;
+        SharesheetTargetSelectedEvent(int id) {
+            mId = id;
+        }
+        @Override public int getId() {
+            return mId;
+        }
+
+        public static SharesheetTargetSelectedEvent fromTargetType(int targetType) {
+            switch(targetType) {
+                case ChooserActivity.SELECTION_TYPE_SERVICE:
+                    return SHARESHEET_SERVICE_TARGET_SELECTED;
+                case ChooserActivity.SELECTION_TYPE_APP:
+                    return SHARESHEET_APP_TARGET_SELECTED;
+                case ChooserActivity.SELECTION_TYPE_STANDARD:
+                    return SHARESHEET_STANDARD_TARGET_SELECTED;
+                case ChooserActivity.SELECTION_TYPE_COPY:
+                    return SHARESHEET_COPY_TARGET_SELECTED;
+                default:
+                    return INVALID;
+            }
+        }
+    }
+
+    /**
+     * The UiEvent enums that this class can log.
+     */
+    enum SharesheetStandardEvent implements UiEventLogger.UiEventEnum {
+        INVALID(0),
+        @UiEvent(doc = "User clicked share.")
+        SHARESHEET_TRIGGERED(227),
+        @UiEvent(doc = "User changed from work to personal profile or vice versa.")
+        SHARESHEET_PROFILE_CHANGED(229),
+        @UiEvent(doc = "User expanded target list.")
+        SHARESHEET_EXPANDED(230),
+        @UiEvent(doc = "User collapsed target list.")
+        SHARESHEET_COLLAPSED(231),
+        @UiEvent(doc = "Sharesheet app targets is fully populated.")
+        SHARESHEET_APP_LOAD_COMPLETE(322),
+        @UiEvent(doc = "Sharesheet direct targets is fully populated.")
+        SHARESHEET_DIRECT_LOAD_COMPLETE(323),
+        @UiEvent(doc = "Sharesheet direct targets timed out.")
+        SHARESHEET_DIRECT_LOAD_TIMEOUT(324);
+
+        private final int mId;
+        SharesheetStandardEvent(int id) {
+            mId = id;
+        }
+        @Override public int getId() {
+            return mId;
+        }
+    }
+
+    /**
+     * Returns the enum used in sharesheet started atom to indicate what preview type was used.
+     */
+    default int typeFromPreviewInt(int previewType) {
+        switch(previewType) {
+            case ChooserActivity.CONTENT_PREVIEW_IMAGE:
+                return FrameworkStatsLog.SHARESHEET_STARTED__PREVIEW_TYPE__CONTENT_PREVIEW_IMAGE;
+            case ChooserActivity.CONTENT_PREVIEW_FILE:
+                return FrameworkStatsLog.SHARESHEET_STARTED__PREVIEW_TYPE__CONTENT_PREVIEW_FILE;
+            case ChooserActivity.CONTENT_PREVIEW_TEXT:
+            default:
+                return FrameworkStatsLog.SHARESHEET_STARTED__PREVIEW_TYPE__CONTENT_PREVIEW_TEXT;
+        }
+    }
+
+    /**
+     * Returns the enum used in sharesheet started atom to indicate what intent triggers the
+     * ChooserActivity.
+     */
+    default int typeFromIntentString(String intent) {
+        switch (intent) {
+            case Intent.ACTION_VIEW:
+                return FrameworkStatsLog.SHARESHEET_STARTED__INTENT_TYPE__INTENT_ACTION_VIEW;
+            case Intent.ACTION_EDIT:
+                return FrameworkStatsLog.SHARESHEET_STARTED__INTENT_TYPE__INTENT_ACTION_EDIT;
+            case Intent.ACTION_SEND:
+                return FrameworkStatsLog.SHARESHEET_STARTED__INTENT_TYPE__INTENT_ACTION_SEND;
+            case Intent.ACTION_SENDTO:
+                return FrameworkStatsLog.SHARESHEET_STARTED__INTENT_TYPE__INTENT_ACTION_SENDTO;
+            case Intent.ACTION_SEND_MULTIPLE:
+                return FrameworkStatsLog
+                        .SHARESHEET_STARTED__INTENT_TYPE__INTENT_ACTION_SEND_MULTIPLE;
+            case MediaStore.ACTION_IMAGE_CAPTURE:
+                return FrameworkStatsLog
+                        .SHARESHEET_STARTED__INTENT_TYPE__INTENT_ACTION_IMAGE_CAPTURE;
+            case Intent.ACTION_MAIN:
+                return FrameworkStatsLog.SHARESHEET_STARTED__INTENT_TYPE__INTENT_ACTION_MAIN;
+            default:
+                return FrameworkStatsLog.SHARESHEET_STARTED__INTENT_TYPE__INTENT_DEFAULT;
+        }
+    }
+}
diff --git a/core/java/com/android/internal/app/ChooserActivityLoggerImpl.java b/core/java/com/android/internal/app/ChooserActivityLoggerImpl.java
new file mode 100644
index 0000000..48bdba3
--- /dev/null
+++ b/core/java/com/android/internal/app/ChooserActivityLoggerImpl.java
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2020 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.internal.app;
+
+import com.android.internal.logging.InstanceId;
+import com.android.internal.logging.InstanceIdSequence;
+import com.android.internal.logging.UiEventLogger;
+import com.android.internal.logging.UiEventLoggerImpl;
+import com.android.internal.util.FrameworkStatsLog;
+
+/**
+ * Standard implementation of ChooserActivityLogger interface.
+ * @hide
+ */
+public class ChooserActivityLoggerImpl implements ChooserActivityLogger {
+    private static final int SHARESHEET_INSTANCE_ID_MAX = (1 << 13);
+
+    private UiEventLogger mUiEventLogger = new UiEventLoggerImpl();
+    // A small per-notification ID, used for statsd logging.
+    private InstanceId mInstanceId;
+    private static InstanceIdSequence sInstanceIdSequence;
+
+    @Override
+    public void logShareStarted(int eventId, String packageName, String mimeType,
+            int appProvidedDirect, int appProvidedApp, boolean isWorkprofile, int previewType,
+            String intent) {
+        FrameworkStatsLog.write(FrameworkStatsLog.SHARESHEET_STARTED,
+                /* event_id = 1 */ SharesheetStartedEvent.SHARE_STARTED.getId(),
+                /* package_name = 2 */ packageName,
+                /* instance_id = 3 */ getInstanceId().getId(),
+                /* mime_type = 4 */ mimeType,
+                /* num_app_provided_direct_targets = 5 */ appProvidedDirect,
+                /* num_app_provided_app_targets = 6 */ appProvidedApp,
+                /* is_workprofile = 7 */ isWorkprofile,
+                /* previewType = 8 */ typeFromPreviewInt(previewType),
+                /* intentType = 9 */ typeFromIntentString(intent));
+    }
+
+    @Override
+    public void logShareTargetSelected(int targetType, String packageName, int positionPicked) {
+        FrameworkStatsLog.write(FrameworkStatsLog.RANKING_SELECTED,
+                /* event_id = 1 */ SharesheetTargetSelectedEvent.fromTargetType(targetType).getId(),
+                /* package_name = 2 */ packageName,
+                /* instance_id = 3 */ getInstanceId().getId(),
+                /* position_picked = 4 */ positionPicked);
+    }
+
+    @Override
+    public void log(UiEventLogger.UiEventEnum event, InstanceId instanceId) {
+        mUiEventLogger.logWithInstanceId(
+                event,
+                0,
+                null,
+                instanceId);
+    }
+
+    @Override
+    public InstanceId getInstanceId() {
+        if (mInstanceId == null) {
+            if (sInstanceIdSequence == null) {
+                sInstanceIdSequence = new InstanceIdSequence(SHARESHEET_INSTANCE_ID_MAX);
+            }
+            mInstanceId = sInstanceIdSequence.newInstanceId();
+        }
+        return mInstanceId;
+    }
+
+}
diff --git a/core/java/com/android/internal/app/ResolverActivity.java b/core/java/com/android/internal/app/ResolverActivity.java
index f088ab3..35253b6 100644
--- a/core/java/com/android/internal/app/ResolverActivity.java
+++ b/core/java/com/android/internal/app/ResolverActivity.java
@@ -1578,6 +1578,7 @@
                 viewPager.setCurrentItem(1);
             }
             setupViewVisibilities();
+            maybeLogProfileChange();
             DevicePolicyEventLogger
                     .createEvent(DevicePolicyEnums.RESOLVER_SWITCH_TABS)
                     .setInt(viewPager.getCurrentItem())
@@ -1998,4 +1999,6 @@
             }
         }
     }
+
+    protected void maybeLogProfileChange() {}
 }
diff --git a/core/java/com/android/internal/inputmethod/CancellationGroup.java b/core/java/com/android/internal/inputmethod/CancellationGroup.java
new file mode 100644
index 0000000..09c9d12
--- /dev/null
+++ b/core/java/com/android/internal/inputmethod/CancellationGroup.java
@@ -0,0 +1,348 @@
+/*
+ * Copyright (C) 2020 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.internal.inputmethod;
+
+import android.annotation.AnyThread;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+
+import com.android.internal.annotations.GuardedBy;
+
+import java.util.ArrayList;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * A utility class, which works as both a factory class of completable objects and a cancellation
+ * signal to cancel all the completable objects created by this object.
+ */
+public final class CancellationGroup {
+    private final Object mLock = new Object();
+
+    /**
+     * List of {@link CountDownLatch}, which can be used to propagate {@link #cancelAll()} to
+     * completable objects.
+     *
+     * <p>This will be lazily instantiated to avoid unnecessary object allocations.</p>
+     */
+    @Nullable
+    @GuardedBy("mLock")
+    private ArrayList<CountDownLatch> mLatchList = null;
+
+    @GuardedBy("mLock")
+    private boolean mCanceled = false;
+
+    /**
+     * An inner class to consolidate completable object types supported by
+     * {@link CancellationGroup}.
+     */
+    public static final class Completable {
+
+        /**
+         * Not intended to be instantiated.
+         */
+        private Completable() {
+        }
+
+        /**
+         * Base class of all the completable types supported by {@link CancellationGroup}.
+         */
+        protected static class ValueBase {
+            /**
+             * {@link CountDownLatch} to be signaled to unblock {@link #await(int, TimeUnit)}.
+             */
+            private final CountDownLatch mLatch = new CountDownLatch(1);
+
+            /**
+             * {@link CancellationGroup} to which this completable object belongs.
+             */
+            @NonNull
+            private final CancellationGroup mParentGroup;
+
+            /**
+             * Lock {@link Object} to guard complete operations within this class.
+             */
+            protected final Object mValueLock = new Object();
+
+            /**
+             * {@code true} after {@link #onComplete()} gets called.
+             */
+            @GuardedBy("mValueLock")
+            protected boolean mHasValue = false;
+
+            /**
+             * Base constructor.
+             *
+             * @param parentGroup {@link CancellationGroup} to which this completable object
+             *                    belongs.
+             */
+            protected ValueBase(@NonNull CancellationGroup parentGroup) {
+                mParentGroup = parentGroup;
+            }
+
+            /**
+             * @return {@link true} if {@link #onComplete()} gets called already.
+             */
+            @AnyThread
+            public boolean hasValue() {
+                synchronized (mValueLock) {
+                    return mHasValue;
+                }
+            }
+
+            /**
+             * Called by subclasses to signale {@link #mLatch}.
+             */
+            @AnyThread
+            protected void onComplete() {
+                mLatch.countDown();
+            }
+
+            /**
+             * Blocks the calling thread until at least one of the following conditions is met.
+             *
+             * <p>
+             *     <ol>
+             *         <li>This object becomes ready to return the value.</li>
+             *         <li>{@link CancellationGroup#cancelAll()} gets called.</li>
+             *         <li>The given timeout period has passed.</li>
+             *     </ol>
+             * </p>
+             *
+             * <p>The caller can distinguish the case 1 and case 2 by calling {@link #hasValue()}.
+             * Note that the return value of {@link #hasValue()} can change from {@code false} to
+             * {@code true} at any time, even after this methods finishes with returning
+             * {@code true}.</p>
+             *
+             * @param timeout length of the timeout.
+             * @param timeUnit unit of {@code timeout}.
+             * @return {@code false} if and only if the given timeout period has passed. Otherwise
+             *         {@code true}.
+             */
+            @AnyThread
+            public boolean await(int timeout, @NonNull TimeUnit timeUnit) {
+                if (!mParentGroup.registerLatch(mLatch)) {
+                    // Already canceled when this method gets called.
+                    return false;
+                }
+                try {
+                    return mLatch.await(timeout, timeUnit);
+                } catch (InterruptedException e) {
+                    return true;
+                } finally {
+                    mParentGroup.unregisterLatch(mLatch);
+                }
+            }
+        }
+
+        /**
+         * Completable object of integer primitive.
+         */
+        public static final class Int extends ValueBase {
+            @GuardedBy("mValueLock")
+            private int mValue = 0;
+
+            private Int(@NonNull CancellationGroup factory) {
+                super(factory);
+            }
+
+            /**
+             * Notify when a value is set to this completable object.
+             *
+             * @param value value to be set.
+             */
+            @AnyThread
+            void onComplete(int value) {
+                synchronized (mValueLock) {
+                    if (mHasValue) {
+                        throw new UnsupportedOperationException(
+                                "onComplete() cannot be called multiple times");
+                    }
+                    mValue = value;
+                    mHasValue = true;
+                }
+                onComplete();
+            }
+
+            /**
+             * @return value associated with this object.
+             * @throws UnsupportedOperationException when called while {@link #hasValue()} returns
+             *                                       {@code false}.
+             */
+            @AnyThread
+            public int getValue() {
+                synchronized (mValueLock) {
+                    if (!mHasValue) {
+                        throw new UnsupportedOperationException(
+                                "getValue() is allowed only if hasValue() returns true");
+                    }
+                    return mValue;
+                }
+            }
+        }
+
+        /**
+         * Base class of completable object types.
+         *
+         * @param <T> type associated with this completable object.
+         */
+        public static class Values<T> extends ValueBase {
+            @GuardedBy("mValueLock")
+            @Nullable
+            private T mValue = null;
+
+            protected Values(@NonNull CancellationGroup factory) {
+                super(factory);
+            }
+
+            /**
+             * Notify when a value is set to this completable value object.
+             *
+             * @param value value to be set.
+             */
+            @AnyThread
+            void onComplete(@Nullable T value) {
+                synchronized (mValueLock) {
+                    if (mHasValue) {
+                        throw new UnsupportedOperationException(
+                                "onComplete() cannot be called multiple times");
+                    }
+                    mValue = value;
+                    mHasValue = true;
+                }
+                onComplete();
+            }
+
+            /**
+             * @return value associated with this object.
+             * @throws UnsupportedOperationException when called while {@link #hasValue()} returns
+             *                                       {@code false}.
+             */
+            @AnyThread
+            @Nullable
+            public T getValue() {
+                synchronized (mValueLock) {
+                    if (!mHasValue) {
+                        throw new UnsupportedOperationException(
+                                "getValue() is allowed only if hasValue() returns true");
+                    }
+                    return mValue;
+                }
+            }
+        }
+
+        /**
+         * Completable object of {@link java.lang.CharSequence}.
+         */
+        public static final class CharSequence extends Values<java.lang.CharSequence> {
+            private CharSequence(@NonNull CancellationGroup factory) {
+                super(factory);
+            }
+        }
+
+        /**
+         * Completable object of {@link android.view.inputmethod.ExtractedText}.
+         */
+        public static final class ExtractedText
+                extends Values<android.view.inputmethod.ExtractedText> {
+            private ExtractedText(@NonNull CancellationGroup factory) {
+                super(factory);
+            }
+        }
+    }
+
+    /**
+     * @return an instance of {@link Completable.Int} that is associated with this
+     *         {@link CancellationGroup}.
+     */
+    @AnyThread
+    public Completable.Int createCompletableInt() {
+        return new Completable.Int(this);
+    }
+
+    /**
+     * @return an instance of {@link Completable.CharSequence} that is associated with this
+     *         {@link CancellationGroup}.
+     */
+    @AnyThread
+    public Completable.CharSequence createCompletableCharSequence() {
+        return new Completable.CharSequence(this);
+    }
+
+    /**
+     * @return an instance of {@link Completable.ExtractedText} that is associated with this
+     *         {@link CancellationGroup}.
+     */
+    @AnyThread
+    public Completable.ExtractedText createCompletableExtractedText() {
+        return new Completable.ExtractedText(this);
+    }
+
+    @AnyThread
+    private boolean registerLatch(@NonNull CountDownLatch latch) {
+        synchronized (mLock) {
+            if (mCanceled) {
+                return false;
+            }
+            if (mLatchList == null) {
+                // Set the initial capacity to 1 with an assumption that usually there is up to 1
+                // on-going operation.
+                mLatchList = new ArrayList<>(1);
+            }
+            mLatchList.add(latch);
+            return true;
+        }
+    }
+
+    @AnyThread
+    private void unregisterLatch(@NonNull CountDownLatch latch) {
+        synchronized (mLock) {
+            if (mLatchList != null) {
+                mLatchList.remove(latch);
+            }
+        }
+    }
+
+    /**
+     * Cancel all the completable objects created from this {@link CancellationGroup}.
+     *
+     * <p>Secondary calls will be silently ignored.</p>
+     */
+    @AnyThread
+    public void cancelAll() {
+        synchronized (mLock) {
+            if (!mCanceled) {
+                mCanceled = true;
+                if (mLatchList != null) {
+                    mLatchList.forEach(CountDownLatch::countDown);
+                    mLatchList.clear();
+                    mLatchList = null;
+                }
+            }
+        }
+    }
+
+    /**
+     * @return {@code true} if {@link #cancelAll()} is already called. {@code false} otherwise.
+     */
+    @AnyThread
+    public boolean isCanceled() {
+        synchronized (mLock) {
+            return mCanceled;
+        }
+    }
+}
diff --git a/core/java/com/android/internal/inputmethod/ICharSequenceResultCallback.aidl b/core/java/com/android/internal/inputmethod/ICharSequenceResultCallback.aidl
new file mode 100644
index 0000000..da56fd0
--- /dev/null
+++ b/core/java/com/android/internal/inputmethod/ICharSequenceResultCallback.aidl
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2020 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.internal.inputmethod;
+
+oneway interface ICharSequenceResultCallback {
+    void onResult(in CharSequence result);
+}
diff --git a/core/java/com/android/internal/inputmethod/IExtractedTextResultCallback.aidl b/core/java/com/android/internal/inputmethod/IExtractedTextResultCallback.aidl
new file mode 100644
index 0000000..b603f6a
--- /dev/null
+++ b/core/java/com/android/internal/inputmethod/IExtractedTextResultCallback.aidl
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2020 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.internal.inputmethod;
+
+import android.view.inputmethod.ExtractedText;
+
+oneway interface IExtractedTextResultCallback {
+    void onResult(in ExtractedText result);
+}
diff --git a/core/java/com/android/internal/inputmethod/IIntResultCallback.aidl b/core/java/com/android/internal/inputmethod/IIntResultCallback.aidl
new file mode 100644
index 0000000..bc5ed0d
--- /dev/null
+++ b/core/java/com/android/internal/inputmethod/IIntResultCallback.aidl
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2020 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.internal.inputmethod;
+
+oneway interface IIntResultCallback {
+    void onResult(int result);
+}
diff --git a/core/java/com/android/internal/inputmethod/ResultCallbacks.java b/core/java/com/android/internal/inputmethod/ResultCallbacks.java
new file mode 100644
index 0000000..44a8a83
--- /dev/null
+++ b/core/java/com/android/internal/inputmethod/ResultCallbacks.java
@@ -0,0 +1,132 @@
+/*
+ * Copyright (C) 2020 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.internal.inputmethod;
+
+import android.annotation.AnyThread;
+import android.annotation.BinderThread;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+
+import java.lang.ref.WeakReference;
+import java.util.concurrent.atomic.AtomicReference;
+
+/**
+ * Defines a set of factory methods to create {@link android.os.IBinder}-based callbacks that are
+ * associated with completable objects defined in {@link CancellationGroup.Completable}.
+ */
+public final class ResultCallbacks {
+
+    /**
+     * Not intended to be instantiated.
+     */
+    private ResultCallbacks() {
+    }
+
+    @AnyThread
+    @Nullable
+    private static <T> T unwrap(@NonNull AtomicReference<WeakReference<T>> atomicRef) {
+        final WeakReference<T> ref = atomicRef.getAndSet(null);
+        if (ref == null) {
+            // Double-call is guaranteed to be ignored here.
+            return null;
+        }
+        final T value = ref.get();
+        ref.clear();
+        return value;
+    }
+
+    /**
+     * Creates {@link IIntResultCallback.Stub} that is to set
+     * {@link CancellationGroup.Completable.Int} when receiving the result.
+     *
+     * @param value {@link CancellationGroup.Completable.Int} to be set when receiving the result.
+     * @return {@link IIntResultCallback.Stub} that can be passed as a binder IPC parameter.
+     */
+    @AnyThread
+    public static IIntResultCallback.Stub of(@NonNull CancellationGroup.Completable.Int value) {
+        final AtomicReference<WeakReference<CancellationGroup.Completable.Int>>
+                atomicRef = new AtomicReference<>(new WeakReference<>(value));
+
+        return new IIntResultCallback.Stub() {
+            @BinderThread
+            @Override
+            public void onResult(int result) {
+                final CancellationGroup.Completable.Int value = unwrap(atomicRef);
+                if (value == null) {
+                    return;
+                }
+                value.onComplete(result);
+            }
+        };
+    }
+
+    /**
+     * Creates {@link ICharSequenceResultCallback.Stub} that is to set
+     * {@link CancellationGroup.Completable.CharSequence} when receiving the result.
+     *
+     * @param value {@link CancellationGroup.Completable.CharSequence} to be set when receiving the
+     *              result.
+     * @return {@link ICharSequenceResultCallback.Stub} that can be passed as a binder IPC
+     *         parameter.
+     */
+    @AnyThread
+    public static ICharSequenceResultCallback.Stub of(
+            @NonNull CancellationGroup.Completable.CharSequence value) {
+        final AtomicReference<WeakReference<CancellationGroup.Completable.CharSequence>> atomicRef =
+                new AtomicReference<>(new WeakReference<>(value));
+
+        return new ICharSequenceResultCallback.Stub() {
+            @BinderThread
+            @Override
+            public void onResult(CharSequence result) {
+                final CancellationGroup.Completable.CharSequence value = unwrap(atomicRef);
+                if (value == null) {
+                    return;
+                }
+                value.onComplete(result);
+            }
+        };
+    }
+
+    /**
+     * Creates {@link IExtractedTextResultCallback.Stub} that is to set
+     * {@link CancellationGroup.Completable.ExtractedText} when receiving the result.
+     *
+     * @param value {@link CancellationGroup.Completable.ExtractedText} to be set when receiving the
+     *              result.
+     * @return {@link IExtractedTextResultCallback.Stub} that can be passed as a binder IPC
+     *         parameter.
+     */
+    @AnyThread
+    public static IExtractedTextResultCallback.Stub of(
+            @NonNull CancellationGroup.Completable.ExtractedText value) {
+        final AtomicReference<WeakReference<CancellationGroup.Completable.ExtractedText>>
+                atomicRef = new AtomicReference<>(new WeakReference<>(value));
+
+        return new IExtractedTextResultCallback.Stub() {
+            @BinderThread
+            @Override
+            public void onResult(android.view.inputmethod.ExtractedText result) {
+                final CancellationGroup.Completable.ExtractedText value = unwrap(atomicRef);
+                if (value == null) {
+                    return;
+                }
+                value.onComplete(result);
+            }
+        };
+    }
+}
diff --git a/core/java/com/android/internal/logging/testing/UiEventLoggerFake.java b/core/java/com/android/internal/logging/testing/UiEventLoggerFake.java
index 91ba0df..180ab08 100644
--- a/core/java/com/android/internal/logging/testing/UiEventLoggerFake.java
+++ b/core/java/com/android/internal/logging/testing/UiEventLoggerFake.java
@@ -85,7 +85,7 @@
     }
 
     @Override
-    public void logWithInstanceId(UiEventLogger.UiEventEnum event, int uid, String packageName,
+    public void logWithInstanceId(UiEventEnum event, int uid, String packageName,
             InstanceId instance) {
         final int eventId = event.getId();
         if (eventId > 0) {
diff --git a/core/java/com/android/internal/view/IInputConnectionWrapper.java b/core/java/com/android/internal/view/IInputConnectionWrapper.java
index 6278d4a3..9257c6d 100644
--- a/core/java/com/android/internal/view/IInputConnectionWrapper.java
+++ b/core/java/com/android/internal/view/IInputConnectionWrapper.java
@@ -29,6 +29,7 @@
 import android.view.KeyEvent;
 import android.view.inputmethod.CompletionInfo;
 import android.view.inputmethod.CorrectionInfo;
+import android.view.inputmethod.ExtractedText;
 import android.view.inputmethod.ExtractedTextRequest;
 import android.view.inputmethod.InputConnection;
 import android.view.inputmethod.InputConnectionInspector;
@@ -36,6 +37,9 @@
 import android.view.inputmethod.InputContentInfo;
 
 import com.android.internal.annotations.GuardedBy;
+import com.android.internal.inputmethod.ICharSequenceResultCallback;
+import com.android.internal.inputmethod.IExtractedTextResultCallback;
+import com.android.internal.inputmethod.IIntResultCallback;
 import com.android.internal.os.SomeArgs;
 
 public abstract class IInputConnectionWrapper extends IInputContext.Stub {
@@ -111,28 +115,31 @@
 
     abstract protected boolean isActive();
 
-    public void getTextAfterCursor(int length, int flags, int seq, IInputContextCallback callback) {
-        dispatchMessage(obtainMessageIISC(DO_GET_TEXT_AFTER_CURSOR, length, flags, seq, callback));
-    }
-    
-    public void getTextBeforeCursor(int length, int flags, int seq, IInputContextCallback callback) {
-        dispatchMessage(obtainMessageIISC(DO_GET_TEXT_BEFORE_CURSOR, length, flags, seq, callback));
+    public void getTextAfterCursor(int length, int flags, ICharSequenceResultCallback callback) {
+        dispatchMessage(mH.obtainMessage(DO_GET_TEXT_AFTER_CURSOR, length, flags, callback));
     }
 
-    public void getSelectedText(int flags, int seq, IInputContextCallback callback) {
-        dispatchMessage(obtainMessageISC(DO_GET_SELECTED_TEXT, flags, seq, callback));
+    public void getTextBeforeCursor(int length, int flags, ICharSequenceResultCallback callback) {
+        dispatchMessage(mH.obtainMessage(DO_GET_TEXT_BEFORE_CURSOR, length, flags, callback));
     }
 
-    public void getCursorCapsMode(int reqModes, int seq, IInputContextCallback callback) {
-        dispatchMessage(obtainMessageISC(DO_GET_CURSOR_CAPS_MODE, reqModes, seq, callback));
+    public void getSelectedText(int flags, ICharSequenceResultCallback callback) {
+        dispatchMessage(mH.obtainMessage(DO_GET_SELECTED_TEXT, flags, 0 /* unused */, callback));
     }
 
-    public void getExtractedText(ExtractedTextRequest request,
-            int flags, int seq, IInputContextCallback callback) {
-        dispatchMessage(obtainMessageIOSC(DO_GET_EXTRACTED_TEXT, flags,
-                request, seq, callback));
+    public void getCursorCapsMode(int reqModes, IIntResultCallback callback) {
+        dispatchMessage(
+                mH.obtainMessage(DO_GET_CURSOR_CAPS_MODE, reqModes, 0 /* unused */, callback));
     }
-    
+
+    public void getExtractedText(ExtractedTextRequest request, int flags,
+            IExtractedTextResultCallback callback) {
+        final SomeArgs args = SomeArgs.obtain();
+        args.arg1 = request;
+        args.arg2 = callback;
+        dispatchMessage(mH.obtainMessage(DO_GET_EXTRACTED_TEXT, flags, 0 /* unused */, args));
+    }
+
     public void commitText(CharSequence text, int newCursorPosition) {
         dispatchMessage(obtainMessageIO(DO_COMMIT_TEXT, newCursorPosition, text));
     }
@@ -199,10 +206,9 @@
         dispatchMessage(obtainMessageOO(DO_PERFORM_PRIVATE_COMMAND, action, data));
     }
 
-    public void requestUpdateCursorAnchorInfo(int cursorUpdateMode, int seq,
-            IInputContextCallback callback) {
-        dispatchMessage(obtainMessageISC(DO_REQUEST_UPDATE_CURSOR_ANCHOR_INFO, cursorUpdateMode,
-                seq, callback));
+    public void requestUpdateCursorAnchorInfo(int cursorUpdateMode, IIntResultCallback callback) {
+        dispatchMessage(mH.obtainMessage(DO_REQUEST_UPDATE_CURSOR_ANCHOR_INFO, cursorUpdateMode,
+                0 /* unused */, callback));
     }
 
     public void closeConnection() {
@@ -210,9 +216,12 @@
     }
 
     public void commitContent(InputContentInfo inputContentInfo, int flags, Bundle opts,
-            int seq, IInputContextCallback callback) {
-        dispatchMessage(obtainMessageIOOSC(DO_COMMIT_CONTENT, flags, inputContentInfo, opts, seq,
-                callback));
+            IIntResultCallback callback) {
+        final SomeArgs args = SomeArgs.obtain();
+        args.arg1 = inputContentInfo;
+        args.arg2 = opts;
+        args.arg3 = callback;
+        dispatchMessage(mH.obtainMessage(DO_COMMIT_CONTENT, flags, 0 /* unused */, args));
     }
 
     void dispatchMessage(Message msg) {
@@ -231,100 +240,97 @@
     void executeMessage(Message msg) {
         switch (msg.what) {
             case DO_GET_TEXT_AFTER_CURSOR: {
-                SomeArgs args = (SomeArgs)msg.obj;
+                final ICharSequenceResultCallback callback = (ICharSequenceResultCallback) msg.obj;
+                final InputConnection ic = getInputConnection();
+                final CharSequence result;
+                if (ic == null || !isActive()) {
+                    Log.w(TAG, "getTextAfterCursor on inactive InputConnection");
+                    result = null;
+                } else {
+                    result = ic.getTextAfterCursor(msg.arg1, msg.arg2);
+                }
                 try {
-                    final IInputContextCallback callback = (IInputContextCallback) args.arg6;
-                    final int callbackSeq = args.argi6;
-                    InputConnection ic = getInputConnection();
-                    if (ic == null || !isActive()) {
-                        Log.w(TAG, "getTextAfterCursor on inactive InputConnection");
-                        callback.setTextAfterCursor(null, callbackSeq);
-                        return;
-                    }
-                    callback.setTextAfterCursor(ic.getTextAfterCursor(
-                            msg.arg1, msg.arg2), callbackSeq);
+                    callback.onResult(result);
                 } catch (RemoteException e) {
-                    Log.w(TAG, "Got RemoteException calling setTextAfterCursor", e);
-                } finally {
-                    args.recycle();
+                    Log.w(TAG, "Failed to return the result to getTextAfterCursor()."
+                            + " result=" + result, e);
                 }
                 return;
             }
             case DO_GET_TEXT_BEFORE_CURSOR: {
-                SomeArgs args = (SomeArgs)msg.obj;
+                final ICharSequenceResultCallback callback = (ICharSequenceResultCallback) msg.obj;
+                final InputConnection ic = getInputConnection();
+                final CharSequence result;
+                if (ic == null || !isActive()) {
+                    Log.w(TAG, "getTextBeforeCursor on inactive InputConnection");
+                    result = null;
+                } else {
+                    result = ic.getTextBeforeCursor(msg.arg1, msg.arg2);
+                }
                 try {
-                    final IInputContextCallback callback = (IInputContextCallback) args.arg6;
-                    final int callbackSeq = args.argi6;
-                    InputConnection ic = getInputConnection();
-                    if (ic == null || !isActive()) {
-                        Log.w(TAG, "getTextBeforeCursor on inactive InputConnection");
-                        callback.setTextBeforeCursor(null, callbackSeq);
-                        return;
-                    }
-                    callback.setTextBeforeCursor(ic.getTextBeforeCursor(
-                            msg.arg1, msg.arg2), callbackSeq);
+                    callback.onResult(result);
                 } catch (RemoteException e) {
-                    Log.w(TAG, "Got RemoteException calling setTextBeforeCursor", e);
-                } finally {
-                    args.recycle();
+                    Log.w(TAG, "Failed to return the result to getTextBeforeCursor()."
+                            + " result=" + result, e);
                 }
                 return;
             }
             case DO_GET_SELECTED_TEXT: {
-                SomeArgs args = (SomeArgs)msg.obj;
+                final ICharSequenceResultCallback callback = (ICharSequenceResultCallback) msg.obj;
+                final InputConnection ic = getInputConnection();
+                final CharSequence result;
+                if (ic == null || !isActive()) {
+                    Log.w(TAG, "getSelectedText on inactive InputConnection");
+                    result = null;
+                } else {
+                    result = ic.getSelectedText(msg.arg1);
+                }
                 try {
-                    final IInputContextCallback callback = (IInputContextCallback) args.arg6;
-                    final int callbackSeq = args.argi6;
-                    InputConnection ic = getInputConnection();
-                    if (ic == null || !isActive()) {
-                        Log.w(TAG, "getSelectedText on inactive InputConnection");
-                        callback.setSelectedText(null, callbackSeq);
-                        return;
-                    }
-                    callback.setSelectedText(ic.getSelectedText(
-                            msg.arg1), callbackSeq);
+                    callback.onResult(result);
                 } catch (RemoteException e) {
-                    Log.w(TAG, "Got RemoteException calling setSelectedText", e);
-                } finally {
-                    args.recycle();
+                    Log.w(TAG, "Failed to return the result to getSelectedText()."
+                            + " result=" + result, e);
                 }
                 return;
             }
             case DO_GET_CURSOR_CAPS_MODE: {
-                SomeArgs args = (SomeArgs)msg.obj;
+                final IIntResultCallback callback = (IIntResultCallback) msg.obj;
+                final InputConnection ic = getInputConnection();
+                final int result;
+                if (ic == null || !isActive()) {
+                    Log.w(TAG, "getCursorCapsMode on inactive InputConnection");
+                    result = 0;
+                } else {
+                    result = ic.getCursorCapsMode(msg.arg1);
+                }
                 try {
-                    final IInputContextCallback callback = (IInputContextCallback) args.arg6;
-                    final int callbackSeq = args.argi6;
-                    InputConnection ic = getInputConnection();
-                    if (ic == null || !isActive()) {
-                        Log.w(TAG, "getCursorCapsMode on inactive InputConnection");
-                        callback.setCursorCapsMode(0, callbackSeq);
-                        return;
-                    }
-                    callback.setCursorCapsMode(ic.getCursorCapsMode(msg.arg1),
-                            callbackSeq);
+                    callback.onResult(result);
                 } catch (RemoteException e) {
-                    Log.w(TAG, "Got RemoteException calling setCursorCapsMode", e);
-                } finally {
-                    args.recycle();
+                    Log.w(TAG, "Failed to return the result to getCursorCapsMode()."
+                            + " result=" + result, e);
                 }
                 return;
             }
             case DO_GET_EXTRACTED_TEXT: {
-                SomeArgs args = (SomeArgs)msg.obj;
+                final SomeArgs args = (SomeArgs) msg.obj;
                 try {
-                    final IInputContextCallback callback = (IInputContextCallback) args.arg6;
-                    final int callbackSeq = args.argi6;
-                    InputConnection ic = getInputConnection();
+                    final ExtractedTextRequest request = (ExtractedTextRequest) args.arg1;
+                    final IExtractedTextResultCallback callback =
+                            (IExtractedTextResultCallback) args.arg2;
+                    final InputConnection ic = getInputConnection();
+                    final ExtractedText result;
                     if (ic == null || !isActive()) {
                         Log.w(TAG, "getExtractedText on inactive InputConnection");
-                        callback.setExtractedText(null, callbackSeq);
-                        return;
+                        result = null;
+                    } else {
+                        result = ic.getExtractedText(request, msg.arg1);
                     }
-                    callback.setExtractedText(ic.getExtractedText(
-                            (ExtractedTextRequest)args.arg1, msg.arg1), callbackSeq);
-                } catch (RemoteException e) {
-                    Log.w(TAG, "Got RemoteException calling setExtractedText", e);
+                    try {
+                        callback.onResult(result);
+                    } catch (RemoteException e) {
+                        Log.w(TAG, "Failed to return the result to getExtractedText()."
+                                + " result=" + result, e);
+                    }
                 } finally {
                     args.recycle();
                 }
@@ -494,22 +500,20 @@
                 return;
             }
             case DO_REQUEST_UPDATE_CURSOR_ANCHOR_INFO: {
-                SomeArgs args = (SomeArgs)msg.obj;
+                final IIntResultCallback callback = (IIntResultCallback) msg.obj;
+                final InputConnection ic = getInputConnection();
+                final boolean result;
+                if (ic == null || !isActive()) {
+                    Log.w(TAG, "requestCursorAnchorInfo on inactive InputConnection");
+                    result = false;
+                } else {
+                    result = ic.requestCursorUpdates(msg.arg1);
+                }
                 try {
-                    final IInputContextCallback callback = (IInputContextCallback) args.arg6;
-                    final int callbackSeq = args.argi6;
-                    InputConnection ic = getInputConnection();
-                    if (ic == null || !isActive()) {
-                        Log.w(TAG, "requestCursorAnchorInfo on inactive InputConnection");
-                        callback.setRequestUpdateCursorAnchorInfoResult(false, callbackSeq);
-                        return;
-                    }
-                    callback.setRequestUpdateCursorAnchorInfoResult(
-                            ic.requestCursorUpdates(msg.arg1), callbackSeq);
+                    callback.onResult(result ? 1 : 0);
                 } catch (RemoteException e) {
-                    Log.w(TAG, "Got RemoteException calling requestCursorAnchorInfo", e);
-                } finally {
-                    args.recycle();
+                    Log.w(TAG, "Failed to return the result to requestCursorUpdates()."
+                            + " result=" + result, e);
                 }
                 return;
             }
@@ -547,26 +551,28 @@
                 final int flags = msg.arg1;
                 SomeArgs args = (SomeArgs) msg.obj;
                 try {
-                    final IInputContextCallback callback = (IInputContextCallback) args.arg6;
-                    final int callbackSeq = args.argi6;
-                    InputConnection ic = getInputConnection();
+                    final IIntResultCallback callback = (IIntResultCallback) args.arg3;
+                    final InputConnection ic = getInputConnection();
+                    final boolean result;
                     if (ic == null || !isActive()) {
                         Log.w(TAG, "commitContent on inactive InputConnection");
-                        callback.setCommitContentResult(false, callbackSeq);
-                        return;
+                        result = false;
+                    } else {
+                        final InputContentInfo inputContentInfo = (InputContentInfo) args.arg1;
+                        if (inputContentInfo == null || !inputContentInfo.validate()) {
+                            Log.w(TAG, "commitContent with invalid inputContentInfo="
+                                    + inputContentInfo);
+                            result = false;
+                        } else {
+                            result = ic.commitContent(inputContentInfo, flags, (Bundle) args.arg2);
+                        }
                     }
-                    final InputContentInfo inputContentInfo = (InputContentInfo) args.arg1;
-                    if (inputContentInfo == null || !inputContentInfo.validate()) {
-                        Log.w(TAG, "commitContent with invalid inputContentInfo="
-                                + inputContentInfo);
-                        callback.setCommitContentResult(false, callbackSeq);
-                        return;
+                    try {
+                        callback.onResult(result ? 1 : 0);
+                    } catch (RemoteException e) {
+                        Log.w(TAG, "Failed to return the result to commitContent()."
+                                + " result=" + result, e);
                     }
-                    final boolean result =
-                            ic.commitContent(inputContentInfo, flags, (Bundle) args.arg2);
-                    callback.setCommitContentResult(result, callbackSeq);
-                } catch (RemoteException e) {
-                    Log.w(TAG, "Got RemoteException calling commitContent", e);
                 } finally {
                     args.recycle();
                 }
@@ -588,40 +594,6 @@
         return mH.obtainMessage(what, 0, 0, arg1);
     }
 
-    Message obtainMessageISC(int what, int arg1, int callbackSeq, IInputContextCallback callback) {
-        final SomeArgs args = SomeArgs.obtain();
-        args.arg6 = callback;
-        args.argi6 = callbackSeq;
-        return mH.obtainMessage(what, arg1, 0, args);
-    }
-
-    Message obtainMessageIISC(int what, int arg1, int arg2, int callbackSeq,
-            IInputContextCallback callback) {
-        final SomeArgs args = SomeArgs.obtain();
-        args.arg6 = callback;
-        args.argi6 = callbackSeq;
-        return mH.obtainMessage(what, arg1, arg2, args);
-    }
-
-    Message obtainMessageIOOSC(int what, int arg1, Object objArg1, Object objArg2, int callbackSeq,
-            IInputContextCallback callback) {
-        final SomeArgs args = SomeArgs.obtain();
-        args.arg1 = objArg1;
-        args.arg2 = objArg2;
-        args.arg6 = callback;
-        args.argi6 = callbackSeq;
-        return mH.obtainMessage(what, arg1, 0, args);
-    }
-
-    Message obtainMessageIOSC(int what, int arg1, Object arg2, int callbackSeq,
-            IInputContextCallback callback) {
-        final SomeArgs args = SomeArgs.obtain();
-        args.arg1 = arg2;
-        args.arg6 = callback;
-        args.argi6 = callbackSeq;
-        return mH.obtainMessage(what, arg1, 0, args);
-    }
-
     Message obtainMessageIO(int what, int arg1, Object arg2) {
         return mH.obtainMessage(what, arg1, 0, arg2);
     }
diff --git a/core/java/com/android/internal/view/IInputContext.aidl b/core/java/com/android/internal/view/IInputContext.aidl
index c227991..86f1293 100644
--- a/core/java/com/android/internal/view/IInputContext.aidl
+++ b/core/java/com/android/internal/view/IInputContext.aidl
@@ -23,7 +23,9 @@
 import android.view.inputmethod.ExtractedTextRequest;
 import android.view.inputmethod.InputContentInfo;
 
-import com.android.internal.view.IInputContextCallback;
+import com.android.internal.inputmethod.ICharSequenceResultCallback;
+import com.android.internal.inputmethod.IExtractedTextResultCallback;
+import com.android.internal.inputmethod.IIntResultCallback;
 
 /**
  * Interface from an input method to the application, allowing it to perform
@@ -31,14 +33,14 @@
  * {@hide}
  */
  oneway interface IInputContext {
-    void getTextBeforeCursor(int length, int flags, int seq, IInputContextCallback callback); 
+    void getTextBeforeCursor(int length, int flags, ICharSequenceResultCallback callback);
 
-    void getTextAfterCursor(int length, int flags, int seq, IInputContextCallback callback);
-    
-    void getCursorCapsMode(int reqModes, int seq, IInputContextCallback callback);
-    
-    void getExtractedText(in ExtractedTextRequest request, int flags, int seq,
-            IInputContextCallback callback);
+    void getTextAfterCursor(int length, int flags, ICharSequenceResultCallback callback);
+
+    void getCursorCapsMode(int reqModes, IIntResultCallback callback);
+
+    void getExtractedText(in ExtractedTextRequest request, int flags,
+            IExtractedTextResultCallback callback);
 
     void deleteSurroundingText(int beforeLength, int afterLength);
     void deleteSurroundingTextInCodePoints(int beforeLength, int afterLength);
@@ -71,11 +73,10 @@
 
     void setComposingRegion(int start, int end);
 
-    void getSelectedText(int flags, int seq, IInputContextCallback callback);
+    void getSelectedText(int flags, ICharSequenceResultCallback callback);
 
-    void requestUpdateCursorAnchorInfo(int cursorUpdateMode, int seq,
-            IInputContextCallback callback);
+    void requestUpdateCursorAnchorInfo(int cursorUpdateMode, IIntResultCallback callback);
 
-    void commitContent(in InputContentInfo inputContentInfo, int flags, in Bundle opts, int sec,
-            IInputContextCallback callback);
+    void commitContent(in InputContentInfo inputContentInfo, int flags, in Bundle opts,
+            IIntResultCallback callback);
 }
diff --git a/core/java/com/android/internal/view/IInputContextCallback.aidl b/core/java/com/android/internal/view/IInputContextCallback.aidl
deleted file mode 100644
index 0f40a83..0000000
--- a/core/java/com/android/internal/view/IInputContextCallback.aidl
+++ /dev/null
@@ -1,32 +0,0 @@
-/*
- * Copyright (C) 2008 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.internal.view;
-
-import android.view.inputmethod.ExtractedText;
-
-/**
- * {@hide}
- */
-oneway interface IInputContextCallback {
-    void setTextBeforeCursor(CharSequence textBeforeCursor, int seq);
-    void setTextAfterCursor(CharSequence textAfterCursor, int seq);
-    void setCursorCapsMode(int capsMode, int seq);
-    void setExtractedText(in ExtractedText extractedText, int seq);
-    void setSelectedText(CharSequence selectedText, int seq);
-    void setRequestUpdateCursorAnchorInfoResult(boolean result, int seq);
-    void setCommitContentResult(boolean result, int seq);
-}
diff --git a/core/java/com/android/internal/view/InputConnectionWrapper.java b/core/java/com/android/internal/view/InputConnectionWrapper.java
index a41048c..0bf5234 100644
--- a/core/java/com/android/internal/view/InputConnectionWrapper.java
+++ b/core/java/com/android/internal/view/InputConnectionWrapper.java
@@ -17,14 +17,12 @@
 package com.android.internal.view;
 
 import android.annotation.AnyThread;
-import android.annotation.BinderThread;
 import android.annotation.NonNull;
-import android.compat.annotation.UnsupportedAppUsage;
+import android.annotation.Nullable;
 import android.inputmethodservice.AbstractInputMethodService;
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.RemoteException;
-import android.os.SystemClock;
 import android.util.Log;
 import android.view.KeyEvent;
 import android.view.inputmethod.CompletionInfo;
@@ -36,10 +34,15 @@
 import android.view.inputmethod.InputConnectionInspector.MissingMethodFlags;
 import android.view.inputmethod.InputContentInfo;
 
+import com.android.internal.inputmethod.CancellationGroup;
+import com.android.internal.inputmethod.ResultCallbacks;
+
 import java.lang.ref.WeakReference;
-import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.TimeUnit;
 
 public class InputConnectionWrapper implements InputConnection {
+    private static final String TAG = "InputConnectionWrapper";
+
     private static final int MAX_WAIT_TIME_MILLIS = 2000;
     private final IInputContext mIInputContext;
     @NonNull
@@ -49,257 +52,94 @@
     private final int mMissingMethods;
 
     /**
-     * {@code true} if the system already decided to take away IME focus from the target app. This
-     * can be signaled even when the corresponding signal is in the task queue and
-     * {@link InputMethodService#onUnbindInput()} is not yet called back on the UI thread.
+     * Signaled when the system decided to take away IME focus from the target app.
+     *
+     * <p>This is expected to be signaled immediately when the IME process receives
+     * {@link IInputMethod#unbindInput()}.</p>
      */
     @NonNull
-    private final AtomicBoolean mIsUnbindIssued;
-
-    static class InputContextCallback extends IInputContextCallback.Stub {
-        private static final String TAG = "InputConnectionWrapper.ICC";
-        public int mSeq;
-        public boolean mHaveValue;
-        public CharSequence mTextBeforeCursor;
-        public CharSequence mTextAfterCursor;
-        public CharSequence mSelectedText;
-        public ExtractedText mExtractedText;
-        public int mCursorCapsMode;
-        public boolean mRequestUpdateCursorAnchorInfoResult;
-        public boolean mCommitContentResult;
-
-        // A 'pool' of one InputContextCallback.  Each ICW request will attempt to gain
-        // exclusive access to this object.
-        private static InputContextCallback sInstance = new InputContextCallback();
-        private static int sSequenceNumber = 1;
-        
-        /**
-         * Returns an InputContextCallback object that is guaranteed not to be in use by
-         * any other thread.  The returned object's 'have value' flag is cleared and its expected
-         * sequence number is set to a new integer.  We use a sequence number so that replies that
-         * occur after a timeout has expired are not interpreted as replies to a later request.
-         */
-        @UnsupportedAppUsage
-        @AnyThread
-        private static InputContextCallback getInstance() {
-            synchronized (InputContextCallback.class) {
-                // Return sInstance if it's non-null, otherwise construct a new callback
-                InputContextCallback callback;
-                if (sInstance != null) {
-                    callback = sInstance;
-                    sInstance = null;
-                    
-                    // Reset the callback
-                    callback.mHaveValue = false;
-                } else {
-                    callback = new InputContextCallback();
-                }
-                
-                // Set the sequence number
-                callback.mSeq = sSequenceNumber++;
-                return callback;
-            }
-        }
-        
-        /**
-         * Makes the given InputContextCallback available for use in the future.
-         */
-        @UnsupportedAppUsage
-        @AnyThread
-        private void dispose() {
-            synchronized (InputContextCallback.class) {
-                // If sInstance is non-null, just let this object be garbage-collected
-                if (sInstance == null) {
-                    // Allow any objects being held to be gc'ed
-                    mTextAfterCursor = null;
-                    mTextBeforeCursor = null;
-                    mExtractedText = null;
-                    sInstance = this;
-                }
-            }
-        }
-
-        @BinderThread
-        public void setTextBeforeCursor(CharSequence textBeforeCursor, int seq) {
-            synchronized (this) {
-                if (seq == mSeq) {
-                    mTextBeforeCursor = textBeforeCursor;
-                    mHaveValue = true;
-                    notifyAll();
-                } else {
-                    Log.i(TAG, "Got out-of-sequence callback " + seq + " (expected " + mSeq
-                            + ") in setTextBeforeCursor, ignoring.");
-                }
-            }
-        }
-
-        @BinderThread
-        public void setTextAfterCursor(CharSequence textAfterCursor, int seq) {
-            synchronized (this) {
-                if (seq == mSeq) {
-                    mTextAfterCursor = textAfterCursor;
-                    mHaveValue = true;
-                    notifyAll();
-                } else {
-                    Log.i(TAG, "Got out-of-sequence callback " + seq + " (expected " + mSeq
-                            + ") in setTextAfterCursor, ignoring.");
-                }
-            }
-        }
-
-        @BinderThread
-        public void setSelectedText(CharSequence selectedText, int seq) {
-            synchronized (this) {
-                if (seq == mSeq) {
-                    mSelectedText = selectedText;
-                    mHaveValue = true;
-                    notifyAll();
-                } else {
-                    Log.i(TAG, "Got out-of-sequence callback " + seq + " (expected " + mSeq
-                            + ") in setSelectedText, ignoring.");
-                }
-            }
-        }
-
-        @BinderThread
-        public void setCursorCapsMode(int capsMode, int seq) {
-            synchronized (this) {
-                if (seq == mSeq) {
-                    mCursorCapsMode = capsMode; 
-                    mHaveValue = true;  
-                    notifyAll();
-                } else {
-                    Log.i(TAG, "Got out-of-sequence callback " + seq + " (expected " + mSeq
-                            + ") in setCursorCapsMode, ignoring.");
-                }
-            }
-        }
-
-        @BinderThread
-        public void setExtractedText(ExtractedText extractedText, int seq) {
-            synchronized (this) {
-                if (seq == mSeq) {
-                    mExtractedText = extractedText;
-                    mHaveValue = true;
-                    notifyAll();
-                } else {
-                    Log.i(TAG, "Got out-of-sequence callback " + seq + " (expected " + mSeq
-                            + ") in setExtractedText, ignoring.");
-                }
-            }
-        }
-
-        @BinderThread
-        public void setRequestUpdateCursorAnchorInfoResult(boolean result, int seq) {
-            synchronized (this) {
-                if (seq == mSeq) {
-                    mRequestUpdateCursorAnchorInfoResult = result;
-                    mHaveValue = true;
-                    notifyAll();
-                } else {
-                    Log.i(TAG, "Got out-of-sequence callback " + seq + " (expected " + mSeq
-                            + ") in setCursorAnchorInfoRequestResult, ignoring.");
-                }
-            }
-        }
-
-        @BinderThread
-        public void setCommitContentResult(boolean result, int seq) {
-            synchronized (this) {
-                if (seq == mSeq) {
-                    mCommitContentResult = result;
-                    mHaveValue = true;
-                    notifyAll();
-                } else {
-                    Log.i(TAG, "Got out-of-sequence callback " + seq + " (expected " + mSeq
-                            + ") in setCommitContentResult, ignoring.");
-                }
-            }
-        }
-
-        /**
-         * Waits for a result for up to {@link #MAX_WAIT_TIME_MILLIS} milliseconds.
-         * 
-         * <p>The caller must be synchronized on this callback object.
-         */
-        @AnyThread
-        void waitForResultLocked() {
-            long startTime = SystemClock.uptimeMillis();
-            long endTime = startTime + MAX_WAIT_TIME_MILLIS;
-
-            while (!mHaveValue) {
-                long remainingTime = endTime - SystemClock.uptimeMillis();
-                if (remainingTime <= 0) {
-                    Log.w(TAG, "Timed out waiting on IInputContextCallback");
-                    return;
-                }
-                try {
-                    wait(remainingTime);
-                } catch (InterruptedException e) {
-                }
-            }
-        }
-    }
+    private final CancellationGroup mCancellationGroup;
 
     public InputConnectionWrapper(
             @NonNull WeakReference<AbstractInputMethodService> inputMethodService,
-            IInputContext inputContext, @MissingMethodFlags final int missingMethods,
-            @NonNull AtomicBoolean isUnbindIssued) {
+            IInputContext inputContext, @MissingMethodFlags int missingMethods,
+            @NonNull CancellationGroup cancellationGroup) {
         mInputMethodService = inputMethodService;
         mIInputContext = inputContext;
         mMissingMethods = missingMethods;
-        mIsUnbindIssued = isUnbindIssued;
+        mCancellationGroup = cancellationGroup;
+    }
+
+    @AnyThread
+    private static void logInternal(@Nullable String methodName, boolean timedOut,
+            @Nullable Object defaultValue) {
+        if (timedOut) {
+            Log.w(TAG, methodName + " didn't respond in " + MAX_WAIT_TIME_MILLIS + " msec."
+                    + " Returning default: " + defaultValue);
+        } else {
+            Log.w(TAG, methodName + " was canceled before complete. Returning default: "
+                    + defaultValue);
+        }
+    }
+
+    @AnyThread
+    private static int getResultOrZero(@NonNull CancellationGroup.Completable.Int value,
+             @NonNull String methodName) {
+        final boolean timedOut = value.await(MAX_WAIT_TIME_MILLIS,  TimeUnit.MILLISECONDS);
+        if (value.hasValue()) {
+            return value.getValue();
+        }
+        logInternal(methodName, timedOut, 0);
+        return 0;
+    }
+
+    @AnyThread
+    @Nullable
+    private static <T> T getResultOrNull(@NonNull CancellationGroup.Completable.Values<T> value,
+            @NonNull String methodName) {
+        final boolean timedOut = value.await(MAX_WAIT_TIME_MILLIS,  TimeUnit.MILLISECONDS);
+        if (value.hasValue()) {
+            return value.getValue();
+        }
+        logInternal(methodName, timedOut, null);
+        return null;
     }
 
     @AnyThread
     public CharSequence getTextAfterCursor(int length, int flags) {
-        if (mIsUnbindIssued.get()) {
+        if (mCancellationGroup.isCanceled()) {
             return null;
         }
 
-        CharSequence value = null;
+        final CancellationGroup.Completable.CharSequence value =
+                mCancellationGroup.createCompletableCharSequence();
         try {
-            InputContextCallback callback = InputContextCallback.getInstance();
-            mIInputContext.getTextAfterCursor(length, flags, callback.mSeq, callback);
-            synchronized (callback) {
-                callback.waitForResultLocked();
-                if (callback.mHaveValue) {
-                    value = callback.mTextAfterCursor;
-                }
-            }
-            callback.dispose();
+            mIInputContext.getTextAfterCursor(length, flags, ResultCallbacks.of(value));
         } catch (RemoteException e) {
             return null;
         }
-        return value;
+        return getResultOrNull(value, "getTextAfterCursor()");
     }
 
     @AnyThread
     public CharSequence getTextBeforeCursor(int length, int flags) {
-        if (mIsUnbindIssued.get()) {
+        if (mCancellationGroup.isCanceled()) {
             return null;
         }
 
-        CharSequence value = null;
+        final CancellationGroup.Completable.CharSequence value =
+                mCancellationGroup.createCompletableCharSequence();
         try {
-            InputContextCallback callback = InputContextCallback.getInstance();
-            mIInputContext.getTextBeforeCursor(length, flags, callback.mSeq, callback);
-            synchronized (callback) {
-                callback.waitForResultLocked();
-                if (callback.mHaveValue) {
-                    value = callback.mTextBeforeCursor;
-                }
-            }
-            callback.dispose();
+            mIInputContext.getTextBeforeCursor(length, flags, ResultCallbacks.of(value));
         } catch (RemoteException e) {
             return null;
         }
-        return value;
+        return getResultOrNull(value, "getTextBeforeCursor()");
     }
 
     @AnyThread
     public CharSequence getSelectedText(int flags) {
-        if (mIsUnbindIssued.get()) {
+        if (mCancellationGroup.isCanceled()) {
             return null;
         }
 
@@ -307,67 +147,46 @@
             // This method is not implemented.
             return null;
         }
-        CharSequence value = null;
+        final CancellationGroup.Completable.CharSequence value =
+                mCancellationGroup.createCompletableCharSequence();
         try {
-            InputContextCallback callback = InputContextCallback.getInstance();
-            mIInputContext.getSelectedText(flags, callback.mSeq, callback);
-            synchronized (callback) {
-                callback.waitForResultLocked();
-                if (callback.mHaveValue) {
-                    value = callback.mSelectedText;
-                }
-            }
-            callback.dispose();
+            mIInputContext.getSelectedText(flags, ResultCallbacks.of(value));
         } catch (RemoteException e) {
             return null;
         }
-        return value;
+        return getResultOrNull(value, "getSelectedText()");
     }
 
     @AnyThread
     public int getCursorCapsMode(int reqModes) {
-        if (mIsUnbindIssued.get()) {
+        if (mCancellationGroup.isCanceled()) {
             return 0;
         }
 
-        int value = 0;
+        final CancellationGroup.Completable.Int value =
+                mCancellationGroup.createCompletableInt();
         try {
-            InputContextCallback callback = InputContextCallback.getInstance();
-            mIInputContext.getCursorCapsMode(reqModes, callback.mSeq, callback);
-            synchronized (callback) {
-                callback.waitForResultLocked();
-                if (callback.mHaveValue) {
-                    value = callback.mCursorCapsMode;
-                }
-            }
-            callback.dispose();
+            mIInputContext.getCursorCapsMode(reqModes, ResultCallbacks.of(value));
         } catch (RemoteException e) {
             return 0;
         }
-        return value;
+        return getResultOrZero(value, "getCursorCapsMode()");
     }
 
     @AnyThread
     public ExtractedText getExtractedText(ExtractedTextRequest request, int flags) {
-        if (mIsUnbindIssued.get()) {
+        if (mCancellationGroup.isCanceled()) {
             return null;
         }
 
-        ExtractedText value = null;
+        final CancellationGroup.Completable.ExtractedText value =
+                mCancellationGroup.createCompletableExtractedText();
         try {
-            InputContextCallback callback = InputContextCallback.getInstance();
-            mIInputContext.getExtractedText(request, flags, callback.mSeq, callback);
-            synchronized (callback) {
-                callback.waitForResultLocked();
-                if (callback.mHaveValue) {
-                    value = callback.mExtractedText;
-                }
-            }
-            callback.dispose();
+            mIInputContext.getExtractedText(request, flags, ResultCallbacks.of(value));
         } catch (RemoteException e) {
             return null;
         }
-        return value;
+        return getResultOrNull(value, "getExtractedText()");
     }
 
     @AnyThread
@@ -563,29 +382,22 @@
 
     @AnyThread
     public boolean requestCursorUpdates(int cursorUpdateMode) {
-        if (mIsUnbindIssued.get()) {
+        if (mCancellationGroup.isCanceled()) {
             return false;
         }
 
-        boolean result = false;
         if (isMethodMissing(MissingMethodFlags.REQUEST_CURSOR_UPDATES)) {
             // This method is not implemented.
             return false;
         }
+        final CancellationGroup.Completable.Int value = mCancellationGroup.createCompletableInt();
         try {
-            InputContextCallback callback = InputContextCallback.getInstance();
-            mIInputContext.requestUpdateCursorAnchorInfo(cursorUpdateMode, callback.mSeq, callback);
-            synchronized (callback) {
-                callback.waitForResultLocked();
-                if (callback.mHaveValue) {
-                    result = callback.mRequestUpdateCursorAnchorInfoResult;
-                }
-            }
-            callback.dispose();
+            mIInputContext.requestUpdateCursorAnchorInfo(cursorUpdateMode,
+                    ResultCallbacks.of(value));
         } catch (RemoteException e) {
             return false;
         }
-        return result;
+        return getResultOrZero(value, "requestUpdateCursorAnchorInfo()") != 0;
     }
 
     @AnyThread
@@ -601,38 +413,31 @@
 
     @AnyThread
     public boolean commitContent(InputContentInfo inputContentInfo, int flags, Bundle opts) {
-        if (mIsUnbindIssued.get()) {
+        if (mCancellationGroup.isCanceled()) {
             return false;
         }
 
-        boolean result = false;
         if (isMethodMissing(MissingMethodFlags.COMMIT_CONTENT)) {
             // This method is not implemented.
             return false;
         }
-        try {
-            if ((flags & InputConnection.INPUT_CONTENT_GRANT_READ_URI_PERMISSION) != 0) {
-                final AbstractInputMethodService inputMethodService = mInputMethodService.get();
-                if (inputMethodService == null) {
-                    // This basically should not happen, because it's the the caller of this method.
-                    return false;
-                }
-                inputMethodService.exposeContent(inputContentInfo, this);
-            }
 
-            InputContextCallback callback = InputContextCallback.getInstance();
-            mIInputContext.commitContent(inputContentInfo, flags, opts, callback.mSeq, callback);
-            synchronized (callback) {
-                callback.waitForResultLocked();
-                if (callback.mHaveValue) {
-                    result = callback.mCommitContentResult;
-                }
+        if ((flags & InputConnection.INPUT_CONTENT_GRANT_READ_URI_PERMISSION) != 0) {
+            final AbstractInputMethodService inputMethodService = mInputMethodService.get();
+            if (inputMethodService == null) {
+                // This basically should not happen, because it's the caller of this method.
+                return false;
             }
-            callback.dispose();
+            inputMethodService.exposeContent(inputContentInfo, this);
+        }
+
+        final CancellationGroup.Completable.Int value = mCancellationGroup.createCompletableInt();
+        try {
+            mIInputContext.commitContent(inputContentInfo, flags, opts, ResultCallbacks.of(value));
         } catch (RemoteException e) {
             return false;
         }
-        return result;
+        return getResultOrZero(value, "commitContent()") != 0;
     }
 
     @AnyThread
diff --git a/core/tests/coretests/src/com/android/internal/app/ChooserActivityLoggerFake.java b/core/tests/coretests/src/com/android/internal/app/ChooserActivityLoggerFake.java
new file mode 100644
index 0000000..374edb8
--- /dev/null
+++ b/core/tests/coretests/src/com/android/internal/app/ChooserActivityLoggerFake.java
@@ -0,0 +1,123 @@
+/*
+ * Copyright (C) 2020 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.internal.app;
+
+import com.android.internal.logging.InstanceId;
+import com.android.internal.logging.UiEventLogger;
+import com.android.internal.util.FrameworkStatsLog;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class ChooserActivityLoggerFake implements ChooserActivityLogger {
+    static class CallRecord {
+        // shared fields between all logs
+        public int atomId;
+        public String packageName;
+        public InstanceId instanceId;
+
+        // generic log field
+        public UiEventLogger.UiEventEnum event;
+
+        // share started fields
+        public String mimeType;
+        public int appProvidedDirect;
+        public int appProvidedApp;
+        public boolean isWorkprofile;
+        public int previewType;
+        public String intent;
+
+        // share completed fields
+        public int targetType;
+        public int positionPicked;
+
+        CallRecord(int atomId, UiEventLogger.UiEventEnum eventId,
+                String packageName, InstanceId instanceId) {
+            this.atomId = atomId;
+            this.packageName = packageName;
+            this.instanceId = instanceId;
+            this.event = eventId;
+        }
+
+        CallRecord(int atomId, String packageName, InstanceId instanceId, String mimeType,
+                int appProvidedDirect, int appProvidedApp, boolean isWorkprofile, int previewType,
+                String intent) {
+            this.atomId = atomId;
+            this.packageName = packageName;
+            this.instanceId = instanceId;
+            this.mimeType = mimeType;
+            this.appProvidedDirect = appProvidedDirect;
+            this.appProvidedApp = appProvidedApp;
+            this.isWorkprofile = isWorkprofile;
+            this.previewType = previewType;
+            this.intent = intent;
+        }
+
+        CallRecord(int atomId, String packageName, InstanceId instanceId, int targetType,
+                int positionPicked) {
+            this.atomId = atomId;
+            this.packageName = packageName;
+            this.instanceId = instanceId;
+            this.targetType = targetType;
+            this.positionPicked = positionPicked;
+        }
+
+    }
+    private List<CallRecord> mCalls = new ArrayList<>();
+
+    public int numCalls() {
+        return mCalls.size();
+    }
+
+    List<CallRecord> getCalls() {
+        return mCalls;
+    }
+
+    CallRecord get(int index) {
+        return mCalls.get(index);
+    }
+
+    UiEventLogger.UiEventEnum event(int index) {
+        return mCalls.get(index).event;
+    }
+
+    @Override
+    public void logShareStarted(int eventId, String packageName, String mimeType,
+            int appProvidedDirect, int appProvidedApp, boolean isWorkprofile, int previewType,
+            String intent) {
+        mCalls.add(new CallRecord(FrameworkStatsLog.SHARESHEET_STARTED, packageName,
+                getInstanceId(), mimeType, appProvidedDirect, appProvidedApp, isWorkprofile,
+                previewType, intent));
+    }
+
+    @Override
+    public void logShareTargetSelected(int targetType, String packageName, int positionPicked) {
+        mCalls.add(new CallRecord(FrameworkStatsLog.RANKING_SELECTED, packageName, getInstanceId(),
+                SharesheetTargetSelectedEvent.fromTargetType(targetType).getId(), positionPicked));
+    }
+
+    @Override
+    public void log(UiEventLogger.UiEventEnum event, InstanceId instanceId) {
+        mCalls.add(new CallRecord(FrameworkStatsLog.UI_EVENT_REPORTED,
+                    event, "", instanceId));
+    }
+
+    @Override
+    public InstanceId getInstanceId() {
+        return InstanceId.fakeInstanceId(-1);
+    }
+}
diff --git a/core/tests/coretests/src/com/android/internal/app/ChooserActivityTest.java b/core/tests/coretests/src/com/android/internal/app/ChooserActivityTest.java
index 812e2a6..be129c7 100644
--- a/core/tests/coretests/src/com/android/internal/app/ChooserActivityTest.java
+++ b/core/tests/coretests/src/com/android/internal/app/ChooserActivityTest.java
@@ -77,6 +77,7 @@
 import com.android.internal.app.chooser.DisplayResolveInfo;
 import com.android.internal.logging.MetricsLogger;
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
+import com.android.internal.util.FrameworkStatsLog;
 
 import org.junit.Before;
 import org.junit.Ignore;
@@ -1430,6 +1431,251 @@
                 .check(matches(isDisplayed()));
     }
 
+    @Test
+    public void testAppTargetLogging() throws InterruptedException {
+        Intent sendIntent = createSendTextIntent();
+        List<ResolvedComponentInfo> resolvedComponentInfos = createResolvedComponentsForTest(2);
+
+        when(sOverrides.resolverListController.getResolversForIntent(Mockito.anyBoolean(),
+                Mockito.anyBoolean(),
+                Mockito.isA(List.class))).thenReturn(resolvedComponentInfos);
+
+        final ChooserWrapperActivity activity = mActivityRule
+                .launchActivity(Intent.createChooser(sendIntent, null));
+        waitForIdle();
+
+        assertThat(activity.getAdapter().getCount(), is(2));
+        onView(withId(R.id.profile_button)).check(doesNotExist());
+
+        ResolveInfo[] chosen = new ResolveInfo[1];
+        sOverrides.onSafelyStartCallback = targetInfo -> {
+            chosen[0] = targetInfo.getResolveInfo();
+            return true;
+        };
+
+        ResolveInfo toChoose = resolvedComponentInfos.get(0).getResolveInfoAt(0);
+        onView(withText(toChoose.activityInfo.name))
+                .perform(click());
+        waitForIdle();
+
+        ChooserActivityLoggerFake logger =
+                (ChooserActivityLoggerFake) activity.getChooserActivityLogger();
+        assertThat(logger.numCalls(), is(6));
+        // first one should be SHARESHEET_TRIGGERED uievent
+        assertThat(logger.get(0).atomId, is(FrameworkStatsLog.UI_EVENT_REPORTED));
+        assertThat(logger.get(0).event.getId(),
+                is(ChooserActivityLogger.SharesheetStandardEvent.SHARESHEET_TRIGGERED.getId()));
+        // second one should be SHARESHEET_STARTED event
+        assertThat(logger.get(1).atomId, is(FrameworkStatsLog.SHARESHEET_STARTED));
+        assertThat(logger.get(1).intent, is(Intent.ACTION_SEND));
+        assertThat(logger.get(1).mimeType, is("text/plain"));
+        assertThat(logger.get(1).packageName, is("com.android.frameworks.coretests"));
+        assertThat(logger.get(1).appProvidedApp, is(0));
+        assertThat(logger.get(1).appProvidedDirect, is(0));
+        assertThat(logger.get(1).isWorkprofile, is(false));
+        assertThat(logger.get(1).previewType, is(3));
+        // third one should be SHARESHEET_APP_LOAD_COMPLETE uievent
+        assertThat(logger.get(2).atomId, is(FrameworkStatsLog.UI_EVENT_REPORTED));
+        assertThat(logger.get(2).event.getId(),
+                is(ChooserActivityLogger
+                        .SharesheetStandardEvent.SHARESHEET_APP_LOAD_COMPLETE.getId()));
+        // fourth and fifth are just artifacts of test set-up
+        // sixth one should be ranking atom with SHARESHEET_APP_TARGET_SELECTED event
+        assertThat(logger.get(5).atomId, is(FrameworkStatsLog.RANKING_SELECTED));
+        assertThat(logger.get(5).targetType,
+                is(ChooserActivityLogger
+                        .SharesheetTargetSelectedEvent.SHARESHEET_APP_TARGET_SELECTED.getId()));
+    }
+
+    @Test
+    public void testDirectTargetLogging() throws InterruptedException {
+        Intent sendIntent = createSendTextIntent();
+        // We need app targets for direct targets to get displayed
+        List<ResolvedComponentInfo> resolvedComponentInfos = createResolvedComponentsForTest(2);
+        when(sOverrides.resolverListController.getResolversForIntent(Mockito.anyBoolean(),
+                Mockito.anyBoolean(),
+                Mockito.isA(List.class))).thenReturn(resolvedComponentInfos);
+
+        // Create direct share target
+        List<ChooserTarget> serviceTargets = createDirectShareTargets(1,
+                resolvedComponentInfos.get(0).getResolveInfoAt(0).activityInfo.packageName);
+        ResolveInfo ri = ResolverDataProvider.createResolveInfo(3, 0);
+
+        // Start activity
+        final ChooserWrapperActivity activity = mActivityRule
+                .launchActivity(Intent.createChooser(sendIntent, null));
+
+        // Insert the direct share target
+        Map<ChooserTarget, ShortcutInfo> directShareToShortcutInfos = new HashMap<>();
+        directShareToShortcutInfos.put(serviceTargets.get(0), null);
+        InstrumentationRegistry.getInstrumentation().runOnMainSync(
+                () -> activity.getAdapter().addServiceResults(
+                        activity.createTestDisplayResolveInfo(sendIntent,
+                                ri,
+                                "testLabel",
+                                "testInfo",
+                                sendIntent,
+                                /* resolveInfoPresentationGetter */ null),
+                        serviceTargets,
+                        TARGET_TYPE_CHOOSER_TARGET,
+                        directShareToShortcutInfos,
+                        null)
+        );
+        // Thread.sleep shouldn't be a thing in an integration test but it's
+        // necessary here because of the way the code is structured
+        // TODO: restructure the tests b/129870719
+        Thread.sleep(ChooserActivity.LIST_VIEW_UPDATE_INTERVAL_IN_MILLIS);
+
+        assertThat("Chooser should have 3 targets (2 apps, 1 direct)",
+                activity.getAdapter().getCount(), is(3));
+        assertThat("Chooser should have exactly one selectable direct target",
+                activity.getAdapter().getSelectableServiceTargetCount(), is(1));
+        assertThat("The resolver info must match the resolver info used to create the target",
+                activity.getAdapter().getItem(0).getResolveInfo(), is(ri));
+
+        // Click on the direct target
+        String name = serviceTargets.get(0).getTitle().toString();
+        onView(withText(name))
+                .perform(click());
+        waitForIdle();
+
+        ChooserActivityLoggerFake logger =
+                (ChooserActivityLoggerFake) activity.getChooserActivityLogger();
+        assertThat(logger.numCalls(), is(6));
+        // first one should be SHARESHEET_TRIGGERED uievent
+        assertThat(logger.get(0).atomId, is(FrameworkStatsLog.UI_EVENT_REPORTED));
+        assertThat(logger.get(0).event.getId(),
+                is(ChooserActivityLogger.SharesheetStandardEvent.SHARESHEET_TRIGGERED.getId()));
+        // second one should be SHARESHEET_STARTED event
+        assertThat(logger.get(1).atomId, is(FrameworkStatsLog.SHARESHEET_STARTED));
+        assertThat(logger.get(1).intent, is(Intent.ACTION_SEND));
+        assertThat(logger.get(1).mimeType, is("text/plain"));
+        assertThat(logger.get(1).packageName, is("com.android.frameworks.coretests"));
+        assertThat(logger.get(1).appProvidedApp, is(0));
+        assertThat(logger.get(1).appProvidedDirect, is(0));
+        assertThat(logger.get(1).isWorkprofile, is(false));
+        assertThat(logger.get(1).previewType, is(3));
+        // third one should be SHARESHEET_APP_LOAD_COMPLETE uievent
+        assertThat(logger.get(2).atomId, is(FrameworkStatsLog.UI_EVENT_REPORTED));
+        assertThat(logger.get(2).event.getId(),
+                is(ChooserActivityLogger
+                        .SharesheetStandardEvent.SHARESHEET_APP_LOAD_COMPLETE.getId()));
+        // fourth and fifth are just artifacts of test set-up
+        // sixth one should be ranking atom with SHARESHEET_COPY_TARGET_SELECTED event
+        assertThat(logger.get(5).atomId, is(FrameworkStatsLog.RANKING_SELECTED));
+        assertThat(logger.get(5).targetType,
+                is(ChooserActivityLogger
+                        .SharesheetTargetSelectedEvent.SHARESHEET_SERVICE_TARGET_SELECTED.getId()));
+    }
+
+    @Test
+    public void testCopyTextToClipboardLogging() throws Exception {
+        Intent sendIntent = createSendTextIntent();
+        List<ResolvedComponentInfo> resolvedComponentInfos = createResolvedComponentsForTest(2);
+
+        when(ChooserWrapperActivity.sOverrides.resolverListController.getResolversForIntent(
+                Mockito.anyBoolean(),
+                Mockito.anyBoolean(),
+                Mockito.isA(List.class))).thenReturn(resolvedComponentInfos);
+
+        final ChooserWrapperActivity activity = mActivityRule
+                .launchActivity(Intent.createChooser(sendIntent, null));
+        waitForIdle();
+
+        onView(withId(R.id.chooser_copy_button)).check(matches(isDisplayed()));
+        onView(withId(R.id.chooser_copy_button)).perform(click());
+
+        ChooserActivityLoggerFake logger =
+                (ChooserActivityLoggerFake) activity.getChooserActivityLogger();
+        assertThat(logger.numCalls(), is(6));
+        // first one should be SHARESHEET_TRIGGERED uievent
+        assertThat(logger.get(0).atomId, is(FrameworkStatsLog.UI_EVENT_REPORTED));
+        assertThat(logger.get(0).event.getId(),
+                is(ChooserActivityLogger.SharesheetStandardEvent.SHARESHEET_TRIGGERED.getId()));
+        // second one should be SHARESHEET_STARTED event
+        assertThat(logger.get(1).atomId, is(FrameworkStatsLog.SHARESHEET_STARTED));
+        assertThat(logger.get(1).intent, is(Intent.ACTION_SEND));
+        assertThat(logger.get(1).mimeType, is("text/plain"));
+        assertThat(logger.get(1).packageName, is("com.android.frameworks.coretests"));
+        assertThat(logger.get(1).appProvidedApp, is(0));
+        assertThat(logger.get(1).appProvidedDirect, is(0));
+        assertThat(logger.get(1).isWorkprofile, is(false));
+        assertThat(logger.get(1).previewType, is(3));
+        // third one should be SHARESHEET_APP_LOAD_COMPLETE uievent
+        assertThat(logger.get(2).atomId, is(FrameworkStatsLog.UI_EVENT_REPORTED));
+        assertThat(logger.get(2).event.getId(),
+                is(ChooserActivityLogger
+                        .SharesheetStandardEvent.SHARESHEET_APP_LOAD_COMPLETE.getId()));
+        // fourth and fifth are just artifacts of test set-up
+        // sixth one should be ranking atom with SHARESHEET_COPY_TARGET_SELECTED event
+        assertThat(logger.get(5).atomId, is(FrameworkStatsLog.RANKING_SELECTED));
+        assertThat(logger.get(5).targetType,
+                is(ChooserActivityLogger
+                        .SharesheetTargetSelectedEvent.SHARESHEET_COPY_TARGET_SELECTED.getId()));
+    }
+
+    @Test
+    public void testSwitchProfileLogging() throws InterruptedException {
+        // enable the work tab feature flag
+        ResolverActivity.ENABLE_TABBED_VIEW = true;
+        markWorkProfileUserAvailable();
+        int workProfileTargets = 4;
+        List<ResolvedComponentInfo> personalResolvedComponentInfos =
+                createResolvedComponentsForTestWithOtherProfile(3, /* userId */ 10);
+        List<ResolvedComponentInfo> workResolvedComponentInfos =
+                createResolvedComponentsForTest(workProfileTargets);
+        setupResolverControllers(personalResolvedComponentInfos, workResolvedComponentInfos);
+        Intent sendIntent = createSendTextIntent();
+        sendIntent.setType("TestType");
+
+        final ChooserWrapperActivity activity =
+                mActivityRule.launchActivity(Intent.createChooser(sendIntent, "work tab test"));
+        waitForIdle();
+        onView(withText(R.string.resolver_work_tab)).perform(click());
+        waitForIdle();
+        onView(withText(R.string.resolver_personal_tab)).perform(click());
+        waitForIdle();
+
+        ChooserActivityLoggerFake logger =
+                (ChooserActivityLoggerFake) activity.getChooserActivityLogger();
+        assertThat(logger.numCalls(), is(8));
+        // first one should be SHARESHEET_TRIGGERED uievent
+        assertThat(logger.get(0).atomId, is(FrameworkStatsLog.UI_EVENT_REPORTED));
+        assertThat(logger.get(0).event.getId(),
+                is(ChooserActivityLogger.SharesheetStandardEvent.SHARESHEET_TRIGGERED.getId()));
+        // second one should be SHARESHEET_STARTED event
+        assertThat(logger.get(1).atomId, is(FrameworkStatsLog.SHARESHEET_STARTED));
+        assertThat(logger.get(1).intent, is(Intent.ACTION_SEND));
+        assertThat(logger.get(1).mimeType, is("TestType"));
+        assertThat(logger.get(1).packageName, is("com.android.frameworks.coretests"));
+        assertThat(logger.get(1).appProvidedApp, is(0));
+        assertThat(logger.get(1).appProvidedDirect, is(0));
+        assertThat(logger.get(1).isWorkprofile, is(false));
+        assertThat(logger.get(1).previewType, is(3));
+        // third one should be SHARESHEET_APP_LOAD_COMPLETE uievent
+        assertThat(logger.get(2).atomId, is(FrameworkStatsLog.UI_EVENT_REPORTED));
+        assertThat(logger.get(2).event.getId(),
+                is(ChooserActivityLogger
+                        .SharesheetStandardEvent.SHARESHEET_APP_LOAD_COMPLETE.getId()));
+        // fourth one is artifact of test setup
+        // fifth one is switch to work profile
+        assertThat(logger.get(4).atomId, is(FrameworkStatsLog.UI_EVENT_REPORTED));
+        assertThat(logger.get(4).event.getId(),
+                is(ChooserActivityLogger
+                        .SharesheetStandardEvent.SHARESHEET_PROFILE_CHANGED.getId()));
+        // sixth one should be SHARESHEET_APP_LOAD_COMPLETE uievent
+        assertThat(logger.get(5).atomId, is(FrameworkStatsLog.UI_EVENT_REPORTED));
+        assertThat(logger.get(5).event.getId(),
+                is(ChooserActivityLogger
+                        .SharesheetStandardEvent.SHARESHEET_APP_LOAD_COMPLETE.getId()));
+        // seventh one is artifact of test setup
+        // eigth one is switch to work profile
+        assertThat(logger.get(7).atomId, is(FrameworkStatsLog.UI_EVENT_REPORTED));
+        assertThat(logger.get(7).event.getId(),
+                is(ChooserActivityLogger
+                        .SharesheetStandardEvent.SHARESHEET_PROFILE_CHANGED.getId()));
+    }
+
     private Intent createSendTextIntent() {
         Intent sendIntent = new Intent();
         sendIntent.setAction(Intent.ACTION_SEND);
diff --git a/core/tests/coretests/src/com/android/internal/app/ChooserWrapperActivity.java b/core/tests/coretests/src/com/android/internal/app/ChooserWrapperActivity.java
index 363551b..5b83f95 100644
--- a/core/tests/coretests/src/com/android/internal/app/ChooserWrapperActivity.java
+++ b/core/tests/coretests/src/com/android/internal/app/ChooserWrapperActivity.java
@@ -146,6 +146,11 @@
     }
 
     @Override
+    protected ChooserActivityLogger getChooserActivityLogger() {
+        return sOverrides.chooserActivityLogger;
+    }
+
+    @Override
     public Cursor queryResolver(ContentResolver resolver, Uri uri) {
         if (sOverrides.resolverCursor != null) {
             return sOverrides.resolverCursor;
@@ -205,6 +210,7 @@
         public boolean resolverForceException;
         public Bitmap previewThumbnail;
         public MetricsLogger metricsLogger;
+        public ChooserActivityLogger chooserActivityLogger;
         public int alternateProfileSetting;
         public Resources resources;
         public UserHandle workProfileUserHandle;
@@ -223,6 +229,7 @@
             resolverListController = mock(ResolverListController.class);
             workResolverListController = mock(ResolverListController.class);
             metricsLogger = mock(MetricsLogger.class);
+            chooserActivityLogger = new ChooserActivityLoggerFake();
             alternateProfileSetting = 0;
             resources = null;
             workProfileUserHandle = null;
diff --git a/packages/Shell/src/com/android/shell/BugreportProgressService.java b/packages/Shell/src/com/android/shell/BugreportProgressService.java
index 5d363f3..68bd407 100644
--- a/packages/Shell/src/com/android/shell/BugreportProgressService.java
+++ b/packages/Shell/src/com/android/shell/BugreportProgressService.java
@@ -351,9 +351,6 @@
         @Override
         public void onProgress(float progress) {
             synchronized (mLock) {
-                if (progress == 0) {
-                    trackInfoWithIdLocked();
-                }
                 checkProgressUpdatedLocked(mInfo, (int) progress);
             }
         }
@@ -365,7 +362,6 @@
         @Override
         public void onError(@BugreportErrorCode int errorCode) {
             synchronized (mLock) {
-                trackInfoWithIdLocked();
                 stopProgressLocked(mInfo.id);
             }
             Log.e(TAG, "Bugreport API callback onError() errorCode = " + errorCode);
@@ -382,10 +378,10 @@
         }
 
         /**
-         * Reads bugreport id and links it to the bugreport info to track the bugreport's
-         * progress/completion/error. id is incremented in dumpstate code. This function is called
-         * when dumpstate calls one of the callback functions (onProgress, onFinished, onError)
-         * after the id has been incremented.
+         * Reads bugreport id and links it to the bugreport info to track a bugreport that is in
+         * process. id is incremented in the dumpstate code.
+         * We do not track a bugreport if there is already a bugreport with the same id being
+         * tracked.
          */
         @GuardedBy("mLock")
         private void trackInfoWithIdLocked() {
@@ -408,7 +404,6 @@
                 sendRemoteBugreportFinishedBroadcast(mContext, bugreportFilePath,
                         mInfo.bugreportFile);
             } else {
-                trackInfoWithIdLocked();
                 cleanupOldFiles(MIN_KEEP_COUNT, MIN_KEEP_AGE, mBugreportsDir);
                 final Intent intent = new Intent(INTENT_BUGREPORT_FINISHED);
                 intent.putExtra(EXTRA_BUGREPORT, bugreportFilePath);
@@ -638,8 +633,11 @@
 
         BugreportCallbackImpl bugreportCallback = new BugreportCallbackImpl(info);
         try {
-            mBugreportManager.startBugreport(bugreportFd, screenshotFd,
-                    new BugreportParams(bugreportType), executor, bugreportCallback);
+            synchronized (mLock) {
+                mBugreportManager.startBugreport(bugreportFd, screenshotFd,
+                        new BugreportParams(bugreportType), executor, bugreportCallback);
+                bugreportCallback.trackInfoWithIdLocked();
+            }
         } catch (RuntimeException e) {
             Log.i(TAG, "Error in generating bugreports: ", e);
             // The binder call didn't go through successfully, so need to close the fds.
@@ -756,7 +754,7 @@
                 != (info.lastProgress.intValue() / LOG_PROGRESS_STEP))) {
             Log.d(TAG, "Progress #" + info.id + ": " + percentageText);
         }
-        info.lastProgress = new AtomicInteger(progress);
+        info.lastProgress.set(progress);
 
         sendForegroundabledNotification(info.id, builder.build());
     }
@@ -1025,7 +1023,7 @@
         }
         Log.d(TAG, "Bugreport finished with title: " + info.getTitle()
                 + " and shareDescription: " + info.shareDescription);
-        info.finished = new AtomicBoolean(true);
+        info.finished.set(true);
 
         synchronized (mLock) {
             // Stop running on foreground, otherwise share notification cannot be dismissed.
@@ -1809,18 +1807,18 @@
          * Current value of progress (in percentage) of the bugreport generation as
          * displayed by the UI.
          */
-        AtomicInteger progress = new AtomicInteger(0);
+        final AtomicInteger progress = new AtomicInteger(0);
 
         /**
          * Last value of progress (in percentage) of the bugreport generation for which
          * system notification was updated.
          */
-        AtomicInteger lastProgress = new AtomicInteger(0);
+        final AtomicInteger lastProgress = new AtomicInteger(0);
 
         /**
          * Time of the last progress update.
          */
-        AtomicLong lastUpdate = new AtomicLong(System.currentTimeMillis());
+        final AtomicLong lastUpdate = new AtomicLong(System.currentTimeMillis());
 
         /**
          * Time of the last progress update when Parcel was created.
@@ -1840,7 +1838,7 @@
         /**
          * Whether dumpstate sent an intent informing it has finished.
          */
-        AtomicBoolean finished = new AtomicBoolean(false);
+        final AtomicBoolean finished = new AtomicBoolean(false);
 
         /**
          * Whether the details entries have been added to the bugreport yet.
@@ -2075,8 +2073,8 @@
             initialName = in.readString();
             title = in.readString();
             description = in.readString();
-            progress = new AtomicInteger(in.readInt());
-            lastUpdate = new AtomicLong(in.readLong());
+            progress.set(in.readInt());
+            lastUpdate.set(in.readLong());
             formattedLastUpdate = in.readString();
             bugreportFile = readFile(in);
 
@@ -2085,7 +2083,7 @@
                   screenshotFiles.add(readFile(in));
             }
 
-            finished = new AtomicBoolean(in.readInt() == 1);
+            finished.set(in.readInt() == 1);
             screenshotCounter = in.readInt();
             shareDescription = in.readString();
             shareTitle = in.readString();
@@ -2157,8 +2155,8 @@
                         + ") from " + info.progress.intValue() + " to " + progress);
             }
         }
-        info.progress = new AtomicInteger(progress);
-        info.lastUpdate = new AtomicLong(System.currentTimeMillis());
+        info.progress.set(progress);
+        info.lastUpdate.set(System.currentTimeMillis());
 
         updateProgress(info);
     }
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 23be78b..a9e5fa9 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -477,6 +477,7 @@
     <dimen name="qs_tile_height">106dp</dimen>
     <dimen name="qs_tile_layout_margin_side">6dp</dimen>
     <dimen name="qs_tile_margin_horizontal">18dp</dimen>
+    <dimen name="qs_tile_margin_horizontal_two_line">2dp</dimen>
     <dimen name="qs_tile_margin_vertical">24dp</dimen>
     <dimen name="qs_tile_margin_top_bottom">12dp</dimen>
     <dimen name="qs_tile_margin_top_bottom_negative">-12dp</dimen>
diff --git a/packages/SystemUI/src/com/android/systemui/qs/DoubleLineTileLayout.kt b/packages/SystemUI/src/com/android/systemui/qs/DoubleLineTileLayout.kt
index f710f7f..448531a 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/DoubleLineTileLayout.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/DoubleLineTileLayout.kt
@@ -25,13 +25,18 @@
 
 class DoubleLineTileLayout(context: Context) : ViewGroup(context), QSPanel.QSTileLayout {
 
+    companion object {
+        private const val NUM_LINES = 2
+    }
+
     protected val mRecords = ArrayList<QSPanel.TileRecord>()
     private var _listening = false
     private var smallTileSize = 0
     private val twoLineHeight
-        get() = smallTileSize * 2 + cellMarginVertical
+        get() = smallTileSize * NUM_LINES + cellMarginVertical * (NUM_LINES - 1)
     private var cellMarginHorizontal = 0
     private var cellMarginVertical = 0
+    private var tilesToShow = 0
 
     init {
         isFocusableInTouchMode = true
@@ -68,7 +73,7 @@
     override fun updateResources(): Boolean {
         with(mContext.resources) {
             smallTileSize = getDimensionPixelSize(R.dimen.qs_quick_tile_size)
-            cellMarginHorizontal = getDimensionPixelSize(R.dimen.qs_tile_margin_horizontal)
+            cellMarginHorizontal = getDimensionPixelSize(R.dimen.qs_tile_margin_horizontal_two_line)
             cellMarginVertical = getDimensionPixelSize(R.dimen.new_qs_vertical_margin)
         }
         requestLayout()
@@ -83,11 +88,12 @@
         }
     }
 
-    override fun getNumVisibleTiles() = mRecords.size
+    override fun getNumVisibleTiles() = tilesToShow
 
     override fun onConfigurationChanged(newConfig: Configuration) {
         super.onConfigurationChanged(newConfig)
         updateResources()
+        postInvalidate()
     }
 
     override fun onFinishInflate() {
@@ -95,39 +101,58 @@
     }
 
     override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
-        var previousView: View = this
-        var tiles = 0
 
         mRecords.forEach {
-            val tileView = it.tileView
-            if (tileView.visibility != View.GONE) {
-                tileView.updateAccessibilityOrder(previousView)
-                previousView = tileView
-                tiles++
-                tileView.measure(exactly(smallTileSize), exactly(smallTileSize))
-            }
+            it.tileView.measure(exactly(smallTileSize), exactly(smallTileSize))
         }
 
         val height = twoLineHeight
-        val columns = tiles / 2
-        val width = paddingStart + paddingEnd +
-                columns * smallTileSize +
-                (columns - 1) * cellMarginHorizontal
-        setMeasuredDimension(width, height)
+        setMeasuredDimension(MeasureSpec.getSize(widthMeasureSpec), height)
     }
 
-    override fun onLayout(changed: Boolean, l: Int, t: Int, r: Int, b: Int) {
-        val tiles = mRecords.filter { it.tileView.visibility != View.GONE }
-        tiles.forEachIndexed {
-            index, tile ->
-            val column = index % (tiles.size / 2)
-            val left = getLeftForColumn(column)
-            val top = if (index < tiles.size / 2) 0 else getTopBottomRow()
-            tile.tileView.layout(left, top, left + smallTileSize, top + smallTileSize)
+    private fun calculateMaxColumns(availableWidth: Int): Int {
+        if (smallTileSize + cellMarginHorizontal == 0) {
+            return 0
+        } else {
+            return (availableWidth - smallTileSize) / (smallTileSize + cellMarginHorizontal) + 1
         }
     }
 
-    private fun getLeftForColumn(column: Int) = column * (smallTileSize + cellMarginHorizontal)
+    override fun onLayout(changed: Boolean, l: Int, t: Int, r: Int, b: Int) {
+        val availableWidth = r - l - paddingLeft - paddingRight
+        val maxColumns = calculateMaxColumns(availableWidth)
+        val actualColumns = Math.min(maxColumns, mRecords.size / NUM_LINES)
+        if (actualColumns == 0) {
+            // No tileSize or horizontal margin
+            return
+        }
+        tilesToShow = actualColumns * NUM_LINES
+
+        val interTileSpace = if (actualColumns <= 2) {
+            // Extra "column" of padding to be distributed on each end
+            (availableWidth - actualColumns * smallTileSize) / actualColumns
+        } else {
+            (availableWidth - actualColumns * smallTileSize) / (actualColumns - 1)
+        }
+
+        for (index in 0 until mRecords.size) {
+            val tileView = mRecords[index].tileView
+            if (index >= tilesToShow) {
+                tileView.visibility = View.GONE
+            } else {
+                tileView.visibility = View.VISIBLE
+                if (index > 0) tileView.updateAccessibilityOrder(mRecords[index - 1].tileView)
+                val column = index % actualColumns
+                val left = getLeftForColumn(column, interTileSpace, actualColumns <= 2)
+                val top = if (index < actualColumns) 0 else getTopBottomRow()
+                tileView.layout(left, top, left + smallTileSize, top + smallTileSize)
+            }
+        }
+    }
+
+    private fun getLeftForColumn(column: Int, interSpace: Int, sideMargin: Boolean): Int {
+        return (if (sideMargin) interSpace / 2 else 0) + column * (smallTileSize + interSpace)
+    }
 
     private fun getTopBottomRow() = smallTileSize + cellMarginVertical
 }
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeDepthController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeDepthController.kt
index d7322a0..fd44f04 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeDepthController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeDepthController.kt
@@ -32,6 +32,7 @@
 import com.android.systemui.Interpolators
 import com.android.systemui.dump.DumpManager
 import com.android.systemui.plugins.statusbar.StatusBarStateController
+import com.android.systemui.statusbar.notification.ActivityLaunchAnimator
 import com.android.systemui.statusbar.phone.BiometricUnlockController
 import com.android.systemui.statusbar.phone.BiometricUnlockController.MODE_WAKE_AND_UNLOCK
 import com.android.systemui.statusbar.phone.NotificationShadeWindowController
@@ -68,6 +69,7 @@
     private var notificationAnimator: Animator? = null
     private var updateScheduled: Boolean = false
     private var shadeExpansion = 0f
+    private var ignoreShadeBlurUntilHidden: Boolean = false
     @VisibleForTesting
     var shadeSpring = DepthAnimation()
     @VisibleForTesting
@@ -83,6 +85,26 @@
         }
 
     /**
+     * When launching an app from the shade, the animations progress should affect how blurry the
+     * shade is, overriding the expansion amount.
+     */
+    var notificationLaunchAnimationParams: ActivityLaunchAnimator.ExpandAnimationParameters? = null
+        set(value) {
+            field = value
+            if (value != null) {
+                scheduleUpdate()
+                return
+            }
+
+            if (shadeSpring.radius == 0) {
+                return
+            }
+            ignoreShadeBlurUntilHidden = true
+            shadeSpring.animateTo(0)
+            shadeSpring.finishIfRunning()
+        }
+
+    /**
      * Blur radius of the wake-up animation on this frame.
      */
     private var wakeAndUnlockBlurRadius = 0
@@ -99,9 +121,19 @@
     val updateBlurCallback = Choreographer.FrameCallback {
         updateScheduled = false
 
-        var shadeRadius = max(shadeSpring.radius, wakeAndUnlockBlurRadius)
-        shadeRadius = (shadeRadius * (1f - brightnessMirrorSpring.ratio)).toInt()
-        val blur = max(shadeRadius, globalActionsSpring.radius)
+        var shadeRadius = max(shadeSpring.radius, wakeAndUnlockBlurRadius).toFloat()
+        shadeRadius *= 1f - brightnessMirrorSpring.ratio
+        val launchProgress = notificationLaunchAnimationParams?.linearProgress ?: 0f
+        shadeRadius *= (1f - launchProgress) * (1f - launchProgress)
+
+        if (ignoreShadeBlurUntilHidden) {
+            if (shadeRadius == 0f) {
+                ignoreShadeBlurUntilHidden = false
+            } else {
+                shadeRadius = 0f
+            }
+        }
+        val blur = max(shadeRadius.toInt(), globalActionsSpring.radius)
         blurUtils.applyBlur(blurRoot?.viewRootImpl ?: root.viewRootImpl, blur)
         try {
             wallpaperManager.setWallpaperZoomOut(root.windowToken,
@@ -187,7 +219,6 @@
         if (statusBarStateController.state == StatusBarState.SHADE) {
             newBlur = blurUtils.blurRadiusOfRatio(shadeExpansion)
         }
-
         shadeSpring.animateTo(newBlur)
     }
 
@@ -212,6 +243,9 @@
             it.println("globalActionsRadius: ${globalActionsSpring.radius}")
             it.println("brightnessMirrorRadius: ${brightnessMirrorSpring.radius}")
             it.println("wakeAndUnlockBlur: $wakeAndUnlockBlurRadius")
+            it.println("notificationLaunchAnimationProgress: " +
+                    "${notificationLaunchAnimationParams?.linearProgress}")
+            it.println("ignoreShadeBlurUntilHidden: $ignoreShadeBlurUntilHidden")
         }
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/ActivityLaunchAnimator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/ActivityLaunchAnimator.java
index 7c06157..6aef6b4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/ActivityLaunchAnimator.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/ActivityLaunchAnimator.java
@@ -34,6 +34,7 @@
 
 import com.android.internal.policy.ScreenDecorationsUtils;
 import com.android.systemui.Interpolators;
+import com.android.systemui.statusbar.NotificationShadeDepthController;
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
 import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
 import com.android.systemui.statusbar.phone.CollapsedStatusBarFragment;
@@ -57,6 +58,7 @@
     private final NotificationListContainer mNotificationContainer;
     private final float mWindowCornerRadius;
     private final NotificationShadeWindowViewController mNotificationShadeWindowViewController;
+    private final NotificationShadeDepthController mDepthController;
     private Callback mCallback;
     private final Runnable mTimeoutRunnable = () -> {
         setAnimationPending(false);
@@ -70,9 +72,11 @@
             NotificationShadeWindowViewController notificationShadeWindowViewController,
             Callback callback,
             NotificationPanelViewController notificationPanel,
+            NotificationShadeDepthController depthController,
             NotificationListContainer container) {
         mNotificationPanel = notificationPanel;
         mNotificationContainer = container;
+        mDepthController = depthController;
         mNotificationShadeWindowViewController = notificationShadeWindowViewController;
         mCallback = callback;
         mWindowCornerRadius = ScreenDecorationsUtils
@@ -212,7 +216,7 @@
                                 mWindowCornerRadius, progress);
                         applyParamsToWindow(primary);
                         applyParamsToNotification(mParams);
-                        applyParamsToNotificationList(mParams);
+                        applyParamsToNotificationShade(mParams);
                     }
                 });
                 anim.addListener(new AnimatorListenerAdapter() {
@@ -256,14 +260,15 @@
             if (!running) {
                 mCallback.onExpandAnimationFinished(mIsFullScreenLaunch);
                 applyParamsToNotification(null);
-                applyParamsToNotificationList(null);
+                applyParamsToNotificationShade(null);
             }
 
         }
 
-        private void applyParamsToNotificationList(ExpandAnimationParameters params) {
+        private void applyParamsToNotificationShade(ExpandAnimationParameters params) {
             mNotificationContainer.applyExpandAnimationParams(params);
             mNotificationPanel.applyExpandAnimationParams(params);
+            mDepthController.setNotificationLaunchAnimationParams(params);
         }
 
         private void applyParamsToNotification(ExpandAnimationParameters params) {
@@ -295,7 +300,7 @@
     };
 
     public static class ExpandAnimationParameters {
-        float linearProgress;
+        public float linearProgress;
         int[] startPosition;
         float startTranslationZ;
         int left;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
index f0cce46..fa55b74 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -1235,6 +1235,7 @@
         // Set up the initial notification state.
         mActivityLaunchAnimator = new ActivityLaunchAnimator(
                 mNotificationShadeWindowViewController, this, mNotificationPanelViewController,
+                mNotificationShadeDepthControllerLazy.get(),
                 (NotificationListContainer) mStackScroller);
 
         // TODO: inject this.
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationShadeDepthControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationShadeDepthControllerTest.kt
index 1e3636b..6b7a3bf 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationShadeDepthControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationShadeDepthControllerTest.kt
@@ -26,6 +26,7 @@
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.dump.DumpManager
 import com.android.systemui.plugins.statusbar.StatusBarStateController
+import com.android.systemui.statusbar.notification.ActivityLaunchAnimator
 import com.android.systemui.statusbar.phone.BiometricUnlockController
 import com.android.systemui.statusbar.phone.NotificationShadeWindowController
 import com.android.systemui.statusbar.policy.KeyguardStateController
@@ -43,6 +44,7 @@
 import org.mockito.Mockito.anyString
 import org.mockito.Mockito.clearInvocations
 import org.mockito.Mockito.doThrow
+import org.mockito.Mockito.never
 import org.mockito.Mockito.verify
 import org.mockito.junit.MockitoJUnit
 
@@ -128,6 +130,23 @@
     }
 
     @Test
+    fun updateBlurCallback_setsBlur_whenExpanded() {
+        `when`(shadeSpring.radius).thenReturn(maxBlur)
+        notificationShadeDepthController.updateBlurCallback.doFrame(0)
+        verify(blurUtils).applyBlur(any(), eq(maxBlur))
+    }
+
+    @Test
+    fun updateBlurCallback_appLaunchAnimation_overridesZoom() {
+        `when`(shadeSpring.radius).thenReturn(maxBlur)
+        val animProgress = ActivityLaunchAnimator.ExpandAnimationParameters()
+        animProgress.linearProgress = 1f
+        notificationShadeDepthController.notificationLaunchAnimationParams = animProgress
+        notificationShadeDepthController.updateBlurCallback.doFrame(0)
+        verify(blurUtils).applyBlur(any(), eq(0))
+    }
+
+    @Test
     fun updateBlurCallback_invalidWindow() {
         doThrow(IllegalArgumentException("test exception")).`when`(wallpaperManager)
                 .setWallpaperZoomOut(any(), anyFloat())
@@ -159,6 +178,24 @@
         verify(blurUtils).applyBlur(safeEq(viewRootImpl), eq(0))
     }
 
+    @Test
+    fun setNotificationLaunchAnimationParams_schedulesFrame() {
+        val animProgress = ActivityLaunchAnimator.ExpandAnimationParameters()
+        animProgress.linearProgress = 0.5f
+        notificationShadeDepthController.notificationLaunchAnimationParams = animProgress
+        verify(choreographer).postFrameCallback(
+                eq(notificationShadeDepthController.updateBlurCallback))
+    }
+
+    @Test
+    fun setNotificationLaunchAnimationParams_whennNull_ignoresIfShadeHasNoBlur() {
+        val animProgress = ActivityLaunchAnimator.ExpandAnimationParameters()
+        animProgress.linearProgress = 0.5f
+        `when`(shadeSpring.radius).thenReturn(0)
+        notificationShadeDepthController.notificationLaunchAnimationParams = animProgress
+        verify(shadeSpring, never()).animateTo(anyInt(), any())
+    }
+
     private fun <T : Any> safeEq(value: T): T {
         return eq(value) ?: value
     }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/ActivityLaunchAnimatorTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/ActivityLaunchAnimatorTest.java
index a07cfc3..cdef49d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/ActivityLaunchAnimatorTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/ActivityLaunchAnimatorTest.java
@@ -19,7 +19,6 @@
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyLong;
 import static org.mockito.Mockito.doAnswer;
-import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
@@ -31,6 +30,7 @@
 import android.view.View;
 
 import com.android.systemui.SysuiTestCase;
+import com.android.systemui.statusbar.NotificationShadeDepthController;
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
 import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
 import com.android.systemui.statusbar.phone.NotificationPanelViewController;
@@ -39,8 +39,12 @@
 
 import org.junit.Assert;
 import org.junit.Before;
+import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
 
 @SmallTest
 @RunWith(AndroidTestingRunner.class)
@@ -48,14 +52,22 @@
 public class ActivityLaunchAnimatorTest extends SysuiTestCase {
 
     private ActivityLaunchAnimator mLaunchAnimator;
-    private ActivityLaunchAnimator.Callback mCallback = mock(ActivityLaunchAnimator.Callback.class);
-    private NotificationShadeWindowViewController mNotificationShadeWindowViewController = mock(
-            NotificationShadeWindowViewController.class);
-    private NotificationShadeWindowView mNotificationShadeWindowView = mock(
-            NotificationShadeWindowView.class);
-    private NotificationListContainer mNotificationContainer
-            = mock(NotificationListContainer.class);
-    private ExpandableNotificationRow mRow = mock(ExpandableNotificationRow.class);
+    @Mock
+    private ActivityLaunchAnimator.Callback mCallback;
+    @Mock
+    private NotificationShadeWindowViewController mNotificationShadeWindowViewController;
+    @Mock
+    private NotificationShadeWindowView mNotificationShadeWindowView;
+    @Mock
+    private NotificationListContainer mNotificationContainer;
+    @Mock
+    private ExpandableNotificationRow mRow;
+    @Mock
+    private NotificationShadeDepthController mNotificationShadeDepthController;
+    @Mock
+    private NotificationPanelViewController mNotificationPanelViewController;
+    @Rule
+    public MockitoRule rule = MockitoJUnit.rule();
 
     @Before
     public void setUp() throws Exception {
@@ -66,7 +78,8 @@
         mLaunchAnimator = new ActivityLaunchAnimator(
                 mNotificationShadeWindowViewController,
                 mCallback,
-                mock(NotificationPanelViewController.class),
+                mNotificationPanelViewController,
+                mNotificationShadeDepthController,
                 mNotificationContainer);
 
     }
diff --git a/services/core/java/com/android/server/TEST_MAPPING b/services/core/java/com/android/server/TEST_MAPPING
index 059eb6a..df16058 100644
--- a/services/core/java/com/android/server/TEST_MAPPING
+++ b/services/core/java/com/android/server/TEST_MAPPING
@@ -32,7 +32,7 @@
             "name": "CtsWindowManagerDeviceTestCases",
             "options": [
                 {
-                    "include-filter": "android.server.wm.ToastTest"
+                    "include-filter": "android.server.wm.ToastWindowTest"
                 }
             ],
             "file_patterns": ["NotificationManagerService\\.java"]
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 689f64d0..85d28831 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -17698,7 +17698,7 @@
 
             proc.setReportedForegroundServiceTypes(fgServiceTypes);
             ProcessChangeItem item = enqueueProcessChangeItemLocked(proc.pid, proc.info.uid);
-            item.changes = ProcessChangeItem.CHANGE_FOREGROUND_SERVICES;
+            item.changes |= ProcessChangeItem.CHANGE_FOREGROUND_SERVICES;
             item.foregroundServiceTypes = fgServiceTypes;
         }
         if (oomAdj) {
diff --git a/services/core/java/com/android/server/am/OomAdjuster.java b/services/core/java/com/android/server/am/OomAdjuster.java
index 1412112..dbcb3da 100644
--- a/services/core/java/com/android/server/am/OomAdjuster.java
+++ b/services/core/java/com/android/server/am/OomAdjuster.java
@@ -2385,7 +2385,7 @@
                     "Changes in " + app + ": " + changes);
             ActivityManagerService.ProcessChangeItem item =
                     mService.enqueueProcessChangeItemLocked(app.pid, app.info.uid);
-            item.changes = changes;
+            item.changes |= changes;
             item.foregroundActivities = app.repForegroundActivities;
             item.capability = app.setCapability;
             if (DEBUG_PROCESS_OBSERVERS) Slog.i(TAG_PROCESS_OBSERVERS,
diff --git a/services/core/java/com/android/server/audio/RecordingActivityMonitor.java b/services/core/java/com/android/server/audio/RecordingActivityMonitor.java
index 65f2218..32c6cc3 100644
--- a/services/core/java/com/android/server/audio/RecordingActivityMonitor.java
+++ b/services/core/java/com/android/server/audio/RecordingActivityMonitor.java
@@ -150,14 +150,14 @@
         final AudioRecordingConfiguration config = createRecordingConfiguration(
                 uid, session, source, recordingInfo,
                 portId, silenced, activeSource, clientEffects, effects);
-        if (source == MediaRecorder.AudioSource.REMOTE_SUBMIX) {
+        if (source == MediaRecorder.AudioSource.REMOTE_SUBMIX
+                && (event == AudioManager.RECORD_CONFIG_EVENT_START
+                        || event == AudioManager.RECORD_CONFIG_EVENT_UPDATE)) {
             final AudioDeviceInfo device = config.getAudioDevice();
-            if (AudioSystem.LEGACY_REMOTE_SUBMIX_ADDRESS.equals(device.getAddress())) {
+            if (device != null
+                    && AudioSystem.LEGACY_REMOTE_SUBMIX_ADDRESS.equals(device.getAddress())) {
                 mLegacyRemoteSubmixRiid.set(riid);
-                if (event == AudioManager.RECORD_CONFIG_EVENT_START
-                        || event == AudioManager.RECORD_CONFIG_EVENT_UPDATE) {
-                    mLegacyRemoteSubmixActive.set(true);
-                }
+                mLegacyRemoteSubmixActive.set(true);
             }
         }
         if (MediaRecorder.isSystemOnlyAudioSource(source)) {
diff --git a/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java b/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java
index 4f51fbd..52e9d7c 100644
--- a/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java
+++ b/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java
@@ -68,10 +68,8 @@
     private static final String TAG = "MR2ServiceImpl";
     private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
 
-    /**
-     * TODO: Change this with the real request ID from MediaRouter2 when
-     * MediaRouter2 needs to get notified for the failures.
-     */
+    // TODO: (In Android S or later) if we add callback methods for generic failures
+    //       in MediaRouter2, remove this constant and replace the usages with the real request IDs.
     private static final long DUMMY_REQUEST_ID = -1;
 
     private final Context mContext;
diff --git a/services/core/java/com/android/server/policy/WindowManagerPolicy.java b/services/core/java/com/android/server/policy/WindowManagerPolicy.java
index 7eb3f01..d89605a 100644
--- a/services/core/java/com/android/server/policy/WindowManagerPolicy.java
+++ b/services/core/java/com/android/server/policy/WindowManagerPolicy.java
@@ -542,12 +542,6 @@
         void unregisterPointerEventListener(PointerEventListener listener, int displayId);
 
         /**
-         * Retrieves the {@param outBounds} from the stack matching the {@param windowingMode} and
-         * {@param activityType}.
-         */
-        void getStackBounds(int windowingMode, int activityType, Rect outBounds);
-
-        /**
          * @return The currently active input method window.
          */
         WindowState getInputMethodWindowLw();
diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java
index 367151c..221258e 100644
--- a/services/core/java/com/android/server/wm/DisplayPolicy.java
+++ b/services/core/java/com/android/server/wm/DisplayPolicy.java
@@ -3246,9 +3246,14 @@
                 mTopFullscreenOpaqueWindowState, mTopFullscreenOpaqueOrDimmingWindowState);
         final int dockedAppearance = updateLightStatusBarAppearanceLw(0 /* vis */,
                 mTopDockedOpaqueWindowState, mTopDockedOpaqueOrDimmingWindowState);
-        mService.getStackBounds(
-                WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, ACTIVITY_TYPE_STANDARD, mDockedStackBounds);
-        final boolean inSplitScreen = !mDockedStackBounds.isEmpty();
+        final boolean inSplitScreen =
+                mService.mRoot.getDefaultDisplay().mTaskContainers.isSplitScreenModeActivated();
+        if (inSplitScreen) {
+            mService.getStackBounds(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, ACTIVITY_TYPE_STANDARD,
+                    mDockedStackBounds);
+        } else {
+            mDockedStackBounds.setEmpty();
+        }
         mService.getStackBounds(inSplitScreen ? WINDOWING_MODE_SPLIT_SCREEN_SECONDARY
                         : WINDOWING_MODE_FULLSCREEN,
                 ACTIVITY_TYPE_UNDEFINED, mNonDockedStackBounds);
diff --git a/services/core/java/com/android/server/wm/WallpaperController.java b/services/core/java/com/android/server/wm/WallpaperController.java
index 29a2e18..e43f4b4 100644
--- a/services/core/java/com/android/server/wm/WallpaperController.java
+++ b/services/core/java/com/android/server/wm/WallpaperController.java
@@ -304,7 +304,11 @@
         }
     }
 
-    boolean updateWallpaperOffset(WindowState wallpaperWin, int dw, int dh, boolean sync) {
+    boolean updateWallpaperOffset(WindowState wallpaperWin, boolean sync) {
+        final DisplayInfo displayInfo = wallpaperWin.getDisplayInfo();
+        final int dw = displayInfo.logicalWidth;
+        final int dh = displayInfo.logicalHeight;
+
         int xOffset = 0;
         int yOffset = 0;
         boolean rawChanged = false;
@@ -444,10 +448,6 @@
     }
 
     private void updateWallpaperOffsetLocked(WindowState changingTarget, boolean sync) {
-        final DisplayInfo displayInfo = mDisplayContent.getDisplayInfo();
-        final int dw = displayInfo.logicalWidth;
-        final int dh = displayInfo.logicalHeight;
-
         WindowState target = mWallpaperTarget;
         if (target != null) {
             if (target.mWallpaperX >= 0) {
@@ -484,7 +484,7 @@
         }
 
         for (int curTokenNdx = mWallpaperTokens.size() - 1; curTokenNdx >= 0; curTokenNdx--) {
-            mWallpaperTokens.get(curTokenNdx).updateWallpaperOffset(dw, dh, sync);
+            mWallpaperTokens.get(curTokenNdx).updateWallpaperOffset(sync);
         }
     }
 
diff --git a/services/core/java/com/android/server/wm/WallpaperWindowToken.java b/services/core/java/com/android/server/wm/WallpaperWindowToken.java
index e29580b..203ca25 100644
--- a/services/core/java/com/android/server/wm/WallpaperWindowToken.java
+++ b/services/core/java/com/android/server/wm/WallpaperWindowToken.java
@@ -73,11 +73,11 @@
         }
     }
 
-    void updateWallpaperOffset(int dw, int dh, boolean sync) {
+    void updateWallpaperOffset(boolean sync) {
         final WallpaperController wallpaperController = mDisplayContent.mWallpaperController;
         for (int wallpaperNdx = mChildren.size() - 1; wallpaperNdx >= 0; wallpaperNdx--) {
             final WindowState wallpaper = mChildren.get(wallpaperNdx);
-            if (wallpaperController.updateWallpaperOffset(wallpaper, dw, dh, sync)) {
+            if (wallpaperController.updateWallpaperOffset(wallpaper, sync)) {
                 // We only want to be synchronous with one wallpaper.
                 sync = false;
             }
@@ -85,10 +85,6 @@
     }
 
     void updateWallpaperVisibility(boolean visible) {
-        final DisplayInfo displayInfo = mDisplayContent.getDisplayInfo();
-        final int dw = displayInfo.logicalWidth;
-        final int dh = displayInfo.logicalHeight;
-
         if (isVisible() != visible) {
             // Need to do a layout to ensure the wallpaper now has the correct size.
             mDisplayContent.setLayoutNeeded();
@@ -98,7 +94,7 @@
         for (int wallpaperNdx = mChildren.size() - 1; wallpaperNdx >= 0; wallpaperNdx--) {
             final WindowState wallpaper = mChildren.get(wallpaperNdx);
             if (visible) {
-                wallpaperController.updateWallpaperOffset(wallpaper, dw, dh, false);
+                wallpaperController.updateWallpaperOffset(wallpaper, false /* sync */);
             }
 
             wallpaper.dispatchWallpaperVisibility(visible);
@@ -145,19 +141,11 @@
             }
         }
 
-        DisplayInfo displayInfo = getFixedRotationTransformDisplayInfo();
-        if (displayInfo == null) {
-            displayInfo = mDisplayContent.getDisplayInfo();
-        }
-
-        final int dw = displayInfo.logicalWidth;
-        final int dh = displayInfo.logicalHeight;
-
         for (int wallpaperNdx = mChildren.size() - 1; wallpaperNdx >= 0; wallpaperNdx--) {
             final WindowState wallpaper = mChildren.get(wallpaperNdx);
 
             if (visible) {
-                wallpaperController.updateWallpaperOffset(wallpaper, dw, dh, false);
+                wallpaperController.updateWallpaperOffset(wallpaper, false /* sync */);
             }
 
             // First, make sure the client has the current visibility state.
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 8e45752..dfaa0ec 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -2332,9 +2332,7 @@
             Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
 
             if (toBeDisplayed && win.mIsWallpaper) {
-                DisplayInfo displayInfo = displayContent.getDisplayInfo();
-                displayContent.mWallpaperController.updateWallpaperOffset(
-                        win, displayInfo.logicalWidth, displayInfo.logicalHeight, false);
+                displayContent.mWallpaperController.updateWallpaperOffset(win, false /* sync */);
             }
             if (win.mActivityRecord != null) {
                 win.mActivityRecord.updateReportedVisibilityLocked();
@@ -2782,7 +2780,6 @@
                 aspectRatio);
     }
 
-    @Override
     public void getStackBounds(int windowingMode, int activityType, Rect bounds) {
         synchronized (mGlobalLock) {
             final ActivityStack stack = mRoot.getStack(windowingMode, activityType);
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 7dcf375..b87d181 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -1101,7 +1101,6 @@
                 }
             }
 
-            final ActivityStack stack = getRootTask();
             layoutDisplayFrame = new Rect(windowFrames.mDisplayFrame);
             windowFrames.mDisplayFrame.set(windowFrames.mContainingFrame);
             layoutXDiff = mInsetFrame.left - windowFrames.mContainingFrame.left;
@@ -1205,8 +1204,7 @@
 
         if (mIsWallpaper && (fw != windowFrames.mFrame.width()
                 || fh != windowFrames.mFrame.height())) {
-            dc.mWallpaperController.updateWallpaperOffset(this,
-                    displayInfo.logicalWidth, displayInfo.logicalHeight, false /* sync */);
+            dc.mWallpaperController.updateWallpaperOffset(this, false /* sync */);
         }
 
         // Calculate relative frame
diff --git a/services/net/java/android/net/ip/IpClientManager.java b/services/net/java/android/net/ip/IpClientManager.java
index 09e333e..db464e7 100644
--- a/services/net/java/android/net/ip/IpClientManager.java
+++ b/services/net/java/android/net/ip/IpClientManager.java
@@ -21,6 +21,7 @@
 import android.net.NattKeepalivePacketData;
 import android.net.ProxyInfo;
 import android.net.TcpKeepalivePacketData;
+import android.net.shared.Layer2Information;
 import android.net.shared.ProvisioningConfiguration;
 import android.net.util.KeepalivePacketDataUtil;
 import android.os.Binder;
@@ -292,4 +293,20 @@
             Binder.restoreCallingIdentity(token);
         }
     }
+
+    /**
+     * Update the bssid, L2 key and group hint layer2 information.
+     */
+    public boolean updateLayer2Information(Layer2Information info) {
+        final long token = Binder.clearCallingIdentity();
+        try {
+            mIpClient.updateLayer2Information(info.toStableParcelable());
+            return true;
+        } catch (RemoteException e) {
+            log("Error updating layer2 information", e);
+            return false;
+        } finally {
+            Binder.restoreCallingIdentity(token);
+        }
+    }
 }
diff --git a/services/people/java/com/android/server/people/data/DataManager.java b/services/people/java/com/android/server/people/data/DataManager.java
index 136ee91..c87ece2 100644
--- a/services/people/java/com/android/server/people/data/DataManager.java
+++ b/services/people/java/com/android/server/people/data/DataManager.java
@@ -220,7 +220,7 @@
         String mimeType = intentFilter != null ? intentFilter.getDataType(0) : null;
         @Event.EventType int eventType = mimeTypeToShareEventType(mimeType);
         EventHistoryImpl eventHistory;
-        if (ChooserActivity.LAUNCH_LOCATON_DIRECT_SHARE.equals(event.getLaunchLocation())) {
+        if (ChooserActivity.LAUNCH_LOCATION_DIRECT_SHARE.equals(event.getLaunchLocation())) {
             // Direct share event
             if (appTarget.getShortcutInfo() == null) {
                 return;
diff --git a/services/tests/servicestests/src/com/android/server/people/data/DataManagerTest.java b/services/tests/servicestests/src/com/android/server/people/data/DataManagerTest.java
index 5199604..728e149 100644
--- a/services/tests/servicestests/src/com/android/server/people/data/DataManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/people/data/DataManagerTest.java
@@ -299,7 +299,7 @@
                 .build();
         AppTargetEvent appTargetEvent =
                 new AppTargetEvent.Builder(appTarget, AppTargetEvent.ACTION_LAUNCH)
-                        .setLaunchLocation(ChooserActivity.LAUNCH_LOCATON_DIRECT_SHARE)
+                        .setLaunchLocation(ChooserActivity.LAUNCH_LOCATION_DIRECT_SHARE)
                         .build();
         IntentFilter intentFilter = new IntentFilter(Intent.ACTION_SEND, "image/jpg");
         mDataManager.reportShareTargetEvent(appTargetEvent, intentFilter);
@@ -319,7 +319,7 @@
                 .build();
         AppTargetEvent appTargetEvent =
                 new AppTargetEvent.Builder(appTarget, AppTargetEvent.ACTION_LAUNCH)
-                        .setLaunchLocation(ChooserActivity.LAUNCH_LOCATON_DIRECT_SHARE)
+                        .setLaunchLocation(ChooserActivity.LAUNCH_LOCATION_DIRECT_SHARE)
                         .build();
         IntentFilter intentFilter = new IntentFilter(Intent.ACTION_SEND, "image/jpg");
 
@@ -667,7 +667,7 @@
                 .build();
         AppTargetEvent appTargetEvent =
                 new AppTargetEvent.Builder(appTarget, AppTargetEvent.ACTION_LAUNCH)
-                        .setLaunchLocation(ChooserActivity.LAUNCH_LOCATON_DIRECT_SHARE)
+                        .setLaunchLocation(ChooserActivity.LAUNCH_LOCATION_DIRECT_SHARE)
                         .build();
         IntentFilter intentFilter = new IntentFilter(Intent.ACTION_SEND, "image/jpg");
 
diff --git a/services/tests/servicestests/src/com/android/server/pm/parsing/AndroidPackageParsingEquivalenceTest.kt b/services/tests/servicestests/src/com/android/server/pm/parsing/AndroidPackageParsingEquivalenceTest.kt
index 191c038..5412bb5 100644
--- a/services/tests/servicestests/src/com/android/server/pm/parsing/AndroidPackageParsingEquivalenceTest.kt
+++ b/services/tests/servicestests/src/com/android/server/pm/parsing/AndroidPackageParsingEquivalenceTest.kt
@@ -18,7 +18,9 @@
 
 import android.content.pm.PackageManager
 import android.platform.test.annotations.Presubmit
+import com.google.common.truth.Expect
 import com.google.common.truth.Truth.assertWithMessage
+import org.junit.Rule
 import org.junit.Test
 
 /**
@@ -28,6 +30,9 @@
 @Presubmit
 class AndroidPackageParsingEquivalenceTest : AndroidPackageParsingTestBase() {
 
+    @get:Rule
+    val expect = Expect.create()
+
     @Test
     fun applicationInfoEquality() {
         val flags = PackageManager.GET_META_DATA or PackageManager.GET_SHARED_LIBRARY_FILES
@@ -41,7 +46,8 @@
             } else {
                 "$firstName | $secondName"
             }
-            assertWithMessage(packageName).that(it.first?.dumpToString())
+            expect.withMessage("${it.first?.sourceDir} $packageName")
+                    .that(it.first?.dumpToString())
                     .isEqualTo(it.second?.dumpToString())
         }
     }
@@ -71,7 +77,8 @@
             } else {
                 "$firstName | $secondName"
             }
-            assertWithMessage(packageName).that(it.first?.dumpToString())
+            expect.withMessage("${it.first?.applicationInfo?.sourceDir} $packageName")
+                    .that(it.first?.dumpToString())
                     .isEqualTo(it.second?.dumpToString())
         }
     }
diff --git a/services/tests/servicestests/src/com/android/server/pm/parsing/AndroidPackageParsingTestBase.kt b/services/tests/servicestests/src/com/android/server/pm/parsing/AndroidPackageParsingTestBase.kt
index 19bf9b6..7b1b2d2 100644
--- a/services/tests/servicestests/src/com/android/server/pm/parsing/AndroidPackageParsingTestBase.kt
+++ b/services/tests/servicestests/src/com/android/server/pm/parsing/AndroidPackageParsingTestBase.kt
@@ -29,14 +29,17 @@
 import android.content.pm.ProviderInfo
 import android.os.Debug
 import android.os.Environment
+import android.os.ServiceManager
 import android.util.SparseArray
 import androidx.test.platform.app.InstrumentationRegistry
+import com.android.internal.compat.IPlatformCompat
 import com.android.server.pm.PackageManagerService
 import com.android.server.pm.PackageSetting
 import com.android.server.pm.parsing.pkg.AndroidPackage
 import com.android.server.pm.pkg.PackageStateUnserialized
 import com.android.server.testutils.mockThrowOnUnmocked
 import com.android.server.testutils.whenever
+import org.junit.After
 import org.junit.BeforeClass
 import org.mockito.Mockito
 import org.mockito.Mockito.anyInt
@@ -59,7 +62,27 @@
             setCallback { false /* hasFeature */ }
         }
 
-        protected val packageParser2 = TestPackageParser2()
+        private val platformCompat = IPlatformCompat.Stub
+                .asInterface(ServiceManager.getService(Context.PLATFORM_COMPAT_SERVICE))
+
+        protected val packageParser2 = PackageParser2(null /* separateProcesses */,
+                false /* onlyCoreApps */, context.resources.displayMetrics, null /* cacheDir */,
+                object : PackageParser2.Callback() {
+                    override fun isChangeEnabled(
+                        changeId: Long,
+                        appInfo: ApplicationInfo
+                    ): Boolean {
+                        // This test queries PlatformCompat because prebuilts in the tree
+                        // may not be updated to be compliant with the latest enforcement checks.
+                        return platformCompat.isChangeEnabled(changeId, appInfo)
+                    }
+
+                    // Assume the device doesn't support anything. This will affect permission
+                    // parsing and will force <uses-permission/> declarations to include all
+                    // requiredNotFeature permissions and exclude all requiredFeature permissions.
+                    // This mirrors the old behavior.
+                    override fun hasFeature(feature: String) = false
+                })
 
         /**
          * It would be difficult to mock all possibilities, so just use the APKs on device.
@@ -91,35 +114,31 @@
 
         lateinit var newPackages: List<AndroidPackage>
 
-        var failureInBeforeClass: Throwable? = null
+        private val thrownInSetUp = mutableListOf<Throwable>()
 
         @Suppress("ConstantConditionIf")
         @JvmStatic
         @BeforeClass
         fun setUpPackages() {
-            failureInBeforeClass = null
-            try {
-                this.oldPackages = apks.map {
+            this.oldPackages = apks.mapNotNull {
+                tryOrNull {
                     packageParser.parsePackage(it, PackageParser.PARSE_IS_SYSTEM_DIR, false)
                 }
+            }
 
-                this.newPackages = apks.map {
+            this.newPackages = apks.mapNotNull {
+                tryOrNull {
                     packageParser2.parsePackage(it, PackageParser.PARSE_IS_SYSTEM_DIR, false)
                 }
+            }
 
-                if (DUMP_HPROF_TO_EXTERNAL) {
-                    System.gc()
-                    Environment.getExternalStorageDirectory()
-                            .resolve(
-                                    "${AndroidPackageParsingTestBase::class.java.simpleName}.hprof")
-                            .absolutePath
-                            .run(Debug::dumpHprofData)
-                }
-            } catch (t: Throwable) {
-                // If we crash here we cause a tool failure (because we don't run any of the tests
-                // in the subclasses, leading to a difference between expected and actual test
-                // result counts).
-                failureInBeforeClass = t
+            if (DUMP_HPROF_TO_EXTERNAL) {
+                System.gc()
+                Environment.getExternalStorageDirectory()
+                        .resolve(
+                                "${AndroidPackageParsingTestBase::class.java.simpleName}.hprof")
+                        .absolutePath
+                        .run(Debug::dumpHprofData)
             }
         }
 
@@ -146,13 +165,36 @@
             this.pkg = aPkg
             whenever(pkgState) { PackageStateUnserialized() }
         }
+
+        private fun <T> tryOrNull(block: () -> T) = try {
+            block()
+        } catch (t: Throwable) {
+            thrownInSetUp.add(t)
+            null
+        }
     }
 
-    @org.junit.Before
+    @After
     fun verifySetUpPackages() {
-        failureInBeforeClass?.let {
-            throw AssertionError("setUpPackages failed:", it)
-        }
+        if (thrownInSetUp.isEmpty()) return
+        val exception = AssertionError("setUpPackages failed with ${thrownInSetUp.size} errors:\n" +
+                thrownInSetUp.joinToString(separator = "\n") { it.message.orEmpty() })
+
+        /*
+            Testing infrastructure doesn't currently support errors thrown in @AfterClass,
+            so instead it's thrown here. But to avoid throwing a massive repeated stack for every
+            test method, only throw on the first method run in the class, clearing the list so that
+            subsequent methods can run without failing. Doing this in @After lets true method
+            failures propagate, as those should throw before this does.
+
+            This will cause the failure to be attached to a different method depending on run order,
+            which could make comparisons difficult. So if a failure points here, it's worth
+            checking failures for all methods in all subclasses.
+
+            TODO: When infrastructure supports @AfterClass errors, move this
+        */
+        thrownInSetUp.clear()
+        throw exception
     }
 
     // The following methods dump an exact set of fields from the object to compare, because
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
index 9cfee34..38b3d76 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
@@ -1039,6 +1039,13 @@
         assertEquals(config90.orientation, app.getConfiguration().orientation);
         assertEquals(config90.windowConfiguration.getBounds(), app.getBounds());
 
+        // Force the negative offset to verify it can be updated.
+        mWallpaperWindow.mWinAnimator.mXOffset = mWallpaperWindow.mWinAnimator.mYOffset = -1;
+        assertTrue(mDisplayContent.mWallpaperController.updateWallpaperOffset(mWallpaperWindow,
+                false /* sync */));
+        assertThat(mWallpaperWindow.mWinAnimator.mXOffset).isGreaterThan(-1);
+        assertThat(mWallpaperWindow.mWinAnimator.mYOffset).isGreaterThan(-1);
+
         mDisplayContent.mAppTransition.notifyAppTransitionFinishedLocked(app.token);
 
         // The animation in old rotation should be cancelled.
diff --git a/tests/net/common/java/android/net/LinkPropertiesTest.java b/tests/net/common/java/android/net/LinkPropertiesTest.java
index b4f0daa..8de27e8 100644
--- a/tests/net/common/java/android/net/LinkPropertiesTest.java
+++ b/tests/net/common/java/android/net/LinkPropertiesTest.java
@@ -445,14 +445,20 @@
         // Check comparisons work.
         LinkProperties lp2 = new LinkProperties(lp);
         assertAllRoutesHaveInterface("wlan0", lp2);
-        assertEquals(0, lp.compareAllRoutes(lp2).added.size());
-        assertEquals(0, lp.compareAllRoutes(lp2).removed.size());
+        // LinkProperties#compareAllRoutes exists both in R and before R, but the return type
+        // changed in R, so a test compiled with the R version of LinkProperties cannot run on Q.
+        if (isAtLeastR()) {
+            assertEquals(0, lp.compareAllRoutes(lp2).added.size());
+            assertEquals(0, lp.compareAllRoutes(lp2).removed.size());
+        }
 
         lp2.setInterfaceName("p2p0");
         assertAllRoutesHaveInterface("p2p0", lp2);
         assertAllRoutesNotHaveInterface("wlan0", lp2);
-        assertEquals(3, lp.compareAllRoutes(lp2).added.size());
-        assertEquals(3, lp.compareAllRoutes(lp2).removed.size());
+        if (isAtLeastR()) {
+            assertEquals(3, lp.compareAllRoutes(lp2).added.size());
+            assertEquals(3, lp.compareAllRoutes(lp2).removed.size());
+        }
 
         // Remove route with incorrect interface, no route removed.
         lp.removeRoute(new RouteInfo(prefix2, null, null));
@@ -946,7 +952,7 @@
 
     }
 
-    @Test
+    @Test @IgnoreUpTo(Build.VERSION_CODES.Q)
     public void testCompareResult() {
         // Either adding or removing items
         compareResult(Arrays.asList(1, 2, 3, 4), Arrays.asList(1),