Merge "Added missing protobuf for PeopleService data persistence."
diff --git a/api/current.txt b/api/current.txt
index c2bbb6d..7bfd9a4 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -289,7 +289,7 @@
     field public static final int allowBackup = 16843392; // 0x1010280
     field public static final int allowClearUserData = 16842757; // 0x1010005
     field public static final int allowEmbedded = 16843765; // 0x10103f5
-    field public static final int allowNativeHeapPointerTagging = 16844311; // 0x1010617
+    field public static final int allowNativeHeapPointerTagging = 16844307; // 0x1010613
     field public static final int allowParallelSyncs = 16843570; // 0x1010332
     field public static final int allowSingleTap = 16843353; // 0x1010259
     field public static final int allowTaskReparenting = 16843268; // 0x1010204
@@ -335,9 +335,6 @@
     field public static final int autoUrlDetect = 16843404; // 0x101028c
     field public static final int autoVerify = 16844014; // 0x10104ee
     field public static final int autofillHints = 16844118; // 0x1010556
-    field public static final int autofillInlineSuggestionChip = 16844307; // 0x1010613
-    field public static final int autofillInlineSuggestionSubtitle = 16844309; // 0x1010615
-    field public static final int autofillInlineSuggestionTitle = 16844308; // 0x1010614
     field public static final int autofilledHighlight = 16844136; // 0x1010568
     field public static final int background = 16842964; // 0x10100d4
     field public static final int backgroundDimAmount = 16842802; // 0x1010032
@@ -1082,6 +1079,7 @@
     field public static final int preferenceScreenStyle = 16842891; // 0x101008b
     field public static final int preferenceStyle = 16842894; // 0x101008e
     field public static final int presentationTheme = 16843712; // 0x10103c0
+    field public static final int preserveLegacyExternalStorage = 16844308; // 0x1010614
     field public static final int previewImage = 16843482; // 0x10102da
     field public static final int primaryContentAlpha = 16844114; // 0x1010552
     field public static final int priority = 16842780; // 0x101001c
@@ -2259,7 +2257,6 @@
     field public static final int ThemeOverlay_Material_Dialog = 16974550; // 0x10302d6
     field public static final int ThemeOverlay_Material_Dialog_Alert = 16974551; // 0x10302d7
     field public static final int ThemeOverlay_Material_Light = 16974410; // 0x103024a
-    field public static final int Theme_AutofillInlineSuggestion = 16974565; // 0x10302e5
     field public static final int Theme_Black = 16973832; // 0x1030008
     field public static final int Theme_Black_NoTitleBar = 16973833; // 0x1030009
     field public static final int Theme_Black_NoTitleBar_Fullscreen = 16973834; // 0x103000a
@@ -43970,7 +43967,7 @@
 
 package android.service.quickaccesswallet {
 
-  public final class GetWalletCardsCallback {
+  public interface GetWalletCardsCallback {
     method public void onFailure(@NonNull android.service.quickaccesswallet.GetWalletCardsError);
     method public void onSuccess(@NonNull android.service.quickaccesswallet.GetWalletCardsResponse);
   }
@@ -44011,7 +44008,6 @@
     method public abstract void onWalletCardsRequested(@NonNull android.service.quickaccesswallet.GetWalletCardsRequest, @NonNull android.service.quickaccesswallet.GetWalletCardsCallback);
     method public abstract void onWalletDismissed();
     method public final void sendWalletServiceEvent(@NonNull android.service.quickaccesswallet.WalletServiceEvent);
-    field public static final String ACTION_DISMISS_WALLET = "android.service.quickaccesswallet.action.DISMISS_WALLET";
     field public static final String ACTION_VIEW_WALLET = "android.service.quickaccesswallet.action.VIEW_WALLET";
     field public static final String ACTION_VIEW_WALLET_SETTINGS = "android.service.quickaccesswallet.action.VIEW_WALLET_SETTINGS";
     field public static final String SERVICE_INTERFACE = "android.service.quickaccesswallet.QuickAccessWalletService";
diff --git a/api/system-current.txt b/api/system-current.txt
index c6d4eb3..773cd4d 100755
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -257,7 +257,6 @@
 
   public static final class R.attr {
     field public static final int allowClearUserDataOnFailedRestore = 16844288; // 0x1010600
-    field public static final int isAutofillInlineSuggestionTheme = 16844310; // 0x1010616
     field public static final int isVrOnly = 16844152; // 0x1010578
     field public static final int minExtensionVersion = 16844306; // 0x1010612
     field public static final int requiredSystemPropertyName = 16844133; // 0x1010565
@@ -4912,8 +4911,10 @@
   }
 
   public final class TunerConstants {
-    field public static final int INVALID_STREAM_ID = -1; // 0xffffffff
-    field public static final int INVALID_TS_PID = -1; // 0xffffffff
+    field public static final int INVALID_AV_SYNC_ID = -1; // 0xffffffff
+    field public static final int INVALID_FILTER_ID = -1; // 0xffffffff
+    field public static final int INVALID_STREAM_ID = 65535; // 0xffff
+    field public static final int INVALID_TS_PID = 65535; // 0xffff
     field public static final int RESULT_INVALID_ARGUMENT = 4; // 0x4
     field public static final int RESULT_INVALID_STATE = 3; // 0x3
     field public static final int RESULT_NOT_INITIALIZED = 2; // 0x2
@@ -10202,6 +10203,7 @@
     method public void onConnected();
     method public void onDisconnected();
     method public void onFillRequest(@NonNull android.service.autofill.augmented.FillRequest, @NonNull android.os.CancellationSignal, @NonNull android.service.autofill.augmented.FillController, @NonNull android.service.autofill.augmented.FillCallback);
+    method public final boolean requestAutofill(@NonNull android.content.ComponentName, @NonNull android.view.autofill.AutofillId);
     field public static final String SERVICE_INTERFACE = "android.service.autofill.augmented.AugmentedAutofillService";
   }
 
diff --git a/api/test-current.txt b/api/test-current.txt
index 7284fe6..0a821e4 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -2976,6 +2976,9 @@
     field public static final String ENABLED_VR_LISTENERS = "enabled_vr_listeners";
     field public static final String LOCATION_ACCESS_CHECK_DELAY_MILLIS = "location_access_check_delay_millis";
     field public static final String LOCATION_ACCESS_CHECK_INTERVAL_MILLIS = "location_access_check_interval_millis";
+    field public static final String LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS = "lock_screen_allow_private_notifications";
+    field public static final String LOCK_SCREEN_SHOW_NOTIFICATIONS = "lock_screen_show_notifications";
+    field public static final String NFC_PAYMENT_DEFAULT_COMPONENT = "nfc_payment_default_component";
     field public static final String NOTIFICATION_BADGING = "notification_badging";
     field @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public static final String SYNC_PARENT_SOUNDS = "sync_parent_sounds";
     field public static final String USER_SETUP_COMPLETE = "user_setup_complete";
@@ -3202,6 +3205,7 @@
     method public void onConnected();
     method public void onDisconnected();
     method public void onFillRequest(@NonNull android.service.autofill.augmented.FillRequest, @NonNull android.os.CancellationSignal, @NonNull android.service.autofill.augmented.FillController, @NonNull android.service.autofill.augmented.FillCallback);
+    method public final boolean requestAutofill(@NonNull android.content.ComponentName, @NonNull android.view.autofill.AutofillId);
     field public static final String SERVICE_INTERFACE = "android.service.autofill.augmented.AugmentedAutofillService";
   }
 
@@ -3399,6 +3403,34 @@
 
 }
 
+package android.service.quickaccesswallet {
+
+  public interface QuickAccessWalletClient {
+    method public void addWalletServiceEventListener(@NonNull android.service.quickaccesswallet.QuickAccessWalletClient.WalletServiceEventListener);
+    method @NonNull public static android.service.quickaccesswallet.QuickAccessWalletClient create(@NonNull android.content.Context);
+    method @Nullable public android.content.Intent createWalletIntent();
+    method @Nullable public android.content.Intent createWalletSettingsIntent();
+    method public void disconnect();
+    method public void getWalletCards(@NonNull android.service.quickaccesswallet.GetWalletCardsRequest, @NonNull android.service.quickaccesswallet.QuickAccessWalletClient.OnWalletCardsRetrievedCallback);
+    method public boolean isWalletFeatureAvailable();
+    method public boolean isWalletFeatureAvailableWhenDeviceLocked();
+    method public boolean isWalletServiceAvailable();
+    method public void notifyWalletDismissed();
+    method public void removeWalletServiceEventListener(@NonNull android.service.quickaccesswallet.QuickAccessWalletClient.WalletServiceEventListener);
+    method public void selectWalletCard(@NonNull android.service.quickaccesswallet.SelectWalletCardRequest);
+  }
+
+  public static interface QuickAccessWalletClient.OnWalletCardsRetrievedCallback {
+    method public void onWalletCardRetrievalError(@NonNull android.service.quickaccesswallet.GetWalletCardsError);
+    method public void onWalletCardsRetrieved(@NonNull android.service.quickaccesswallet.GetWalletCardsResponse);
+  }
+
+  public static interface QuickAccessWalletClient.WalletServiceEventListener {
+    method public void onWalletServiceEvent(@NonNull android.service.quickaccesswallet.WalletServiceEvent);
+  }
+
+}
+
 package android.service.quicksettings {
 
   public class TileService extends android.app.Service {
diff --git a/cmds/statsd/Android.bp b/cmds/statsd/Android.bp
index 956fd29..2ee055d 100644
--- a/cmds/statsd/Android.bp
+++ b/cmds/statsd/Android.bp
@@ -123,11 +123,11 @@
         "libutils",
     ],
     shared_libs: [
-        "libbinder",
+        "libbinder_ndk",
         "libincident",
         "liblog",
         "libstatssocket",
-        "statsd-aidl-cpp",
+        "statsd-aidl-ndk_platform",
     ],
 }
 
diff --git a/cmds/statsd/src/HashableDimensionKey.cpp b/cmds/statsd/src/HashableDimensionKey.cpp
index 6b9d0e4..d95d594 100644
--- a/cmds/statsd/src/HashableDimensionKey.cpp
+++ b/cmds/statsd/src/HashableDimensionKey.cpp
@@ -25,6 +25,7 @@
 
 using std::string;
 using std::vector;
+using android::base::StringPrintf;
 
 // These constants must be kept in sync with those in StatsDimensionsValue.java
 const static int STATS_DIMENSIONS_VALUE_STRING_TYPE = 2;
@@ -73,7 +74,7 @@
                     break;
                 case STRING:
                     childParcel.valueType = STATS_DIMENSIONS_VALUE_STRING_TYPE;
-                    childParcel.stringValue = String16(dim.mValue.str_value.c_str());
+                    childParcel.stringValue = dim.mValue.str_value;
                     break;
                 default:
                     ALOGE("Encountered FieldValue with unsupported value type.");
diff --git a/cmds/statsd/src/HashableDimensionKey.h b/cmds/statsd/src/HashableDimensionKey.h
index 4adcf96..a766bba 100644
--- a/cmds/statsd/src/HashableDimensionKey.h
+++ b/cmds/statsd/src/HashableDimensionKey.h
@@ -16,7 +16,7 @@
 
 #pragma once
 
-#include <android/os/StatsDimensionsValueParcel.h>
+#include <aidl/android/os/StatsDimensionsValueParcel.h>
 #include <utils/JenkinsHash.h>
 #include <vector>
 #include "android-base/stringprintf.h"
@@ -27,7 +27,7 @@
 namespace os {
 namespace statsd {
 
-using android::base::StringPrintf;
+using ::aidl::android::os::StatsDimensionsValueParcel;
 
 struct Metric2Condition {
     int64_t conditionId;
diff --git a/cmds/statsd/src/StatsService.cpp b/cmds/statsd/src/StatsService.cpp
index 168833f..7087c68 100644
--- a/cmds/statsd/src/StatsService.cpp
+++ b/cmds/statsd/src/StatsService.cpp
@@ -28,8 +28,6 @@
 
 #include <android-base/file.h>
 #include <android-base/strings.h>
-#include <binder/IPCThreadState.h>
-#include <binder/PermissionController.h>
 #include <cutils/multiuser.h>
 #include <frameworks/base/cmds/statsd/src/statsd_config.pb.h>
 #include <frameworks/base/cmds/statsd/src/uid_data.pb.h>
@@ -47,85 +45,42 @@
 using android::util::FIELD_COUNT_REPEATED;
 using android::util::FIELD_TYPE_MESSAGE;
 
+using Status = ::ndk::ScopedAStatus;
+
 namespace android {
 namespace os {
 namespace statsd {
 
 constexpr const char* kPermissionDump = "android.permission.DUMP";
-constexpr const char* kPermissionUsage = "android.permission.PACKAGE_USAGE_STATS";
-
-constexpr const char* kOpUsage = "android:get_usage_stats";
 
 #define STATS_SERVICE_DIR "/data/misc/stats-service"
 
 // for StatsDataDumpProto
 const int FIELD_ID_REPORTS_LIST = 1;
 
-static binder::Status ok() {
-    return binder::Status::ok();
-}
-
-static binder::Status exception(uint32_t code, const std::string& msg) {
+static Status exception(int32_t code, const std::string& msg) {
     ALOGE("%s (%d)", msg.c_str(), code);
-    return binder::Status::fromExceptionCode(code, String8(msg.c_str()));
+    return ::ndk::ScopedAStatus(AStatus_fromExceptionCodeWithMessage(code, msg.c_str()));
 }
 
 static bool checkPermission(const char* permission) {
-    pid_t pid = IPCThreadState::self()->getCallingPid();
-    uid_t uid = IPCThreadState::self()->getCallingUid();
+    pid_t pid = AIBinder_getCallingPid();
+    uid_t uid = AIBinder_getCallingUid();
     return checkPermissionForIds(permission, pid, uid);
 }
 
-binder::Status checkUid(uid_t expectedUid) {
-    uid_t uid = IPCThreadState::self()->getCallingUid();
+Status checkUid(uid_t expectedUid) {
+    uid_t uid = AIBinder_getCallingUid();
     if (uid == expectedUid || uid == AID_ROOT) {
-        return ok();
+        return Status::ok();
     } else {
-        return exception(binder::Status::EX_SECURITY,
-                StringPrintf("UID %d is not expected UID %d", uid, expectedUid));
-    }
-}
-
-binder::Status checkDumpAndUsageStats(const String16& packageName) {
-    pid_t pid = IPCThreadState::self()->getCallingPid();
-    uid_t uid = IPCThreadState::self()->getCallingUid();
-
-    // Root, system, and shell always have access
-    if (uid == AID_ROOT || uid == AID_SYSTEM || uid == AID_SHELL) {
-        return ok();
-    }
-
-    // Caller must be granted these permissions
-    if (!checkPermission(kPermissionDump)) {
-        return exception(binder::Status::EX_SECURITY,
-                StringPrintf("UID %d / PID %d lacks permission %s", uid, pid, kPermissionDump));
-    }
-    if (!checkPermission(kPermissionUsage)) {
-        return exception(binder::Status::EX_SECURITY,
-                StringPrintf("UID %d / PID %d lacks permission %s", uid, pid, kPermissionUsage));
-    }
-
-    // Caller must also have usage stats op granted
-    PermissionController pc;
-    switch (pc.noteOp(String16(kOpUsage), uid, packageName)) {
-        case PermissionController::MODE_ALLOWED:
-        case PermissionController::MODE_DEFAULT:
-            return ok();
-        default:
-            return exception(binder::Status::EX_SECURITY,
-                    StringPrintf("UID %d / PID %d lacks app-op %s", uid, pid, kOpUsage));
+        return exception(EX_SECURITY,
+                         StringPrintf("UID %d is not expected UID %d", uid, expectedUid));
     }
 }
 
 #define ENFORCE_UID(uid) {                                        \
-    binder::Status status = checkUid((uid));                      \
-    if (!status.isOk()) {                                         \
-        return status;                                            \
-    }                                                             \
-}
-
-#define ENFORCE_DUMP_AND_USAGE_STATS(packageName) {               \
-    binder::Status status = checkDumpAndUsageStats(packageName);  \
+    Status status = checkUid((uid));                              \
     if (!status.isOk()) {                                         \
         return status;                                            \
     }                                                             \
@@ -134,13 +89,13 @@
 StatsService::StatsService(const sp<Looper>& handlerLooper, shared_ptr<LogEventQueue> queue)
     : mAnomalyAlarmMonitor(new AlarmMonitor(
               MIN_DIFF_TO_UPDATE_REGISTERED_ALARM_SECS,
-              [](const sp<IStatsCompanionService>& sc, int64_t timeMillis) {
+              [](const shared_ptr<IStatsCompanionService>& sc, int64_t timeMillis) {
                   if (sc != nullptr) {
                       sc->setAnomalyAlarm(timeMillis);
                       StatsdStats::getInstance().noteRegisteredAnomalyAlarmChanged();
                   }
               },
-              [](const sp<IStatsCompanionService>& sc) {
+              [](const shared_ptr<IStatsCompanionService>& sc) {
                   if (sc != nullptr) {
                       sc->cancelAnomalyAlarm();
                       StatsdStats::getInstance().noteRegisteredAnomalyAlarmChanged();
@@ -148,19 +103,21 @@
               })),
       mPeriodicAlarmMonitor(new AlarmMonitor(
               MIN_DIFF_TO_UPDATE_REGISTERED_ALARM_SECS,
-              [](const sp<IStatsCompanionService>& sc, int64_t timeMillis) {
+              [](const shared_ptr<IStatsCompanionService>& sc, int64_t timeMillis) {
                   if (sc != nullptr) {
                       sc->setAlarmForSubscriberTriggering(timeMillis);
                       StatsdStats::getInstance().noteRegisteredPeriodicAlarmChanged();
                   }
               },
-              [](const sp<IStatsCompanionService>& sc) {
+              [](const shared_ptr<IStatsCompanionService>& sc) {
                   if (sc != nullptr) {
                       sc->cancelAlarmForSubscriberTriggering();
                       StatsdStats::getInstance().noteRegisteredPeriodicAlarmChanged();
                   }
               })),
-      mEventQueue(queue) {
+      mEventQueue(queue),
+      mStatsCompanionServiceDeathRecipient(AIBinder_DeathRecipient_new(
+              StatsService::statsCompanionServiceDied)) {
     mUidMap = UidMap::getInstance();
     mPullerManager = new StatsPullerManager();
     StatsPuller::SetUidMap(mUidMap);
@@ -169,22 +126,20 @@
             mUidMap, mPullerManager, mAnomalyAlarmMonitor, mPeriodicAlarmMonitor,
             getElapsedRealtimeNs(),
             [this](const ConfigKey& key) {
-                sp<IPendingIntentRef> receiver = mConfigManager->GetConfigReceiver(key);
+                shared_ptr<IPendingIntentRef> receiver = mConfigManager->GetConfigReceiver(key);
                 if (receiver == nullptr) {
-                    VLOG("Could not find a broadcast receiver for %s",
-                        key.ToString().c_str());
+                    VLOG("Could not find a broadcast receiver for %s", key.ToString().c_str());
                     return false;
                 } else if (receiver->sendDataBroadcast(
                            mProcessor->getLastReportTimeNs(key)).isOk()) {
                     return true;
                 } else {
-                    VLOG("Failed to send a broadcast for receiver %s",
-                        key.ToString().c_str());
+                    VLOG("Failed to send a broadcast for receiver %s", key.ToString().c_str());
                     return false;
                 }
             },
             [this](const int& uid, const vector<int64_t>& activeConfigs) {
-                sp<IPendingIntentRef> receiver =
+                shared_ptr<IPendingIntentRef> receiver =
                     mConfigManager->GetActiveConfigsChangedReceiver(uid);
                 if (receiver == nullptr) {
                     VLOG("Could not find receiver for uid %d", uid);
@@ -245,36 +200,6 @@
 }
 
 /**
- * Implement our own because the default binder implementation isn't
- * properly handling SHELL_COMMAND_TRANSACTION.
- */
-status_t StatsService::onTransact(uint32_t code, const Parcel& data, Parcel* reply,
-                                  uint32_t flags) {
-    switch (code) {
-        case SHELL_COMMAND_TRANSACTION: {
-            int in = data.readFileDescriptor();
-            int out = data.readFileDescriptor();
-            int err = data.readFileDescriptor();
-            int argc = data.readInt32();
-            Vector<String8> args;
-            for (int i = 0; i < argc && data.dataAvail() > 0; i++) {
-                args.add(String8(data.readString16()));
-            }
-            sp<IShellCallback> shellCallback = IShellCallback::asInterface(data.readStrongBinder());
-            sp<IResultReceiver> resultReceiver =
-                    IResultReceiver::asInterface(data.readStrongBinder());
-
-            err = command(in, out, err, args, resultReceiver);
-            if (resultReceiver != nullptr) {
-                resultReceiver->send(err);
-            }
-            return NO_ERROR;
-        }
-        default: { return BnStatsd::onTransact(code, data, reply, flags); }
-    }
-}
-
-/**
  * Write data from statsd.
  * Format for statsdStats:  adb shell dumpsys stats --metadata [-v] [--proto]
  * Format for data report:  adb shell dumpsys stats [anything other than --metadata] [--proto]
@@ -283,20 +208,21 @@
  *     (bugreports call "adb shell dumpsys stats --dump-priority NORMAL -a --proto")
  * TODO: Come up with a more robust method of enacting <serviceutils/PriorityDumper.h>.
  */
-status_t StatsService::dump(int fd, const Vector<String16>& args) {
+status_t StatsService::dump(int fd, const char** args, uint32_t numArgs) {
     if (!checkPermission(kPermissionDump)) {
         return PERMISSION_DENIED;
     }
-    int lastArg = args.size() - 1;
+
+    int lastArg = numArgs - 1;
     bool asProto = false;
-    if (lastArg >= 0 && !args[lastArg].compare(String16("--proto"))) { // last argument
+    if (lastArg >= 0 && string(args[lastArg]) == "--proto") { // last argument
         asProto = true;
         lastArg--;
     }
-    if (args.size() > 0 && !args[0].compare(String16("--metadata"))) { // first argument
+    if (numArgs > 0 && string(args[0]) == "--metadata") { // first argument
         // Request is to dump statsd stats.
         bool verbose = false;
-        if (lastArg >= 0 && !args[lastArg].compare(String16("-v"))) {
+        if (lastArg >= 0 && string(args[lastArg]) == "-v") {
             verbose = true;
             lastArg--;
         }
@@ -351,67 +277,72 @@
 /**
  * Implementation of the adb shell cmd stats command.
  */
-status_t StatsService::command(int in, int out, int err, Vector<String8>& args,
-                               sp<IResultReceiver> resultReceiver) {
-    uid_t uid = IPCThreadState::self()->getCallingUid();
-    if (uid != AID_ROOT && uid != AID_SHELL) {
+status_t StatsService::handleShellCommand(int in, int out, int err, const char** argv,
+                                          uint32_t argc) {
+    uid_t uid = AIBinder_getCallingUid();
+    if (uid != AID_ROOT || uid != AID_SHELL) {
         return PERMISSION_DENIED;
     }
 
-    const int argCount = args.size();
-    if (argCount >= 1) {
+    Vector<String8> utf8Args;
+    utf8Args.setCapacity(argc);
+    for (uint32_t i = 0; i < argc; i++) {
+        utf8Args.push(String8(argv[i]));
+    }
+
+    if (argc >= 1) {
         // adb shell cmd stats config ...
-        if (!args[0].compare(String8("config"))) {
-            return cmd_config(in, out, err, args);
+        if (!utf8Args[0].compare(String8("config"))) {
+            return cmd_config(in, out, err, utf8Args);
         }
 
-        if (!args[0].compare(String8("print-uid-map"))) {
-            return cmd_print_uid_map(out, args);
+        if (!utf8Args[0].compare(String8("print-uid-map"))) {
+            return cmd_print_uid_map(out, utf8Args);
         }
 
-        if (!args[0].compare(String8("dump-report"))) {
-            return cmd_dump_report(out, args);
+        if (!utf8Args[0].compare(String8("dump-report"))) {
+            return cmd_dump_report(out, utf8Args);
         }
 
-        if (!args[0].compare(String8("pull-source")) && args.size() > 1) {
-            return cmd_print_pulled_metrics(out, args);
+        if (!utf8Args[0].compare(String8("pull-source")) && argc > 1) {
+            return cmd_print_pulled_metrics(out, utf8Args);
         }
 
-        if (!args[0].compare(String8("send-broadcast"))) {
-            return cmd_trigger_broadcast(out, args);
+        if (!utf8Args[0].compare(String8("send-broadcast"))) {
+            return cmd_trigger_broadcast(out, utf8Args);
         }
 
-        if (!args[0].compare(String8("print-stats"))) {
-            return cmd_print_stats(out, args);
+        if (!utf8Args[0].compare(String8("print-stats"))) {
+            return cmd_print_stats(out, utf8Args);
         }
 
-        if (!args[0].compare(String8("meminfo"))) {
+        if (!utf8Args[0].compare(String8("meminfo"))) {
             return cmd_dump_memory_info(out);
         }
 
-        if (!args[0].compare(String8("write-to-disk"))) {
+        if (!utf8Args[0].compare(String8("write-to-disk"))) {
             return cmd_write_data_to_disk(out);
         }
 
-        if (!args[0].compare(String8("log-app-breadcrumb"))) {
-            return cmd_log_app_breadcrumb(out, args);
+        if (!utf8Args[0].compare(String8("log-app-breadcrumb"))) {
+            return cmd_log_app_breadcrumb(out, utf8Args);
         }
 
-        if (!args[0].compare(String8("log-binary-push"))) {
-            return cmd_log_binary_push(out, args);
+        if (!utf8Args[0].compare(String8("log-binary-push"))) {
+            return cmd_log_binary_push(out, utf8Args);
         }
 
-        if (!args[0].compare(String8("clear-puller-cache"))) {
+        if (!utf8Args[0].compare(String8("clear-puller-cache"))) {
             return cmd_clear_puller_cache(out);
         }
 
-        if (!args[0].compare(String8("print-logs"))) {
-            return cmd_print_logs(out, args);
+        if (!utf8Args[0].compare(String8("print-logs"))) {
+            return cmd_print_logs(out, utf8Args);
         }
-        if (!args[0].compare(String8("send-active-configs"))) {
-            return cmd_trigger_active_config_broadcast(out, args);
+        if (!utf8Args[0].compare(String8("send-active-configs"))) {
+            return cmd_trigger_active_config_broadcast(out, utf8Args);
         }
-        if (!args[0].compare(String8("data-subscribe"))) {
+        if (!utf8Args[0].compare(String8("data-subscribe"))) {
             {
                 std::lock_guard<std::mutex> lock(mShellSubscriberMutex);
                 if (mShellSubscriber == nullptr) {
@@ -419,14 +350,10 @@
                 }
             }
             int timeoutSec = -1;
-            if (argCount >= 2) {
-                timeoutSec = atoi(args[1].c_str());
+            if (argc >= 2) {
+                timeoutSec = atoi(utf8Args[1].c_str());
             }
-            if (resultReceiver == nullptr) {
-                ALOGI("Null resultReceiver given, no subscription will be started");
-                return UNEXPECTED_NULL;
-            }
-            mShellSubscriber->startNewSubscription(in, out, resultReceiver, timeoutSec);
+            mShellSubscriber->startNewSubscription(in, out, timeoutSec);
             return NO_ERROR;
         }
     }
@@ -556,7 +483,7 @@
     const int argCount = args.size();
     if (argCount == 2) {
         // Automatically pick the UID
-        uid = IPCThreadState::self()->getCallingUid();
+        uid = AIBinder_getCallingUid();
         name.assign(args[1].c_str(), args[1].size());
         good = true;
     } else if (argCount == 3) {
@@ -572,17 +499,15 @@
         return UNKNOWN_ERROR;
     }
     ConfigKey key(uid, StrToInt64(name));
-    sp<IPendingIntentRef> receiver = mConfigManager->GetConfigReceiver(key);
+    shared_ptr<IPendingIntentRef> receiver = mConfigManager->GetConfigReceiver(key);
     if (receiver == nullptr) {
         VLOG("Could not find receiver for %s, %s", args[1].c_str(), args[2].c_str());
         return UNKNOWN_ERROR;
-    } else if (receiver->sendDataBroadcast(
-               mProcessor->getLastReportTimeNs(key)).isOk()) {
+    } else if (receiver->sendDataBroadcast(mProcessor->getLastReportTimeNs(key)).isOk()) {
         VLOG("StatsService::trigger broadcast succeeded to %s, %s", args[1].c_str(),
              args[2].c_str());
     } else {
-        VLOG("StatsService::trigger broadcast failed to %s, %s", args[1].c_str(),
-             args[2].c_str());
+        VLOG("StatsService::trigger broadcast failed to %s, %s", args[1].c_str(), args[2].c_str());
         return UNKNOWN_ERROR;
     }
     return NO_ERROR;
@@ -594,7 +519,7 @@
     vector<int64_t> configIds;
     if (argCount == 1) {
         // Automatically pick the uid and send a broadcast that has no active configs.
-        uid = IPCThreadState::self()->getCallingUid();
+        uid = AIBinder_getCallingUid();
         mProcessor->GetActiveConfigs(uid, configIds);
     } else {
         int curArg = 1;
@@ -608,7 +533,7 @@
             }
             curArg++;
         } else {
-            uid = IPCThreadState::self()->getCallingUid();
+            uid = AIBinder_getCallingUid();
         }
         if (curArg == argCount || args[curArg] != "--configs") {
             VLOG("Reached end of args, or specify configs not set. Sending actual active configs,");
@@ -628,7 +553,7 @@
             }
         }
     }
-    sp<IPendingIntentRef> receiver = mConfigManager->GetActiveConfigsChangedReceiver(uid);
+    shared_ptr<IPendingIntentRef> receiver = mConfigManager->GetActiveConfigsChangedReceiver(uid);
     if (receiver == nullptr) {
         VLOG("Could not find receiver for uid %d", uid);
         return UNKNOWN_ERROR;
@@ -651,7 +576,7 @@
 
             if (argCount == 3) {
                 // Automatically pick the UID
-                uid = IPCThreadState::self()->getCallingUid();
+                uid = AIBinder_getCallingUid();
                 name.assign(args[2].c_str(), args[2].size());
                 good = true;
             } else if (argCount == 4) {
@@ -734,7 +659,7 @@
         }
         if (argCount == 2) {
             // Automatically pick the UID
-            uid = IPCThreadState::self()->getCallingUid();
+            uid = AIBinder_getCallingUid();
             name.assign(args[1].c_str(), args[1].size());
             good = true;
         } else if (argCount == 3) {
@@ -826,7 +751,7 @@
     const int argCount = args.size();
     if (argCount == 3) {
         // Automatically pick the UID
-        uid = IPCThreadState::self()->getCallingUid();
+        uid = AIBinder_getCallingUid();
         label = atoi(args[1].c_str());
         state = atoi(args[2].c_str());
         good = true;
@@ -903,9 +828,8 @@
 }
 
 status_t StatsService::cmd_clear_puller_cache(int out) {
-    IPCThreadState* ipc = IPCThreadState::self();
     VLOG("StatsService::cmd_clear_puller_cache with Pid %i, Uid %i",
-            ipc->getCallingPid(), ipc->getCallingUid());
+            AIBinder_getCallingPid(), AIBinder_getCallingUid());
     if (checkPermission(kPermissionDump)) {
         int cleared = mPullerManager->ForceClearPullerCache();
         dprintf(out, "Puller removed %d cached data!\n", cleared);
@@ -916,9 +840,8 @@
 }
 
 status_t StatsService::cmd_print_logs(int out, const Vector<String8>& args) {
-    IPCThreadState* ipc = IPCThreadState::self();
-    VLOG("StatsService::cmd_print_logs with Pid %i, Uid %i", ipc->getCallingPid(),
-         ipc->getCallingUid());
+    VLOG("StatsService::cmd_print_logs with Pid %i, Uid %i", AIBinder_getCallingPid(),
+         AIBinder_getCallingUid());
     if (checkPermission(kPermissionDump)) {
         bool enabled = true;
         if (args.size() >= 2) {
@@ -950,24 +873,24 @@
     }
     uid = goodUid;
 
-    int32_t callingUid = IPCThreadState::self()->getCallingUid();
+    int32_t callingUid = AIBinder_getCallingUid();
     return mEngBuild // UserDebug/EngBuild are allowed to impersonate uids.
             || (callingUid == goodUid) // Anyone can 'impersonate' themselves.
             || (callingUid == AID_ROOT && goodUid == AID_SHELL); // ROOT can impersonate SHELL.
 }
 
-Status StatsService::informAllUidData(const ParcelFileDescriptor& fd) {
+Status StatsService::informAllUidData(const ScopedFileDescriptor& fd) {
     ENFORCE_UID(AID_SYSTEM);
     // Read stream into buffer.
     string buffer;
     if (!android::base::ReadFdToString(fd.get(), &buffer)) {
-        return exception(Status::EX_ILLEGAL_ARGUMENT, "Failed to read all data from the pipe.");
+        return exception(EX_ILLEGAL_ARGUMENT, "Failed to read all data from the pipe.");
     }
 
     // Parse buffer.
     UidData uidData;
     if (!uidData.ParseFromString(buffer)) {
-        return exception(Status::EX_ILLEGAL_ARGUMENT, "Error parsing proto stream for UidData.");
+        return exception(EX_ILLEGAL_ARGUMENT, "Error parsing proto stream for UidData.");
     }
 
     vector<String16> versionStrings;
@@ -1002,20 +925,28 @@
     return Status::ok();
 }
 
-Status StatsService::informOnePackage(const String16& app, int32_t uid, int64_t version,
-                                      const String16& version_string, const String16& installer) {
+Status StatsService::informOnePackage(const string& app, int32_t uid, int64_t version,
+                                      const string& versionString, const string& installer) {
     ENFORCE_UID(AID_SYSTEM);
 
     VLOG("StatsService::informOnePackage was called");
-    mUidMap->updateApp(getElapsedRealtimeNs(), app, uid, version, version_string, installer);
+    // TODO(b/149254662): This is gross. We should consider changing statsd
+    // internals to use std::string.
+    String16 utf16App = String16(app.c_str());
+    String16 utf16VersionString = String16(versionString.c_str());
+    String16 utf16Installer = String16(installer.c_str());
+
+    mUidMap->updateApp(getElapsedRealtimeNs(), utf16App, uid, version, utf16VersionString,
+                       utf16Installer);
     return Status::ok();
 }
 
-Status StatsService::informOnePackageRemoved(const String16& app, int32_t uid) {
+Status StatsService::informOnePackageRemoved(const string& app, int32_t uid) {
     ENFORCE_UID(AID_SYSTEM);
 
     VLOG("StatsService::informOnePackageRemoved was called");
-    mUidMap->removeApp(getElapsedRealtimeNs(), app, uid);
+    String16 utf16App = String16(app.c_str());
+    mUidMap->removeApp(getElapsedRealtimeNs(), utf16App, uid);
     mConfigManager->RemoveConfigs(uid);
     return Status::ok();
 }
@@ -1079,7 +1010,7 @@
 }
 
 void StatsService::sayHiToStatsCompanion() {
-    sp<IStatsCompanionService> statsCompanion = getStatsCompanionService();
+    shared_ptr<IStatsCompanionService> statsCompanion = getStatsCompanionService();
     if (statsCompanion != nullptr) {
         VLOG("Telling statsCompanion that statsd is ready");
         statsCompanion->statsdReady();
@@ -1092,14 +1023,14 @@
     ENFORCE_UID(AID_SYSTEM);
 
     VLOG("StatsService::statsCompanionReady was called");
-    sp<IStatsCompanionService> statsCompanion = getStatsCompanionService();
+    shared_ptr<IStatsCompanionService> statsCompanion = getStatsCompanionService();
     if (statsCompanion == nullptr) {
-        return Status::fromExceptionCode(
-                Status::EX_NULL_POINTER,
-                "statscompanion unavailable despite it contacting statsd!");
+        return exception(EX_NULL_POINTER,
+                         "StatsCompanion unavailable despite it contacting statsd.");
     }
     VLOG("StatsService::statsCompanionReady linking to statsCompanion.");
-    IInterface::asBinder(statsCompanion)->linkToDeath(this);
+    AIBinder_linkToDeath(statsCompanion->asBinder().get(),
+                         mStatsCompanionServiceDeathRecipient.get(), this);
     mPullerManager->SetStatsCompanionService(statsCompanion);
     mAnomalyAlarmMonitor->setStatsCompanionService(statsCompanion);
     mPeriodicAlarmMonitor->setStatsCompanionService(statsCompanion);
@@ -1127,39 +1058,47 @@
     }
 }
 
-Status StatsService::getData(int64_t key, const int32_t callingUid, vector<uint8_t>* output) {
+Status StatsService::getData(int64_t key, const int32_t callingUid, vector<int8_t>* output) {
     ENFORCE_UID(AID_SYSTEM);
 
     VLOG("StatsService::getData with Uid %i", callingUid);
     ConfigKey configKey(callingUid, key);
+    // TODO(b/149254662): Since libbinder_ndk uses int8_t instead of uint8_t,
+    // there are inconsistencies with internal statsd logic. Instead of
+    // modifying lots of files, we create a temporary output array of int8_t and
+    // copy its data into output. This is a bad hack, but hopefully
+    // libbinder_ndk will transition to using uint8_t soon: progress is tracked
+    // in b/144957764. Same applies to StatsService::getMetadata.
+    vector<uint8_t> unsignedOutput;
     // The dump latency does not matter here since we do not include the current bucket, we do not
     // need to pull any new data anyhow.
     mProcessor->onDumpReport(configKey, getElapsedRealtimeNs(), false /* include_current_bucket*/,
-                             true /* erase_data */, GET_DATA_CALLED, FAST, output);
+                             true /* erase_data */, GET_DATA_CALLED, FAST, &unsignedOutput);
+    *output = vector<int8_t>(unsignedOutput.begin(), unsignedOutput.end());
     return Status::ok();
 }
 
-Status StatsService::getMetadata(vector<uint8_t>* output) {
+Status StatsService::getMetadata(vector<int8_t>* output) {
     ENFORCE_UID(AID_SYSTEM);
 
-    StatsdStats::getInstance().dumpStats(output, false); // Don't reset the counters.
+    vector<uint8_t> unsignedOutput;
+    StatsdStats::getInstance().dumpStats(&unsignedOutput, false); // Don't reset the counters.
+    *output = vector<int8_t>(unsignedOutput.begin(), unsignedOutput.end());
     return Status::ok();
 }
 
-Status StatsService::addConfiguration(int64_t key, const vector <uint8_t>& config,
+Status StatsService::addConfiguration(int64_t key, const vector <int8_t>& config,
                                       const int32_t callingUid) {
     ENFORCE_UID(AID_SYSTEM);
 
     if (addConfigurationChecked(callingUid, key, config)) {
         return Status::ok();
     } else {
-        ALOGE("Could not parse malformatted StatsdConfig");
-        return Status::fromExceptionCode(binder::Status::EX_ILLEGAL_ARGUMENT,
-                                         "config does not correspond to a StatsdConfig proto");
+        return exception(EX_ILLEGAL_ARGUMENT, "Could not parse malformatted StatsdConfig.");
     }
 }
 
-bool StatsService::addConfigurationChecked(int uid, int64_t key, const vector<uint8_t>& config) {
+bool StatsService::addConfigurationChecked(int uid, int64_t key, const vector<int8_t>& config) {
     ConfigKey configKey(uid, key);
     StatsdConfig cfg;
     if (config.size() > 0) {  // If the config is empty, skip parsing.
@@ -1180,7 +1119,7 @@
 }
 
 Status StatsService::setDataFetchOperation(int64_t key,
-                                           const sp<IPendingIntentRef>& pir,
+                                           const shared_ptr<IPendingIntentRef>& pir,
                                            const int32_t callingUid) {
     ENFORCE_UID(AID_SYSTEM);
 
@@ -1194,7 +1133,7 @@
     return Status::ok();
 }
 
-Status StatsService::setActiveConfigsChangedOperation(const sp<IPendingIntentRef>& pir,
+Status StatsService::setActiveConfigsChangedOperation(const shared_ptr<IPendingIntentRef>& pir,
                                                       const int32_t callingUid,
                                                       vector<int64_t>* output) {
     ENFORCE_UID(AID_SYSTEM);
@@ -1225,7 +1164,7 @@
 
 Status StatsService::setBroadcastSubscriber(int64_t configId,
                                             int64_t subscriberId,
-                                            const sp<IPendingIntentRef>& pir,
+                                            const shared_ptr<IPendingIntentRef>& pir,
                                             const int32_t callingUid) {
     ENFORCE_UID(AID_SYSTEM);
 
@@ -1252,14 +1191,14 @@
     // Permission check not necessary as it's meant for applications to write to
     // statsd.
     android::util::stats_write(util::APP_BREADCRUMB_REPORTED,
-                               (int32_t) IPCThreadState::self()->getCallingUid(), label,
+                               (int32_t) AIBinder_getCallingUid(), label,
                                state);
     return Status::ok();
 }
 
 Status StatsService::registerPullAtomCallback(int32_t uid, int32_t atomTag, int64_t coolDownNs,
                                     int64_t timeoutNs, const std::vector<int32_t>& additiveFields,
-                                    const sp<android::os::IPullAtomCallback>& pullerCallback) {
+                                    const shared_ptr<IPullAtomCallback>& pullerCallback) {
     ENFORCE_UID(AID_SYSTEM);
 
     VLOG("StatsService::registerPullAtomCallback called.");
@@ -1270,10 +1209,10 @@
 
 Status StatsService::registerNativePullAtomCallback(int32_t atomTag, int64_t coolDownNs,
                                     int64_t timeoutNs, const std::vector<int32_t>& additiveFields,
-                                    const sp<android::os::IPullAtomCallback>& pullerCallback) {
+                                    const shared_ptr<IPullAtomCallback>& pullerCallback) {
 
     VLOG("StatsService::registerNativePullAtomCallback called.");
-    int32_t uid = IPCThreadState::self()->getCallingUid();
+    int32_t uid = AIBinder_getCallingUid();
     mPullerManager->RegisterPullAtomCallback(uid, atomTag, coolDownNs, timeoutNs, additiveFields,
                                              pullerCallback);
     return Status::ok();
@@ -1288,7 +1227,7 @@
 
 Status StatsService::unregisterNativePullAtomCallback(int32_t atomTag) {
     VLOG("StatsService::unregisterNativePullAtomCallback called.");
-    int32_t uid = IPCThreadState::self()->getCallingUid();
+    int32_t uid = AIBinder_getCallingUid();
     mPullerManager->UnregisterPullAtomCallback(uid, atomTag);
     return Status::ok();
 }
@@ -1314,7 +1253,13 @@
     return Status::ok();
 }
 
-void StatsService::binderDied(const wp <IBinder>& who) {
+
+void StatsService::statsCompanionServiceDied(void* cookie) {
+    auto thiz = static_cast<StatsService*>(cookie);
+    thiz->statsCompanionServiceDiedImpl();
+}
+
+void StatsService::statsCompanionServiceDiedImpl() {
     ALOGW("statscompanion service died");
     StatsdStats::getInstance().noteSystemServerRestart(getWallClockSec());
     if (mProcessor != nullptr) {
diff --git a/cmds/statsd/src/StatsService.h b/cmds/statsd/src/StatsService.h
index 0527d43..e6723c8 100644
--- a/cmds/statsd/src/StatsService.h
+++ b/cmds/statsd/src/StatsService.h
@@ -27,27 +27,30 @@
 #include "shell/ShellSubscriber.h"
 #include "statscompanion_util.h"
 
-#include <android/os/BnStatsd.h>
-#include <android/os/IPendingIntentRef.h>
-#include <android/os/IStatsCompanionService.h>
-#include <android/os/IStatsd.h>
-#include <binder/IResultReceiver.h>
-#include <binder/ParcelFileDescriptor.h>
+#include <aidl/android/os/BnStatsd.h>
+#include <aidl/android/os/IPendingIntentRef.h>
+#include <aidl/android/os/IPullAtomCallback.h>
 #include <utils/Looper.h>
 
 #include <mutex>
 
 using namespace android;
-using namespace android::binder;
 using namespace android::os;
 using namespace std;
 
+using Status = ::ndk::ScopedAStatus;
+using aidl::android::os::BnStatsd;
+using aidl::android::os::IPendingIntentRef;
+using aidl::android::os::IPullAtomCallback;
+using ::ndk::ScopedAIBinder_DeathRecipient;
+using ::ndk::ScopedFileDescriptor;
+using std::shared_ptr;
+
 namespace android {
 namespace os {
 namespace statsd {
 
-class StatsService : public BnStatsd,
-                     public IBinder::DeathRecipient {
+class StatsService : public BnStatsd {
 public:
     StatsService(const sp<Looper>& handlerLooper, std::shared_ptr<LogEventQueue> queue);
     virtual ~StatsService();
@@ -55,10 +58,9 @@
     /** The anomaly alarm registered with AlarmManager won't be updated by less than this. */
     const uint32_t MIN_DIFF_TO_UPDATE_REGISTERED_ALARM_SECS = 5;
 
-    virtual status_t onTransact(uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags);
-    virtual status_t dump(int fd, const Vector<String16>& args);
-    virtual status_t command(int inFd, int outFd, int err, Vector<String8>& args,
-                             sp<IResultReceiver> resultReceiver);
+    virtual status_t dump(int fd, const char** args, uint32_t numArgs) override;
+    virtual status_t handleShellCommand(int in, int out, int err, const char** argv,
+                                        uint32_t argc) override;
 
     virtual Status systemRunning();
     virtual Status statsCompanionReady();
@@ -66,10 +68,10 @@
     virtual Status informPollAlarmFired();
     virtual Status informAlarmForSubscriberTriggeringFired();
 
-    virtual Status informAllUidData(const ParcelFileDescriptor& fd);
-    virtual Status informOnePackage(const String16& app, int32_t uid, int64_t version,
-                                    const String16& version_string, const String16& installer);
-    virtual Status informOnePackageRemoved(const String16& app, int32_t uid);
+    virtual Status informAllUidData(const ScopedFileDescriptor& fd);
+    virtual Status informOnePackage(const string& app, int32_t uid, int64_t version,
+                                    const string& versionString, const string& installer);
+    virtual Status informOnePackageRemoved(const string& app, int32_t uid);
     virtual Status informDeviceShutdown();
 
     /**
@@ -92,13 +94,13 @@
      */
     virtual Status getData(int64_t key,
                            const int32_t callingUid,
-                           vector<uint8_t>* output) override;
+                           vector<int8_t>* output) override;
 
 
     /**
      * Binder call for clients to get metadata across all configs in statsd.
      */
-    virtual Status getMetadata(vector<uint8_t>* output) override;
+    virtual Status getMetadata(vector<int8_t>* output) override;
 
 
     /**
@@ -106,14 +108,14 @@
      * should requestData for this configuration.
      */
     virtual Status addConfiguration(int64_t key,
-                                    const vector<uint8_t>& config,
+                                    const vector<int8_t>& config,
                                     const int32_t callingUid) override;
 
     /**
      * Binder call to let clients register the data fetch operation for a configuration.
      */
     virtual Status setDataFetchOperation(int64_t key,
-                                         const sp<IPendingIntentRef>& pir,
+                                         const shared_ptr<IPendingIntentRef>& pir,
                                          const int32_t callingUid) override;
 
     /**
@@ -125,7 +127,7 @@
     /**
      * Binder call to let clients register the active configs changed operation.
      */
-    virtual Status setActiveConfigsChangedOperation(const sp<IPendingIntentRef>& pir,
+    virtual Status setActiveConfigsChangedOperation(const shared_ptr<IPendingIntentRef>& pir,
                                                     const int32_t callingUid,
                                                     vector<int64_t>* output) override;
 
@@ -144,7 +146,7 @@
      */
     virtual Status setBroadcastSubscriber(int64_t configId,
                                           int64_t subscriberId,
-                                          const sp<IPendingIntentRef>& pir,
+                                          const shared_ptr<IPendingIntentRef>& pir,
                                           const int32_t callingUid) override;
 
     /**
@@ -167,14 +169,14 @@
      */
     virtual Status registerPullAtomCallback(int32_t uid, int32_t atomTag, int64_t coolDownNs,
             int64_t timeoutNs, const std::vector<int32_t>& additiveFields,
-            const sp<android::os::IPullAtomCallback>& pullerCallback) override;
+            const shared_ptr<IPullAtomCallback>& pullerCallback) override;
 
     /**
      * Binder call to register a callback function for a pulled atom.
      */
     virtual Status registerNativePullAtomCallback(int32_t atomTag, int64_t coolDownNs,
             int64_t timeoutNs, const std::vector<int32_t>& additiveFields,
-            const sp<android::os::IPullAtomCallback>& pullerCallback) override;
+            const shared_ptr<IPullAtomCallback>& pullerCallback) override;
 
     /**
      * Binder call to unregister any existing callback for the given uid and atom.
@@ -191,9 +193,6 @@
      */
     virtual Status getRegisteredExperimentIds(std::vector<int64_t>* expIdsOut);
 
-    /** IBinder::DeathRecipient */
-    virtual void binderDied(const wp<IBinder>& who) override;
-
 private:
     /**
      * Load system properties at init.
@@ -317,7 +316,7 @@
     /**
      * Adds a configuration after checking permissions and obtaining UID from binder call.
      */
-    bool addConfigurationChecked(int uid, int64_t key, const vector<uint8_t>& config);
+    bool addConfigurationChecked(int uid, int64_t key, const vector<int8_t>& config);
 
     /**
      * Update a configuration.
@@ -325,6 +324,17 @@
     void set_config(int uid, const string& name, const StatsdConfig& config);
 
     /**
+     * Death recipient callback that is called when StatsCompanionService dies.
+     * The cookie is a pointer to a StatsService object.
+     */
+    static void statsCompanionServiceDied(void* cookie);
+
+    /**
+     * Implementation of statsCompanionServiceDied.
+     */
+    void statsCompanionServiceDiedImpl();
+
+    /**
      * Tracks the uid <--> package name mapping.
      */
     sp<UidMap> mUidMap;
@@ -367,6 +377,8 @@
     mutable mutex mShellSubscriberMutex;
     std::shared_ptr<LogEventQueue> mEventQueue;
 
+    ScopedAIBinder_DeathRecipient mStatsCompanionServiceDeathRecipient;
+
     FRIEND_TEST(StatsLogProcessorTest, TestActivationsPersistAcrossSystemServerRestart);
     FRIEND_TEST(StatsServiceTest, TestAddConfig_simple);
     FRIEND_TEST(StatsServiceTest, TestAddConfig_empty);
diff --git a/cmds/statsd/src/anomaly/AlarmMonitor.cpp b/cmds/statsd/src/anomaly/AlarmMonitor.cpp
index bc36dad..02291181 100644
--- a/cmds/statsd/src/anomaly/AlarmMonitor.cpp
+++ b/cmds/statsd/src/anomaly/AlarmMonitor.cpp
@@ -26,17 +26,21 @@
 
 AlarmMonitor::AlarmMonitor(
         uint32_t minDiffToUpdateRegisteredAlarmTimeSec,
-        const std::function<void(const sp<IStatsCompanionService>&, int64_t)>& updateAlarm,
-        const std::function<void(const sp<IStatsCompanionService>&)>& cancelAlarm)
-    : mRegisteredAlarmTimeSec(0), mMinUpdateTimeSec(minDiffToUpdateRegisteredAlarmTimeSec),
+        const std::function<void(const shared_ptr<IStatsCompanionService>&, int64_t)>& updateAlarm,
+        const std::function<void(const shared_ptr<IStatsCompanionService>&)>& cancelAlarm)
+    : mRegisteredAlarmTimeSec(0),
+      mMinUpdateTimeSec(minDiffToUpdateRegisteredAlarmTimeSec),
       mUpdateAlarm(updateAlarm),
       mCancelAlarm(cancelAlarm) {}
 
 AlarmMonitor::~AlarmMonitor() {}
 
-void AlarmMonitor::setStatsCompanionService(sp<IStatsCompanionService> statsCompanionService) {
+void AlarmMonitor::setStatsCompanionService(
+        shared_ptr<IStatsCompanionService> statsCompanionService) {
     std::lock_guard<std::mutex> lock(mLock);
-    sp<IStatsCompanionService> tmpForLock = mStatsCompanionService;
+    // TODO(b/149254662): determine if tmpForLock is needed now that we have moved
+    // from sp to shared_ptr
+    shared_ptr<IStatsCompanionService> tmpForLock = mStatsCompanionService;
     mStatsCompanionService = statsCompanionService;
     if (statsCompanionService == nullptr) {
         VLOG("Erasing link to statsCompanionService");
diff --git a/cmds/statsd/src/anomaly/AlarmMonitor.h b/cmds/statsd/src/anomaly/AlarmMonitor.h
index 219695e..5c34e38 100644
--- a/cmds/statsd/src/anomaly/AlarmMonitor.h
+++ b/cmds/statsd/src/anomaly/AlarmMonitor.h
@@ -18,7 +18,7 @@
 
 #include "anomaly/indexed_priority_queue.h"
 
-#include <android/os/IStatsCompanionService.h>
+#include <aidl/android/os/IStatsCompanionService.h>
 #include <utils/RefBase.h>
 
 #include <unordered_set>
@@ -26,7 +26,9 @@
 
 using namespace android;
 
-using android::os::IStatsCompanionService;
+using aidl::android::os::IStatsCompanionService;
+using std::function;
+using std::shared_ptr;
 using std::unordered_set;
 
 namespace android {
@@ -64,8 +66,9 @@
      * alarm.
      */
     AlarmMonitor(uint32_t minDiffToUpdateRegisteredAlarmTimeSec,
-                 const std::function<void(const sp<IStatsCompanionService>&, int64_t)>& updateAlarm,
-                 const std::function<void(const sp<IStatsCompanionService>&)>& cancelAlarm);
+                 const function<void(const shared_ptr<IStatsCompanionService>&, int64_t)>&
+                         updateAlarm,
+                 const function<void(const shared_ptr<IStatsCompanionService>&)>& cancelAlarm);
     ~AlarmMonitor();
 
     /**
@@ -74,7 +77,7 @@
      * If nullptr, AnomalyMonitor will continue to add/remove alarms, but won't
      * update IStatsCompanionService (until such time as it is set non-null).
      */
-    void setStatsCompanionService(sp<IStatsCompanionService> statsCompanionService);
+    void setStatsCompanionService(shared_ptr<IStatsCompanionService> statsCompanionService);
 
     /**
      * Adds the given alarm (reference) to the queue.
@@ -123,7 +126,7 @@
     /**
      * Binder interface for communicating with StatsCompanionService.
      */
-    sp<IStatsCompanionService> mStatsCompanionService = nullptr;
+    shared_ptr<IStatsCompanionService> mStatsCompanionService = nullptr;
 
     /**
      * Amount by which the soonest projected alarm must differ from
@@ -147,10 +150,10 @@
     int64_t secToMs(uint32_t timeSec);
 
     // Callback function to update the alarm via StatsCompanionService.
-    std::function<void(const sp<IStatsCompanionService>, int64_t)> mUpdateAlarm;
+    std::function<void(const shared_ptr<IStatsCompanionService>, int64_t)> mUpdateAlarm;
 
     // Callback function to cancel the alarm via StatsCompanionService.
-    std::function<void(const sp<IStatsCompanionService>)> mCancelAlarm;
+    std::function<void(const shared_ptr<IStatsCompanionService>)> mCancelAlarm;
 
 };
 
diff --git a/cmds/statsd/src/anomaly/AlarmTracker.h b/cmds/statsd/src/anomaly/AlarmTracker.h
index 2fb3e3b..2da4a18 100644
--- a/cmds/statsd/src/anomaly/AlarmTracker.h
+++ b/cmds/statsd/src/anomaly/AlarmTracker.h
@@ -22,12 +22,9 @@
 #include "config/ConfigKey.h"
 #include "frameworks/base/cmds/statsd/src/statsd_config.pb.h"  // Alarm
 
-#include <android/os/IStatsCompanionService.h>
 #include <stdlib.h>
 #include <utils/RefBase.h>
 
-using android::os::IStatsCompanionService;
-
 namespace android {
 namespace os {
 namespace statsd {
diff --git a/cmds/statsd/src/config/ConfigManager.cpp b/cmds/statsd/src/config/ConfigManager.cpp
index 986955b..9bdb588 100644
--- a/cmds/statsd/src/config/ConfigManager.cpp
+++ b/cmds/statsd/src/config/ConfigManager.cpp
@@ -42,42 +42,67 @@
 using android::base::StringPrintf;
 using std::unique_ptr;
 
-class ConfigReceiverDeathRecipient : public android::IBinder::DeathRecipient {
-    public:
-        ConfigReceiverDeathRecipient(sp<ConfigManager> configManager, const ConfigKey& configKey):
-            mConfigManager(configManager),
-            mConfigKey(configKey) {}
-        ~ConfigReceiverDeathRecipient() override = default;
-    private:
-        sp<ConfigManager> mConfigManager;
-        ConfigKey mConfigKey;
+struct ConfigReceiverDeathCookie {
+    ConfigReceiverDeathCookie(sp<ConfigManager> configManager, const ConfigKey& configKey,
+                              const shared_ptr<IPendingIntentRef>& pir):
+        mConfigManager(configManager),
+        mConfigKey(configKey),
+        mPir(pir) {}
 
-    void binderDied(const android::wp<android::IBinder>& who) override {
-        if (IInterface::asBinder(mConfigManager->GetConfigReceiver(mConfigKey)) == who.promote()) {
-             mConfigManager->RemoveConfigReceiver(mConfigKey);
-        }
-    }
+    sp<ConfigManager> mConfigManager;
+    ConfigKey mConfigKey;
+    shared_ptr<IPendingIntentRef> mPir;
 };
 
-class ActiveConfigChangedReceiverDeathRecipient : public android::IBinder::DeathRecipient {
-    public:
-        ActiveConfigChangedReceiverDeathRecipient(sp<ConfigManager> configManager, const int uid):
-            mConfigManager(configManager),
-            mUid(uid) {}
-        ~ActiveConfigChangedReceiverDeathRecipient() override = default;
-    private:
-        sp<ConfigManager> mConfigManager;
-        int mUid;
+static void configReceiverDied(void* cookie) {
+    auto cookie_ = static_cast<ConfigReceiverDeathCookie*>(cookie);
+    sp<ConfigManager> configManager = cookie_->mConfigManager;
+    ConfigKey configKey = cookie_->mConfigKey;
+    shared_ptr<IPendingIntentRef> pir = cookie_->mPir;
 
-    void binderDied(const android::wp<android::IBinder>& who) override {
-        if (IInterface::asBinder(mConfigManager->GetActiveConfigsChangedReceiver(mUid))
-              == who.promote()) {
-            mConfigManager->RemoveActiveConfigsChangedReceiver(mUid);
-        }
+    // TODO(b/149254662): Fix threading. This currently fails if a new
+    // pir gets set between the get and the remove.
+    if (configManager->GetConfigReceiver(configKey) == pir) {
+        configManager->RemoveConfigReceiver(configKey);
     }
+    // The death recipient corresponding to this specific pir can never
+    // be triggered again, so free up resources.
+    // TODO(b/149254662): Investigate other options to manage the memory.
+    delete cookie_;
+}
+
+struct ActiveConfigChangedReceiverDeathCookie {
+    ActiveConfigChangedReceiverDeathCookie(sp<ConfigManager> configManager, const int uid,
+                                           const shared_ptr<IPendingIntentRef>& pir):
+        mConfigManager(configManager),
+        mUid(uid),
+        mPir(pir) {}
+
+    sp<ConfigManager> mConfigManager;
+    int mUid;
+    shared_ptr<IPendingIntentRef> mPir;
 };
 
-ConfigManager::ConfigManager() {
+static void activeConfigChangedReceiverDied(void* cookie) {
+    auto cookie_ = static_cast<ActiveConfigChangedReceiverDeathCookie*>(cookie);
+    sp<ConfigManager> configManager = cookie_->mConfigManager;
+    int uid = cookie_->mUid;
+    shared_ptr<IPendingIntentRef> pir = cookie_->mPir;
+
+    // TODO(b/149254662): Fix threading. This currently fails if a new
+    // pir gets set between the get and the remove.
+    if (configManager->GetActiveConfigsChangedReceiver(uid) == pir) {
+        configManager->RemoveActiveConfigsChangedReceiver(uid);
+    }
+    // The death recipient corresponding to this specific pir can never
+    // be triggered again, so free up resources.
+    delete cookie_;
+}
+
+ConfigManager::ConfigManager():
+    mConfigReceiverDeathRecipient(AIBinder_DeathRecipient_new(configReceiverDied)),
+    mActiveConfigChangedReceiverDeathRecipient(
+        AIBinder_DeathRecipient_new(activeConfigChangedReceiverDied)) {
 }
 
 ConfigManager::~ConfigManager() {
@@ -150,10 +175,11 @@
 }
 
 void ConfigManager::SetConfigReceiver(const ConfigKey& key,
-                                      const sp<IPendingIntentRef>& pir) {
+                                      const shared_ptr<IPendingIntentRef>& pir) {
     lock_guard<mutex> lock(mMutex);
     mConfigReceivers[key] = pir;
-    IInterface::asBinder(pir)->linkToDeath(new ConfigReceiverDeathRecipient(this, key));
+    AIBinder_linkToDeath(pir->asBinder().get(), mConfigReceiverDeathRecipient.get(),
+                         new ConfigReceiverDeathCookie(this, key, pir));
 }
 
 void ConfigManager::RemoveConfigReceiver(const ConfigKey& key) {
@@ -162,11 +188,11 @@
 }
 
 void ConfigManager::SetActiveConfigsChangedReceiver(const int uid,
-                                                    const sp<IPendingIntentRef>& pir) {
+                                                    const shared_ptr<IPendingIntentRef>& pir) {
     lock_guard<mutex> lock(mMutex);
     mActiveConfigsChangedReceivers[uid] = pir;
-    IInterface::asBinder(pir)->linkToDeath(
-        new ActiveConfigChangedReceiverDeathRecipient(this, uid));
+    AIBinder_linkToDeath(pir->asBinder().get(), mActiveConfigChangedReceiverDeathRecipient.get(),
+                         new ActiveConfigChangedReceiverDeathCookie(this, uid, pir));
 }
 
 void ConfigManager::RemoveActiveConfigsChangedReceiver(const int uid) {
@@ -279,7 +305,7 @@
     return ret;
 }
 
-const sp<IPendingIntentRef> ConfigManager::GetConfigReceiver(const ConfigKey& key) const {
+const shared_ptr<IPendingIntentRef> ConfigManager::GetConfigReceiver(const ConfigKey& key) const {
     lock_guard<mutex> lock(mMutex);
 
     auto it = mConfigReceivers.find(key);
@@ -290,7 +316,8 @@
     }
 }
 
-const sp<IPendingIntentRef> ConfigManager::GetActiveConfigsChangedReceiver(const int uid) const {
+const shared_ptr<IPendingIntentRef> ConfigManager::GetActiveConfigsChangedReceiver(const int uid)
+        const {
     lock_guard<mutex> lock(mMutex);
 
     auto it = mActiveConfigsChangedReceivers.find(uid);
diff --git a/cmds/statsd/src/config/ConfigManager.h b/cmds/statsd/src/config/ConfigManager.h
index 2095173..824e588 100644
--- a/cmds/statsd/src/config/ConfigManager.h
+++ b/cmds/statsd/src/config/ConfigManager.h
@@ -19,12 +19,15 @@
 #include "config/ConfigKey.h"
 #include "config/ConfigListener.h"
 
-#include <android/os/IPendingIntentRef.h>
+#include <aidl/android/os/IPendingIntentRef.h>
 #include <mutex>
 #include <string>
 
 #include <stdio.h>
 
+using aidl::android::os::IPendingIntentRef;
+using std::shared_ptr;
+
 namespace android {
 namespace os {
 namespace statsd {
@@ -62,12 +65,12 @@
     /**
      * Sets the broadcast receiver for a configuration key.
      */
-    void SetConfigReceiver(const ConfigKey& key, const sp<IPendingIntentRef>& pir);
+    void SetConfigReceiver(const ConfigKey& key, const shared_ptr<IPendingIntentRef>& pir);
 
     /**
      * Returns the package name and class name representing the broadcast receiver for this config.
      */
-    const sp<IPendingIntentRef> GetConfigReceiver(const ConfigKey& key) const;
+    const shared_ptr<IPendingIntentRef> GetConfigReceiver(const ConfigKey& key) const;
 
     /**
      * Returns all config keys registered.
@@ -83,13 +86,13 @@
      * Sets the broadcast receiver that is notified whenever the list of active configs
      * changes for this uid.
      */
-    void SetActiveConfigsChangedReceiver(const int uid, const sp<IPendingIntentRef>& pir);
+    void SetActiveConfigsChangedReceiver(const int uid, const shared_ptr<IPendingIntentRef>& pir);
 
     /**
      * Returns the broadcast receiver for active configs changed for this uid.
      */
 
-    const sp<IPendingIntentRef> GetActiveConfigsChangedReceiver(const int uid) const;
+    const shared_ptr<IPendingIntentRef> GetActiveConfigsChangedReceiver(const int uid) const;
 
     /**
      * Erase any active configs changed broadcast receiver associated with this uid.
@@ -141,18 +144,23 @@
     /**
      * Each config key can be subscribed by up to one receiver, specified as IPendingIntentRef.
      */
-    std::map<ConfigKey, sp<IPendingIntentRef>> mConfigReceivers;
+    std::map<ConfigKey, shared_ptr<IPendingIntentRef>> mConfigReceivers;
 
     /**
      * Each uid can be subscribed by up to one receiver to notify that the list of active configs
      * for this uid has changed. The receiver is specified as IPendingIntentRef.
      */
-     std::map<int, sp<IPendingIntentRef>> mActiveConfigsChangedReceivers;
+     std::map<int, shared_ptr<IPendingIntentRef>> mActiveConfigsChangedReceivers;
 
     /**
      * The ConfigListeners that will be told about changes.
      */
     std::vector<sp<ConfigListener>> mListeners;
+
+    // Death recipients that are triggered when the host process holding an
+    // IPendingIntentRef dies.
+    ::ndk::ScopedAIBinder_DeathRecipient mConfigReceiverDeathRecipient;
+    ::ndk::ScopedAIBinder_DeathRecipient mActiveConfigChangedReceiverDeathRecipient;
 };
 
 }  // namespace statsd
diff --git a/cmds/statsd/src/external/PullResultReceiver.cpp b/cmds/statsd/src/external/PullResultReceiver.cpp
index 6b6fe7d..8aa4792 100644
--- a/cmds/statsd/src/external/PullResultReceiver.cpp
+++ b/cmds/statsd/src/external/PullResultReceiver.cpp
@@ -16,17 +16,12 @@
 
 #include "PullResultReceiver.h"
 
-using namespace android::binder;
-using namespace android::util;
-using namespace std;
-
 namespace android {
 namespace os {
 namespace statsd {
 
 PullResultReceiver::PullResultReceiver(
-        std::function<void(int32_t, bool, const vector<android::util::StatsEventParcel>&)>
-             pullFinishCb)
+        std::function<void(int32_t, bool, const vector<StatsEventParcel>&)> pullFinishCb)
     : pullFinishCallback(std::move(pullFinishCb)) {
 }
 
diff --git a/cmds/statsd/src/external/PullResultReceiver.h b/cmds/statsd/src/external/PullResultReceiver.h
index 17d06e4..ceaae80 100644
--- a/cmds/statsd/src/external/PullResultReceiver.h
+++ b/cmds/statsd/src/external/PullResultReceiver.h
@@ -14,29 +14,33 @@
  * limitations under the License.
  */
 
-#include <android/os/BnPullAtomResultReceiver.h>
+#include <aidl/android/os/BnPullAtomResultReceiver.h>
+#include <aidl/android/util/StatsEventParcel.h>
 
 using namespace std;
 
+using Status = ::ndk::ScopedAStatus;
+using aidl::android::os::BnPullAtomResultReceiver;
+using aidl::android::util::StatsEventParcel;
+
 namespace android {
 namespace os {
 namespace statsd {
 
 class PullResultReceiver : public BnPullAtomResultReceiver {
 public:
-    PullResultReceiver(function<void(int32_t, bool, const vector<android::util::StatsEventParcel>&)>
+    PullResultReceiver(function<void(int32_t, bool, const vector<StatsEventParcel>&)>
                                pullFinishCallback);
     ~PullResultReceiver();
 
     /**
      * Binder call for finishing a pull.
      */
-    binder::Status pullFinished(int32_t atomTag, bool success,
-                                const vector<android::util::StatsEventParcel>& output) override;
+    Status pullFinished(int32_t atomTag, bool success,
+                        const vector<StatsEventParcel>& output) override;
 
 private:
-    function<void(int32_t, bool, const vector<android::util::StatsEventParcel>&)>
-            pullFinishCallback;
+    function<void(int32_t, bool, const vector<StatsEventParcel>&)> pullFinishCallback;
 };
 
 }  // namespace statsd
diff --git a/cmds/statsd/src/external/StatsCallbackPuller.cpp b/cmds/statsd/src/external/StatsCallbackPuller.cpp
index 1a11f0e..b6c8e34 100644
--- a/cmds/statsd/src/external/StatsCallbackPuller.cpp
+++ b/cmds/statsd/src/external/StatsCallbackPuller.cpp
@@ -18,24 +18,24 @@
 #include "Log.h"
 
 #include "StatsCallbackPuller.h"
-
-#include <android/os/IPullAtomCallback.h>
-#include <android/util/StatsEventParcel.h>
-
 #include "PullResultReceiver.h"
 #include "StatsPullerManager.h"
 #include "logd/LogEvent.h"
 #include "stats_log_util.h"
 
-using namespace android::binder;
-using namespace android::util;
+#include <aidl/android/util/StatsEventParcel.h>
+
 using namespace std;
 
+using Status = ::ndk::ScopedAStatus;
+using aidl::android::util::StatsEventParcel;
+using ::ndk::SharedRefBase;
+
 namespace android {
 namespace os {
 namespace statsd {
 
-StatsCallbackPuller::StatsCallbackPuller(int tagId, const sp<IPullAtomCallback>& callback,
+StatsCallbackPuller::StatsCallbackPuller(int tagId, const shared_ptr<IPullAtomCallback>& callback,
                                          const int64_t coolDownNs, int64_t timeoutNs,
                                          const vector<int> additiveFields)
     : StatsPuller(tagId, coolDownNs, timeoutNs, additiveFields), mCallback(callback) {
@@ -57,7 +57,7 @@
     shared_ptr<vector<shared_ptr<LogEvent>>> sharedData =
             make_shared<vector<shared_ptr<LogEvent>>>();
 
-    sp<PullResultReceiver> resultReceiver = new PullResultReceiver(
+    shared_ptr<PullResultReceiver> resultReceiver = SharedRefBase::make<PullResultReceiver>(
             [cv_mutex, cv, pullFinish, pullSuccess, sharedData](
                     int32_t atomTag, bool success, const vector<StatsEventParcel>& output) {
                 // This is the result of the pull, executing in a statsd binder thread.
@@ -66,9 +66,11 @@
                 {
                     lock_guard<mutex> lk(*cv_mutex);
                     for (const StatsEventParcel& parcel: output) {
+                        uint8_t* buf = reinterpret_cast<uint8_t*>(
+                                const_cast<int8_t*>(parcel.buffer.data()));
                         shared_ptr<LogEvent> event = make_shared<LogEvent>(
-                                const_cast<uint8_t*>(parcel.buffer.data()), parcel.buffer.size(),
-                                /*uid=*/-1, /*pid=*/-1, /*useNewSchema=*/true);
+                                buf, parcel.buffer.size(), /*uid=*/-1, /*pid=*/-1,
+                                /*useNewSchema=*/true);
                         sharedData->push_back(event);
                     }
                     *pullSuccess = success;
diff --git a/cmds/statsd/src/external/StatsCallbackPuller.h b/cmds/statsd/src/external/StatsCallbackPuller.h
index fe6af19..e82e8bb 100644
--- a/cmds/statsd/src/external/StatsCallbackPuller.h
+++ b/cmds/statsd/src/external/StatsCallbackPuller.h
@@ -16,23 +16,25 @@
 
 #pragma once
 
-#include <android/os/IPullAtomCallback.h>
-
+#include <aidl/android/os/IPullAtomCallback.h>
 #include "StatsPuller.h"
 
+using aidl::android::os::IPullAtomCallback;
+using std::shared_ptr;
+
 namespace android {
 namespace os {
 namespace statsd {
 
 class StatsCallbackPuller : public StatsPuller {
 public:
-    explicit StatsCallbackPuller(int tagId, const sp<IPullAtomCallback>& callback,
+    explicit StatsCallbackPuller(int tagId, const shared_ptr<IPullAtomCallback>& callback,
                                  const int64_t coolDownNs, const int64_t timeoutNs,
                                  const std::vector<int> additiveFields);
 
 private:
-    bool PullInternal(vector<std::shared_ptr<LogEvent> >* data) override;
-    const sp<IPullAtomCallback> mCallback;
+    bool PullInternal(vector<std::shared_ptr<LogEvent>>* data) override;
+    const shared_ptr<IPullAtomCallback> mCallback;
 
     FRIEND_TEST(StatsCallbackPullerTest, PullFail);
     FRIEND_TEST(StatsCallbackPullerTest, PullSuccess);
diff --git a/cmds/statsd/src/external/StatsPuller.h b/cmds/statsd/src/external/StatsPuller.h
index 9c21695..fee571c 100644
--- a/cmds/statsd/src/external/StatsPuller.h
+++ b/cmds/statsd/src/external/StatsPuller.h
@@ -16,7 +16,7 @@
 
 #pragma once
 
-#include <android/os/IStatsCompanionService.h>
+#include <aidl/android/os/IStatsCompanionService.h>
 #include <utils/RefBase.h>
 #include <mutex>
 #include <vector>
@@ -26,6 +26,9 @@
 #include "logd/LogEvent.h"
 #include "puller_util.h"
 
+using aidl::android::os::IStatsCompanionService;
+using std::shared_ptr;
+
 namespace android {
 namespace os {
 namespace statsd {
@@ -58,7 +61,8 @@
 
     static void SetUidMap(const sp<UidMap>& uidMap);
 
-    virtual void SetStatsCompanionService(sp<IStatsCompanionService> statsCompanionService){};
+    virtual void SetStatsCompanionService(
+            shared_ptr<IStatsCompanionService> statsCompanionService) {};
 
 protected:
     const int mTagId;
diff --git a/cmds/statsd/src/external/StatsPullerManager.cpp b/cmds/statsd/src/external/StatsPullerManager.cpp
index 3ceff75..1c38542 100644
--- a/cmds/statsd/src/external/StatsPullerManager.cpp
+++ b/cmds/statsd/src/external/StatsPullerManager.cpp
@@ -19,8 +19,6 @@
 
 #include "StatsPullerManager.h"
 
-#include <android/os/IPullAtomCallback.h>
-#include <android/os/IStatsCompanionService.h>
 #include <cutils/log.h>
 #include <math.h>
 #include <stdint.h>
@@ -87,7 +85,10 @@
         return;
     }
 
-    sp<IStatsCompanionService> statsCompanionServiceCopy = mStatsCompanionService;
+    // TODO(b/149254662): Why are we creating a copy here? This is different
+    // from the other places where we create a copy because we don't reassign
+    // mStatsCompanionService so a destructor can't implicitly be called...
+    shared_ptr<IStatsCompanionService> statsCompanionServiceCopy = mStatsCompanionService;
     if (statsCompanionServiceCopy != nullptr) {
         statsCompanionServiceCopy->setPullingAlarm(mNextPullTimeNs / 1000000);
     } else {
@@ -97,9 +98,11 @@
 }
 
 void StatsPullerManager::SetStatsCompanionService(
-        sp<IStatsCompanionService> statsCompanionService) {
+        shared_ptr<IStatsCompanionService> statsCompanionService) {
+    // TODO(b/149254662): Why are we using AutoMutex instead of lock_guard?
+    // Additionally, do we need the temporary shared_ptr to prevent deadlocks?
     AutoMutex _l(mLock);
-    sp<IStatsCompanionService> tmpForLock = mStatsCompanionService;
+    shared_ptr<IStatsCompanionService> tmpForLock = mStatsCompanionService;
     mStatsCompanionService = statsCompanionService;
     for (const auto& pulledAtom : kAllPullAtomInfo) {
         pulledAtom.second->SetStatsCompanionService(statsCompanionService);
@@ -250,10 +253,11 @@
 void StatsPullerManager::RegisterPullAtomCallback(const int uid, const int32_t atomTag,
                                                   const int64_t coolDownNs, const int64_t timeoutNs,
                                                   const vector<int32_t>& additiveFields,
-                                                  const sp<IPullAtomCallback>& callback) {
+                                                  const shared_ptr<IPullAtomCallback>& callback) {
     AutoMutex _l(mLock);
     VLOG("RegisterPullerCallback: adding puller for tag %d", atomTag);
-    // TODO: linkToDeath with the callback so that we can remove it and delete the puller.
+    // TODO(b/146439412): linkToDeath with the callback so that we can remove it
+    // and delete the puller.
     StatsdStats::getInstance().notePullerCallbackRegistrationChanged(atomTag, /*registered=*/true);
     kAllPullAtomInfo[{.atomTag = atomTag}] =
             new StatsCallbackPuller(atomTag, callback, coolDownNs, timeoutNs, additiveFields);
diff --git a/cmds/statsd/src/external/StatsPullerManager.h b/cmds/statsd/src/external/StatsPullerManager.h
index aef16dc..e067766 100644
--- a/cmds/statsd/src/external/StatsPullerManager.h
+++ b/cmds/statsd/src/external/StatsPullerManager.h
@@ -16,9 +16,8 @@
 
 #pragma once
 
-#include <android/os/IPullAtomCallback.h>
-#include <android/os/IStatsCompanionService.h>
-#include <binder/IServiceManager.h>
+#include <aidl/android/os/IPullAtomCallback.h>
+#include <aidl/android/os/IStatsCompanionService.h>
 #include <utils/RefBase.h>
 #include <utils/threads.h>
 
@@ -30,11 +29,14 @@
 #include "guardrail/StatsdStats.h"
 #include "logd/LogEvent.h"
 
+using aidl::android::os::IPullAtomCallback;
+using aidl::android::os::IStatsCompanionService;
+using std::shared_ptr;
+
 namespace android {
 namespace os {
 namespace statsd {
 
-
 typedef struct PullerKey {
     // The uid of the process that registers this puller.
     const int uid = -1;
@@ -93,18 +95,18 @@
     // Clear pull data cache if it is beyond respective cool down time.
     int ClearPullerCacheIfNecessary(int64_t timestampNs);
 
-    void SetStatsCompanionService(sp<IStatsCompanionService> statsCompanionService);
+    void SetStatsCompanionService(shared_ptr<IStatsCompanionService> statsCompanionService);
 
     void RegisterPullAtomCallback(const int uid, const int32_t atomTag, const int64_t coolDownNs,
                                   const int64_t timeoutNs, const vector<int32_t>& additiveFields,
-                                  const sp<IPullAtomCallback>& callback);
+                                  const shared_ptr<IPullAtomCallback>& callback);
 
     void UnregisterPullAtomCallback(const int uid, const int32_t atomTag);
 
     std::map<const PullerKey, sp<StatsPuller>> kAllPullAtomInfo;
 
 private:
-    sp<IStatsCompanionService> mStatsCompanionService = nullptr;
+    shared_ptr<IStatsCompanionService> mStatsCompanionService = nullptr;
 
     typedef struct {
         int64_t nextPullTimeNs;
diff --git a/cmds/statsd/src/logd/LogEvent.cpp b/cmds/statsd/src/logd/LogEvent.cpp
index 1ef1ab2..9c50846 100644
--- a/cmds/statsd/src/logd/LogEvent.cpp
+++ b/cmds/statsd/src/logd/LogEvent.cpp
@@ -20,7 +20,8 @@
 #include "stats_log_util.h"
 #include "statslog.h"
 
-#include <binder/IPCThreadState.h>
+#include <android/binder_ibinder.h>
+#include <android-base/stringprintf.h>
 #include <private/android_filesystem_config.h>
 
 namespace android {
@@ -31,6 +32,7 @@
 const int FIELD_ID_EXPERIMENT_ID = 1;
 
 using namespace android::util;
+using android::base::StringPrintf;
 using android::util::ProtoOutputStream;
 using std::string;
 using std::vector;
@@ -180,8 +182,8 @@
     mLogdTimestampNs = getWallClockNs();
     mElapsedTimestampNs = getElapsedRealtimeNs();
     mTagId = android::util::BINARY_PUSH_STATE_CHANGED;
-    mLogUid = android::IPCThreadState::self()->getCallingUid();
-    mLogPid = android::IPCThreadState::self()->getCallingPid();
+    mLogUid = AIBinder_getCallingUid();
+    mLogPid = AIBinder_getCallingPid();
 
     mValues.push_back(FieldValue(Field(mTagId, getSimpleField(1)), Value(trainName)));
     mValues.push_back(FieldValue(Field(mTagId, getSimpleField(2)), Value(trainVersionCode)));
diff --git a/cmds/statsd/src/main.cpp b/cmds/statsd/src/main.cpp
index 140ef4e..d79b6a2 100644
--- a/cmds/statsd/src/main.cpp
+++ b/cmds/statsd/src/main.cpp
@@ -20,9 +20,9 @@
 #include "StatsService.h"
 #include "socket/StatsSocketListener.h"
 
-#include <binder/IPCThreadState.h>
-#include <binder/IServiceManager.h>
-#include <binder/ProcessState.h>
+#include <android/binder_interface_utils.h>
+#include <android/binder_process.h>
+#include <android/binder_manager.h>
 #include <utils/Looper.h>
 
 #include <stdio.h>
@@ -32,16 +32,11 @@
 
 using namespace android;
 using namespace android::os::statsd;
+using ::ndk::SharedRefBase;
+using std::shared_ptr;
+using std::make_shared;
 
-/**
- * Thread function data.
- */
-struct log_reader_thread_data {
-    sp<StatsService> service;
-};
-
-
-sp<StatsService> gStatsService = nullptr;
+shared_ptr<StatsService> gStatsService = nullptr;
 
 void sigHandler(int sig) {
     if (gStatsService != nullptr) {
@@ -68,20 +63,18 @@
     sp<Looper> looper(Looper::prepare(0 /* opts */));
 
     // Set up the binder
-    sp<ProcessState> ps(ProcessState::self());
-    ps->setThreadPoolMaxThreadCount(9);
-    ps->startThreadPool();
-    ps->giveThreadPoolName();
-    IPCThreadState::self()->disableBackgroundScheduling(true);
+    ABinderProcess_setThreadPoolMaxThreadCount(9);
+    ABinderProcess_startThreadPool();
 
     std::shared_ptr<LogEventQueue> eventQueue =
             std::make_shared<LogEventQueue>(2000 /*buffer limit. Buffer is NOT pre-allocated*/);
 
     // Create the service
-    gStatsService = new StatsService(looper, eventQueue);
-    if (defaultServiceManager()->addService(String16("stats"), gStatsService, false,
-                IServiceManager::DUMP_FLAG_PRIORITY_NORMAL | IServiceManager::DUMP_FLAG_PROTO)
-            != 0) {
+    gStatsService = SharedRefBase::make<StatsService>(looper, eventQueue);
+    // TODO(b/149582373): Set DUMP_FLAG_PROTO once libbinder_ndk supports
+    // setting dumpsys priorities.
+    binder_status_t status = AServiceManager_addService(gStatsService->asBinder().get(), "stats");
+    if (status != STATUS_OK) {
         ALOGE("Failed to add service as AIDL service");
         return -1;
     }
diff --git a/cmds/statsd/src/packages/UidMap.h b/cmds/statsd/src/packages/UidMap.h
index bfac6e3..4e3c506 100644
--- a/cmds/statsd/src/packages/UidMap.h
+++ b/cmds/statsd/src/packages/UidMap.h
@@ -21,10 +21,11 @@
 #include "packages/PackageInfoListener.h"
 #include "stats_util.h"
 
-#include <binder/IShellCallback.h>
 #include <gtest/gtest_prod.h>
 #include <stdio.h>
 #include <utils/RefBase.h>
+#include <utils/String16.h>
+
 #include <list>
 #include <mutex>
 #include <set>
diff --git a/cmds/statsd/src/shell/ShellSubscriber.cpp b/cmds/statsd/src/shell/ShellSubscriber.cpp
index d6a0433..a861a3b 100644
--- a/cmds/statsd/src/shell/ShellSubscriber.cpp
+++ b/cmds/statsd/src/shell/ShellSubscriber.cpp
@@ -29,49 +29,88 @@
 
 const static int FIELD_ID_ATOM = 1;
 
-void ShellSubscriber::startNewSubscription(int in, int out, sp<IResultReceiver> resultReceiver,
-                                           int timeoutSec) {
+void ShellSubscriber::startNewSubscription(int in, int out, int timeoutSec) {
     VLOG("start new shell subscription");
+    int64_t subscriberId = getElapsedRealtimeNs();
+
     {
         std::lock_guard<std::mutex> lock(mMutex);
-        if (mResultReceiver != nullptr) {
+        if (mSubscriberId> 0) {
             VLOG("Only one shell subscriber is allowed.");
             return;
         }
+        mSubscriberId = subscriberId;
         mInput = in;
         mOutput = out;
-        mResultReceiver = resultReceiver;
-        IInterface::asBinder(mResultReceiver)->linkToDeath(this);
     }
 
-    // Note that the following is blocking, and it's intended as we cannot return until the shell
-    // cmd exits, otherwise all resources & FDs will be automatically closed.
+    bool success = readConfig();
+    if (!success) {
+        std::lock_guard<std::mutex> lock(mMutex);
+        cleanUpLocked();
+    }
 
-    // Read config forever until EOF is reached. Clients may send multiple configs -- each new
-    // config replace the previous one.
-    readConfig(in);
-    VLOG("timeout : %d", timeoutSec);
-
-    // Now we have read an EOF we now wait for the semaphore until the client exits.
-    VLOG("Now wait for client to exit");
+    VLOG("Wait for client to exit or timeout (%d sec)", timeoutSec);
     std::unique_lock<std::mutex> lk(mMutex);
 
+    // Note that the following is blocking, and it's intended as we cannot return until the shell
+    // cmd exits or we time out.
     if (timeoutSec > 0) {
         mShellDied.wait_for(lk, timeoutSec * 1s,
-                            [this, resultReceiver] { return mResultReceiver != resultReceiver; });
+                            [this, subscriberId] { return mSubscriberId != subscriberId; });
     } else {
-        mShellDied.wait(lk, [this, resultReceiver] { return mResultReceiver != resultReceiver; });
+        mShellDied.wait(lk, [this, subscriberId] { return mSubscriberId != subscriberId; });
+    }
+}
+
+
+// Read configs until EOF is reached. There may be multiple configs in the input
+// -- each new config should replace the previous one.
+//
+// Returns a boolean indicating whether the input was read successfully.
+bool ShellSubscriber::readConfig() {
+    if (mInput < 0) {
+        return false;
+    }
+
+    while (true) {
+        // Read the size of the config.
+        size_t bufferSize = 0;
+        ssize_t bytesRead = read(mInput, &bufferSize, sizeof(bufferSize));
+        if (bytesRead == 0) {
+            VLOG("We have reached the end of the input.");
+            return true;
+        } else if (bytesRead < 0 || (size_t)bytesRead != sizeof(bufferSize)) {
+            ALOGE("Error reading config size");
+            return false;
+        }
+
+        // Read and parse the config.
+        vector<uint8_t> buffer(bufferSize);
+        bytesRead = read(mInput, buffer.data(), bufferSize);
+        if (bytesRead > 0 && (size_t)bytesRead == bufferSize) {
+            ShellSubscription config;
+            if (config.ParseFromArray(buffer.data(), bufferSize)) {
+                updateConfig(config);
+            } else {
+                ALOGE("Error parsing the config");
+                return false;
+            }
+        } else {
+            VLOG("Error reading the config, expected bytes: %zu, actual bytes: %zu", bufferSize, 
+                 bytesRead);
+            return false;
+        }
     }
 }
 
 void ShellSubscriber::updateConfig(const ShellSubscription& config) {
-    std::lock_guard<std::mutex> lock(mMutex);
     mPushedMatchers.clear();
     mPulledInfo.clear();
 
     for (const auto& pushed : config.pushed()) {
         mPushedMatchers.push_back(pushed);
-        VLOG("adding matcher for atom %d", pushed.atom_id());
+        VLOG("adding matcher for pushed atom %d", pushed.atom_id());
     }
 
     int64_t token = getElapsedRealtimeNs();
@@ -89,46 +128,20 @@
     }
 
     if (mPulledInfo.size() > 0 && minInterval > 0) {
-        // This thread is guaranteed to terminate after it detects the token is different or
-        // cleaned up.
+        // This thread is guaranteed to terminate after it detects the token is
+        // different.
         std::thread puller([token, minInterval, this] { startPull(token, minInterval); });
         puller.detach();
     }
 }
 
-void ShellSubscriber::writeToOutputLocked(const vector<std::shared_ptr<LogEvent>>& data,
-                                          const SimpleAtomMatcher& matcher) {
-    if (mOutput == 0) return;
-    int count = 0;
-    mProto.clear();
-    for (const auto& event : data) {
-        VLOG("%s", event->ToString().c_str());
-        if (matchesSimple(*mUidMap, matcher, *event)) {
-            VLOG("matched");
-            count++;
-            uint64_t atomToken = mProto.start(util::FIELD_TYPE_MESSAGE |
-                                              util::FIELD_COUNT_REPEATED | FIELD_ID_ATOM);
-            event->ToProto(mProto);
-            mProto.end(atomToken);
-        }
-    }
-
-    if (count > 0) {
-        // First write the payload size.
-        size_t bufferSize = mProto.size();
-        write(mOutput, &bufferSize, sizeof(bufferSize));
-        VLOG("%d atoms, proto size: %zu", count, bufferSize);
-        // Then write the payload.
-        mProto.flush(mOutput);
-    }
-    mProto.clear();
-}
-
 void ShellSubscriber::startPull(int64_t token, int64_t intervalMillis) {
-    while (1) {
+    while (true) {
         int64_t nowMillis = getElapsedRealtimeMillis();
         {
             std::lock_guard<std::mutex> lock(mMutex);
+            // If the token has changed, the config has changed, so this
+            // puller can now stop.
             if (mPulledInfo.size() == 0 || mPullToken != token) {
                 VLOG("Pulling thread %lld done!", (long long)token);
                 return;
@@ -152,55 +165,47 @@
     }
 }
 
-void ShellSubscriber::readConfig(int in) {
-    if (in <= 0) {
+// Must be called with the lock acquired, so that mProto isn't being written to
+// at the same time by multiple threads.
+void ShellSubscriber::writeToOutputLocked(const vector<std::shared_ptr<LogEvent>>& data,
+                                          const SimpleAtomMatcher& matcher) {
+    if (mOutput < 0) {
         return;
     }
-
-    while (1) {
-        size_t bufferSize = 0;
-        int result = 0;
-        if ((result = read(in, &bufferSize, sizeof(bufferSize))) == 0) {
-            VLOG("Done reading");
-            break;
-        } else if (result < 0 || result != sizeof(bufferSize)) {
-            ALOGE("Error reading config size");
-            break;
-        }
-
-        vector<uint8_t> buffer(bufferSize);
-        if ((result = read(in, buffer.data(), bufferSize)) > 0 && ((size_t)result) == bufferSize) {
-            ShellSubscription config;
-            if (config.ParseFromArray(buffer.data(), bufferSize)) {
-                updateConfig(config);
-            } else {
-                ALOGE("error parsing the config");
-                break;
-            }
-        } else {
-            VLOG("Error reading the config, returned: %d, expecting %zu", result, bufferSize);
-            break;
+    int count = 0;
+    mProto.clear();
+    for (const auto& event : data) {
+        VLOG("%s", event->ToString().c_str());
+        if (matchesSimple(*mUidMap, matcher, *event)) {
+            VLOG("matched");
+            count++;
+            uint64_t atomToken = mProto.start(util::FIELD_TYPE_MESSAGE |
+                                              util::FIELD_COUNT_REPEATED | FIELD_ID_ATOM);
+            event->ToProto(mProto);
+            mProto.end(atomToken);
         }
     }
-}
 
-void ShellSubscriber::cleanUpLocked() {
-    // The file descriptors will be closed by binder.
-    mInput = 0;
-    mOutput = 0;
-    mResultReceiver = nullptr;
-    mPushedMatchers.clear();
-    mPulledInfo.clear();
-    mPullToken = 0;
-    VLOG("done clean up");
+    if (count > 0) {
+        // First write the payload size.
+        size_t bufferSize = mProto.size();
+        write(mOutput, &bufferSize, sizeof(bufferSize));
+
+        VLOG("%d atoms, proto size: %zu", count, bufferSize);
+        // Then write the payload.
+        mProto.flush(mOutput);
+    }
 }
 
 void ShellSubscriber::onLogEvent(const LogEvent& event) {
+    // Acquire a lock to prevent corruption from multiple threads writing to
+    // mProto.
     std::lock_guard<std::mutex> lock(mMutex);
-
-    if (mOutput <= 0) {
+    if (mOutput < 0) {
         return;
     }
+
+    mProto.clear();
     for (const auto& matcher : mPushedMatchers) {
         if (matchesSimple(*mUidMap, matcher, event)) {
             VLOG("%s", event.ToString().c_str());
@@ -208,25 +213,27 @@
                                               util::FIELD_COUNT_REPEATED | FIELD_ID_ATOM);
             event.ToProto(mProto);
             mProto.end(atomToken);
+
             // First write the payload size.
             size_t bufferSize = mProto.size();
             write(mOutput, &bufferSize, sizeof(bufferSize));
 
             // Then write the payload.
             mProto.flush(mOutput);
-            mProto.clear();
-            break;
         }
     }
 }
 
-void ShellSubscriber::binderDied(const wp<IBinder>& who) {
-    {
-        VLOG("Shell exits");
-        std::lock_guard<std::mutex> lock(mMutex);
-        cleanUpLocked();
-    }
-    mShellDied.notify_all();
+void ShellSubscriber::cleanUpLocked() {
+    // The file descriptors will be closed by binder.
+    mInput = -1;
+    mOutput = -1;
+    mSubscriberId = 0;
+    mPushedMatchers.clear();
+    mPulledInfo.clear();
+    // Setting mPullToken == 0 tells pull thread that its work is done.
+    mPullToken = 0;
+    VLOG("done clean up");
 }
 
 }  // namespace statsd
diff --git a/cmds/statsd/src/shell/ShellSubscriber.h b/cmds/statsd/src/shell/ShellSubscriber.h
index 86d8590..eaf2ad1 100644
--- a/cmds/statsd/src/shell/ShellSubscriber.h
+++ b/cmds/statsd/src/shell/ShellSubscriber.h
@@ -19,7 +19,6 @@
 #include "logd/LogEvent.h"
 
 #include <android/util/ProtoOutputStream.h>
-#include <binder/IResultReceiver.h>
 #include <condition_variable>
 #include <mutex>
 #include <thread>
@@ -56,7 +55,7 @@
  * Only one shell subscriber allowed at a time, because each shell subscriber blocks one thread
  * until it exits.
  */
-class ShellSubscriber : public virtual IBinder::DeathRecipient {
+class ShellSubscriber : public virtual RefBase {
 public:
     ShellSubscriber(sp<UidMap> uidMap, sp<StatsPullerManager> pullerMgr)
         : mUidMap(uidMap), mPullerMgr(pullerMgr){};
@@ -64,10 +63,7 @@
     /**
      * Start a new subscription.
      */
-    void startNewSubscription(int inFd, int outFd, sp<IResultReceiver> resultReceiver,
-                              int timeoutSec);
-
-    void binderDied(const wp<IBinder>& who);
+    void startNewSubscription(int inFd, int outFd, int timeoutSec);
 
     void onLogEvent(const LogEvent& event);
 
@@ -80,7 +76,7 @@
         int64_t mInterval;
         int64_t mPrevPullElapsedRealtimeMs;
     };
-    void readConfig(int in);
+    bool readConfig();
 
     void updateConfig(const ShellSubscription& config);
 
@@ -101,16 +97,16 @@
 
     std::condition_variable mShellDied;  // semaphore for waiting until shell exits.
 
-    int mInput;  // The input file descriptor
+    int mInput = -1;  // The input file descriptor
 
-    int mOutput;  // The output file descriptor
-
-    sp<IResultReceiver> mResultReceiver;
+    int mOutput = -1;  // The output file descriptor
 
     std::vector<SimpleAtomMatcher> mPushedMatchers;
 
     std::vector<PullInfo> mPulledInfo;
 
+    int64_t mSubscriberId = 0; // A unique id to identify a subscriber.
+
     int64_t mPullToken = 0;  // A unique token to identify a puller thread.
 };
 
diff --git a/cmds/statsd/src/stats_log_util.cpp b/cmds/statsd/src/stats_log_util.cpp
index 73f640e..3d02ffb 100644
--- a/cmds/statsd/src/stats_log_util.cpp
+++ b/cmds/statsd/src/stats_log_util.cpp
@@ -17,6 +17,7 @@
 #include "hash.h"
 #include "stats_log_util.h"
 
+#include <aidl/android/os/IStatsCompanionService.h>
 #include <private/android_filesystem_config.h>
 #include <set>
 #include <utils/SystemClock.h>
@@ -35,6 +36,10 @@
 using android::util::FIELD_TYPE_UINT64;
 using android::util::ProtoOutputStream;
 
+using aidl::android::os::IStatsCompanionService;
+using std::shared_ptr;
+using std::string;
+
 namespace android {
 namespace os {
 namespace statsd {
@@ -587,13 +592,13 @@
 }
 
 bool checkPermissionForIds(const char* permission, pid_t pid, uid_t uid) {
-    sp<IStatsCompanionService> scs = getStatsCompanionService();
+    shared_ptr<IStatsCompanionService> scs = getStatsCompanionService();
     if (scs == nullptr) {
         return false;
     }
 
     bool success;
-    binder::Status status = scs->checkPermission(String16(permission), pid, uid, &success);
+    ::ndk::ScopedAStatus status = scs->checkPermission(string(permission), pid, uid, &success);
     if (!status.isOk()) {
         return false;
     }
diff --git a/cmds/statsd/src/statscompanion_util.cpp b/cmds/statsd/src/statscompanion_util.cpp
index d338827..ce07ec0 100644
--- a/cmds/statsd/src/statscompanion_util.cpp
+++ b/cmds/statsd/src/statscompanion_util.cpp
@@ -18,26 +18,16 @@
 #include "Log.h"
 
 #include "statscompanion_util.h"
+#include <android/binder_auto_utils.h>
+#include <android/binder_manager.h>
 
 namespace android {
 namespace os {
 namespace statsd {
 
-sp <IStatsCompanionService> getStatsCompanionService() {
-    sp<IStatsCompanionService> statsCompanion = nullptr;
-    // Get statscompanion service from service manager
-    static const sp <IServiceManager> sm(defaultServiceManager());
-    if (statsCompanion == nullptr) {
-        if (sm != nullptr) {
-            const String16 name("statscompanion");
-            statsCompanion = interface_cast<IStatsCompanionService>(sm->checkService(name));
-            if (statsCompanion == nullptr) {
-                ALOGW("statscompanion service unavailable!");
-                return nullptr;
-            }
-        }
-    }
-    return statsCompanion;
+shared_ptr<IStatsCompanionService> getStatsCompanionService() {
+    ::ndk::SpAIBinder binder(AServiceManager_getService("statscompanion"));
+    return IStatsCompanionService::fromBinder(binder);
 }
 
 }  // namespace statsd
diff --git a/cmds/statsd/src/statscompanion_util.h b/cmds/statsd/src/statscompanion_util.h
index dc4f283..e20c40b 100644
--- a/cmds/statsd/src/statscompanion_util.h
+++ b/cmds/statsd/src/statscompanion_util.h
@@ -16,14 +16,17 @@
 
 #pragma once
 
-#include "StatsLogProcessor.h"
+#include <aidl/android/os/IStatsCompanionService.h>
+
+using aidl::android::os::IStatsCompanionService;
+using std::shared_ptr;
 
 namespace android {
 namespace os {
 namespace statsd {
 
 /** Fetches and returns the StatsCompanionService. */
-sp<IStatsCompanionService> getStatsCompanionService();
+shared_ptr<IStatsCompanionService> getStatsCompanionService();
 
 }  // namespace statsd
 }  // namespace os
diff --git a/cmds/statsd/src/subscriber/SubscriberReporter.cpp b/cmds/statsd/src/subscriber/SubscriberReporter.cpp
index 8fd6b46..93af5e9 100644
--- a/cmds/statsd/src/subscriber/SubscriberReporter.cpp
+++ b/cmds/statsd/src/subscriber/SubscriberReporter.cpp
@@ -27,38 +27,52 @@
 
 using std::vector;
 
-class BroadcastSubscriberDeathRecipient : public android::IBinder::DeathRecipient {
-    public:
-        BroadcastSubscriberDeathRecipient(const ConfigKey& configKey, int64_t subscriberId):
-            mConfigKey(configKey),
-            mSubscriberId(subscriberId) {}
-        ~BroadcastSubscriberDeathRecipient() override = default;
-    private:
-        ConfigKey mConfigKey;
-        int64_t mSubscriberId;
+struct BroadcastSubscriberDeathCookie {
+    BroadcastSubscriberDeathCookie(const ConfigKey& configKey, int64_t subscriberId,
+                                   const shared_ptr<IPendingIntentRef>& pir):
+        mConfigKey(configKey),
+        mSubscriberId(subscriberId),
+        mPir(pir) {}
 
-    void binderDied(const android::wp<android::IBinder>& who) override {
-        if (IInterface::asBinder(SubscriberReporter::getInstance().getBroadcastSubscriber(
-              mConfigKey, mSubscriberId)) == who.promote()) {
-            SubscriberReporter::getInstance().unsetBroadcastSubscriber(mConfigKey, mSubscriberId);
-        }
-    }
+    ConfigKey mConfigKey;
+    int64_t mSubscriberId;
+    shared_ptr<IPendingIntentRef> mPir;
 };
 
+static void broadcastSubscriberDied(void* cookie) {
+    BroadcastSubscriberDeathCookie* cookie_ = (BroadcastSubscriberDeathCookie*)cookie;
+    ConfigKey configKey = cookie_->mConfigKey;
+    int64_t subscriberId = cookie_->mSubscriberId;
+    shared_ptr<IPendingIntentRef> pir = cookie_->mPir;
+
+    // TODO(b/149254662): Fix threading. This currently fails if a new pir gets
+    // set between the get and the unset.
+    if (SubscriberReporter::getInstance().getBroadcastSubscriber(configKey, subscriberId) == pir) {
+        SubscriberReporter::getInstance().unsetBroadcastSubscriber(configKey, subscriberId);
+    }
+    // The death recipient corresponding to this specific pir can never be
+    // triggered again, so free up resources.
+    delete cookie_;
+}
+
+static ::ndk::ScopedAIBinder_DeathRecipient sBroadcastSubscriberDeathRecipient(
+        AIBinder_DeathRecipient_new(broadcastSubscriberDied));
+
 void SubscriberReporter::setBroadcastSubscriber(const ConfigKey& configKey,
                                                 int64_t subscriberId,
-                                                const sp<IPendingIntentRef>& pir) {
+                                                const shared_ptr<IPendingIntentRef>& pir) {
     VLOG("SubscriberReporter::setBroadcastSubscriber called.");
-    lock_guard<std::mutex> lock(mLock);
+    lock_guard<mutex> lock(mLock);
     mIntentMap[configKey][subscriberId] = pir;
-    IInterface::asBinder(pir)->linkToDeath(
-        new BroadcastSubscriberDeathRecipient(configKey, subscriberId));
+    // TODO(b/149254662): Is it ok to call linkToDeath while holding a lock?
+    AIBinder_linkToDeath(pir->asBinder().get(), sBroadcastSubscriberDeathRecipient.get(),
+                         new BroadcastSubscriberDeathCookie(configKey, subscriberId, pir));
 }
 
 void SubscriberReporter::unsetBroadcastSubscriber(const ConfigKey& configKey,
                                                   int64_t subscriberId) {
     VLOG("SubscriberReporter::unsetBroadcastSubscriber called.");
-    lock_guard<std::mutex> lock(mLock);
+    lock_guard<mutex> lock(mLock);
     auto subscriberMapIt = mIntentMap.find(configKey);
     if (subscriberMapIt != mIntentMap.end()) {
         subscriberMapIt->second.erase(subscriberId);
@@ -80,7 +94,7 @@
     //  config id - the name of this config (for this particular uid)
 
     VLOG("SubscriberReporter::alertBroadcastSubscriber called.");
-    lock_guard<std::mutex> lock(mLock);
+    lock_guard<mutex> lock(mLock);
 
     if (!subscription.has_broadcast_subscriber_details()
             || !subscription.broadcast_subscriber_details().has_subscriber_id()) {
@@ -89,10 +103,12 @@
     }
     int64_t subscriberId = subscription.broadcast_subscriber_details().subscriber_id();
 
-    vector<String16> cookies;
+    // TODO(b/149254662): Is there a way to convert a RepeatedPtrField into a
+    // vector without copying?
+    vector<string> cookies;
     cookies.reserve(subscription.broadcast_subscriber_details().cookie_size());
     for (auto& cookie : subscription.broadcast_subscriber_details().cookie()) {
-        cookies.push_back(String16(cookie.c_str()));
+        cookies.push_back(cookie);
     }
 
     auto it1 = mIntentMap.find(configKey);
@@ -109,10 +125,10 @@
     sendBroadcastLocked(it2->second, configKey, subscription, cookies, dimKey);
 }
 
-void SubscriberReporter::sendBroadcastLocked(const sp<IPendingIntentRef>& pir,
+void SubscriberReporter::sendBroadcastLocked(const shared_ptr<IPendingIntentRef>& pir,
                                              const ConfigKey& configKey,
                                              const Subscription& subscription,
-                                             const vector<String16>& cookies,
+                                             const vector<string>& cookies,
                                              const MetricDimensionKey& dimKey) const {
     VLOG("SubscriberReporter::sendBroadcastLocked called.");
     pir->sendSubscriberBroadcast(
@@ -124,9 +140,9 @@
             dimKey.getDimensionKeyInWhat().toStatsDimensionsValueParcel());
 }
 
-sp<IPendingIntentRef> SubscriberReporter::getBroadcastSubscriber(const ConfigKey& configKey,
-                                                                 int64_t subscriberId) {
-    lock_guard<std::mutex> lock(mLock);
+shared_ptr<IPendingIntentRef> SubscriberReporter::getBroadcastSubscriber(const ConfigKey& configKey,
+                                                                         int64_t subscriberId) {
+    lock_guard<mutex> lock(mLock);
     auto subscriberMapIt = mIntentMap.find(configKey);
     if (subscriberMapIt == mIntentMap.end()) {
         return nullptr;
diff --git a/cmds/statsd/src/subscriber/SubscriberReporter.h b/cmds/statsd/src/subscriber/SubscriberReporter.h
index 42599f5..0f97d39 100644
--- a/cmds/statsd/src/subscriber/SubscriberReporter.h
+++ b/cmds/statsd/src/subscriber/SubscriberReporter.h
@@ -16,9 +16,9 @@
 
 #pragma once
 
-#include <android/os/IPendingIntentRef.h>
-#include <android/os/IStatsCompanionService.h>
+#include <aidl/android/os/IPendingIntentRef.h>
 #include <utils/RefBase.h>
+#include <utils/String16.h>
 
 #include "config/ConfigKey.h"
 #include "frameworks/base/cmds/statsd/src/statsd_config.pb.h"  // subscription
@@ -28,6 +28,13 @@
 #include <unordered_map>
 #include <vector>
 
+using aidl::android::os::IPendingIntentRef;
+using std::mutex;
+using std::shared_ptr;
+using std::string;
+using std::unordered_map;
+using std::vector;
+
 namespace android {
 namespace os {
 namespace statsd {
@@ -51,7 +58,7 @@
      */
     void setBroadcastSubscriber(const ConfigKey& configKey,
                                 int64_t subscriberId,
-                                const sp<IPendingIntentRef>& pir);
+                                const shared_ptr<IPendingIntentRef>& pir);
 
     /**
      * Erases any intentSender information from the given (configKey, subscriberId) pair.
@@ -67,28 +74,25 @@
                                   const Subscription& subscription,
                                   const MetricDimensionKey& dimKey) const;
 
-    sp<IPendingIntentRef> getBroadcastSubscriber(const ConfigKey& configKey, int64_t subscriberId);
+    shared_ptr<IPendingIntentRef> getBroadcastSubscriber(const ConfigKey& configKey,
+                                                         int64_t subscriberId);
 
 private:
     SubscriberReporter() {};
 
-    mutable std::mutex mLock;
-
-    /** Binder interface for communicating with StatsCompanionService. */
-    sp<IStatsCompanionService> mStatsCompanionService = nullptr;
+    mutable mutex mLock;
 
     /** Maps <ConfigKey, SubscriberId> -> IPendingIntentRef (which represents a PendingIntent). */
-    std::unordered_map<ConfigKey,
-            std::unordered_map<int64_t, sp<IPendingIntentRef>>> mIntentMap;
+    unordered_map<ConfigKey, unordered_map<int64_t, shared_ptr<IPendingIntentRef>>> mIntentMap;
 
     /**
      * Sends a broadcast via the given intentSender (using mStatsCompanionService), along
      * with the information in the other parameters.
      */
-    void sendBroadcastLocked(const sp<IPendingIntentRef>& pir,
+    void sendBroadcastLocked(const shared_ptr<IPendingIntentRef>& pir,
                              const ConfigKey& configKey,
                              const Subscription& subscription,
-                             const std::vector<String16>& cookies,
+                             const vector<string>& cookies,
                              const MetricDimensionKey& dimKey) const;
 };
 
diff --git a/cmds/statsd/tests/AlarmMonitor_test.cpp b/cmds/statsd/tests/AlarmMonitor_test.cpp
index 1fccb35..edbf8b5 100644
--- a/cmds/statsd/tests/AlarmMonitor_test.cpp
+++ b/cmds/statsd/tests/AlarmMonitor_test.cpp
@@ -17,14 +17,16 @@
 #include <gtest/gtest.h>
 
 using namespace android::os::statsd;
+using std::shared_ptr;
 
 #ifdef __ANDROID__
 TEST(AlarmMonitor, popSoonerThan) {
     std::string emptyMetricId;
     std::string emptyDimensionId;
     unordered_set<sp<const InternalAlarm>, SpHash<InternalAlarm>> set;
-    AlarmMonitor am(2, [](const sp<IStatsCompanionService>&, int64_t){},
-                    [](const sp<IStatsCompanionService>&){});
+    AlarmMonitor am(2,
+                    [](const shared_ptr<IStatsCompanionService>&, int64_t){},
+                    [](const shared_ptr<IStatsCompanionService>&){});
 
     set = am.popSoonerThan(5);
     EXPECT_TRUE(set.empty());
diff --git a/cmds/statsd/tests/StatsLogProcessor_test.cpp b/cmds/statsd/tests/StatsLogProcessor_test.cpp
index 69e11ed..3d7e5c9 100644
--- a/cmds/statsd/tests/StatsLogProcessor_test.cpp
+++ b/cmds/statsd/tests/StatsLogProcessor_test.cpp
@@ -32,6 +32,8 @@
 
 using namespace android;
 using namespace testing;
+using ::ndk::SharedRefBase;
+using std::shared_ptr;
 
 namespace android {
 namespace os {
@@ -49,10 +51,12 @@
     MockMetricsManager()
         : MetricsManager(ConfigKey(1, 12345), StatsdConfig(), 1000, 1000, new UidMap(),
                          new StatsPullerManager(),
-                         new AlarmMonitor(10, [](const sp<IStatsCompanionService>&, int64_t) {},
-                                          [](const sp<IStatsCompanionService>&) {}),
-                         new AlarmMonitor(10, [](const sp<IStatsCompanionService>&, int64_t) {},
-                                          [](const sp<IStatsCompanionService>&) {})) {
+                         new AlarmMonitor(10,
+                                          [](const shared_ptr<IStatsCompanionService>&, int64_t) {},
+                                          [](const shared_ptr<IStatsCompanionService>&) {}),
+                         new AlarmMonitor(10,
+                                          [](const shared_ptr<IStatsCompanionService>&, int64_t) {},
+                                          [](const shared_ptr<IStatsCompanionService>&) {})) {
     }
 
     MOCK_METHOD0(byteSize, size_t());
@@ -1488,9 +1492,9 @@
     metric2ActivationTrigger2->set_activation_type(ACTIVATE_IMMEDIATELY);
 
     // Send the config.
-    StatsService service(nullptr, nullptr);
+    shared_ptr<StatsService> service = SharedRefBase::make<StatsService>(nullptr, nullptr);
     string serialized = config1.SerializeAsString();
-    service.addConfigurationChecked(uid, configId, {serialized.begin(), serialized.end()});
+    service->addConfigurationChecked(uid, configId, {serialized.begin(), serialized.end()});
 
     // Make sure the config is stored on disk. Otherwise, we will not reset on system server death.
     StatsdConfig tmpConfig;
@@ -1501,7 +1505,7 @@
     // Metric 2 is not active.
     // Metric 3 is active.
     // {{{---------------------------------------------------------------------------
-    sp<StatsLogProcessor> processor = service.mProcessor;
+    sp<StatsLogProcessor> processor = service->mProcessor;
     EXPECT_EQ(1, processor->mMetricsManagers.size());
     auto it = processor->mMetricsManagers.find(cfgKey1);
     EXPECT_TRUE(it != processor->mMetricsManagers.end());
@@ -1592,7 +1596,7 @@
     EXPECT_TRUE(approximateSystemServerDeath < NS_PER_SEC + configAddedTimeNs);
 
     // System server dies.
-    service.binderDied(nullptr);
+    service->statsCompanionServiceDiedImpl();
 
     // We should have a new metrics manager. Lets get it and ensure activation status is restored.
     // {{{---------------------------------------------------------------------------
diff --git a/cmds/statsd/tests/StatsService_test.cpp b/cmds/statsd/tests/StatsService_test.cpp
index 7c00531..86f786e 100644
--- a/cmds/statsd/tests/StatsService_test.cpp
+++ b/cmds/statsd/tests/StatsService_test.cpp
@@ -16,6 +16,7 @@
 #include "config/ConfigKey.h"
 #include "frameworks/base/cmds/statsd/src/statsd_config.pb.h"
 
+#include <android/binder_interface_utils.h>
 #include <gmock/gmock.h>
 #include <gtest/gtest.h>
 
@@ -29,33 +30,34 @@
 namespace statsd {
 
 using android::util::ProtoOutputStream;
+using ::ndk::SharedRefBase;
 
 #ifdef __ANDROID__
 
 TEST(StatsServiceTest, TestAddConfig_simple) {
-    StatsService service(nullptr, nullptr);
+    shared_ptr<StatsService> service = SharedRefBase::make<StatsService>(nullptr, nullptr);
     StatsdConfig config;
     config.set_id(12345);
     string serialized = config.SerializeAsString();
 
     EXPECT_TRUE(
-            service.addConfigurationChecked(123, 12345, {serialized.begin(), serialized.end()}));
+            service->addConfigurationChecked(123, 12345, {serialized.begin(), serialized.end()}));
 }
 
 TEST(StatsServiceTest, TestAddConfig_empty) {
-    StatsService service(nullptr, nullptr);
+    shared_ptr<StatsService> service = SharedRefBase::make<StatsService>(nullptr, nullptr);
     string serialized = "";
 
     EXPECT_TRUE(
-            service.addConfigurationChecked(123, 12345, {serialized.begin(), serialized.end()}));
+            service->addConfigurationChecked(123, 12345, {serialized.begin(), serialized.end()}));
 }
 
 TEST(StatsServiceTest, TestAddConfig_invalid) {
-    StatsService service(nullptr, nullptr);
+    shared_ptr<StatsService> service = SharedRefBase::make<StatsService>(nullptr, nullptr);
     string serialized = "Invalid config!";
 
     EXPECT_FALSE(
-            service.addConfigurationChecked(123, 12345, {serialized.begin(), serialized.end()}));
+            service->addConfigurationChecked(123, 12345, {serialized.begin(), serialized.end()}));
 }
 
 TEST(StatsServiceTest, TestGetUidFromArgs) {
@@ -69,32 +71,32 @@
 
     int32_t uid;
 
-    StatsService service(nullptr, nullptr);
-    service.mEngBuild = true;
+    shared_ptr<StatsService> service = SharedRefBase::make<StatsService>(nullptr, nullptr);
+    service->mEngBuild = true;
 
     // "-1"
-    EXPECT_FALSE(service.getUidFromArgs(args, 0, uid));
+    EXPECT_FALSE(service->getUidFromArgs(args, 0, uid));
 
     // "0"
-    EXPECT_TRUE(service.getUidFromArgs(args, 1, uid));
+    EXPECT_TRUE(service->getUidFromArgs(args, 1, uid));
     EXPECT_EQ(0, uid);
 
     // "1"
-    EXPECT_TRUE(service.getUidFromArgs(args, 2, uid));
+    EXPECT_TRUE(service->getUidFromArgs(args, 2, uid));
     EXPECT_EQ(1, uid);
 
     // "999999999999999999"
-    EXPECT_FALSE(service.getUidFromArgs(args, 3, uid));
+    EXPECT_FALSE(service->getUidFromArgs(args, 3, uid));
 
     // "a1"
-    EXPECT_FALSE(service.getUidFromArgs(args, 4, uid));
+    EXPECT_FALSE(service->getUidFromArgs(args, 4, uid));
 
     // ""
-    EXPECT_FALSE(service.getUidFromArgs(args, 5, uid));
+    EXPECT_FALSE(service->getUidFromArgs(args, 5, uid));
 
     // For a non-userdebug, uid "1" cannot be impersonated.
-    service.mEngBuild = false;
-    EXPECT_FALSE(service.getUidFromArgs(args, 2, uid));
+    service->mEngBuild = false;
+    EXPECT_FALSE(service->getUidFromArgs(args, 2, uid));
 }
 
 #else
diff --git a/cmds/statsd/tests/anomaly/AlarmTracker_test.cpp b/cmds/statsd/tests/anomaly/AlarmTracker_test.cpp
index 5e6de1c..90ffcd0 100644
--- a/cmds/statsd/tests/anomaly/AlarmTracker_test.cpp
+++ b/cmds/statsd/tests/anomaly/AlarmTracker_test.cpp
@@ -15,12 +15,14 @@
 #include "src/anomaly/AlarmTracker.h"
 
 #include <gtest/gtest.h>
+#include <log/log_time.h>
 #include <stdio.h>
 #include <vector>
 
 using namespace testing;
 using android::sp;
 using std::set;
+using std::shared_ptr;
 using std::unordered_map;
 using std::vector;
 
@@ -34,8 +36,9 @@
 
 TEST(AlarmTrackerTest, TestTriggerTimestamp) {
     sp<AlarmMonitor> subscriberAlarmMonitor =
-        new AlarmMonitor(100, [](const sp<IStatsCompanionService>&, int64_t){},
-                         [](const sp<IStatsCompanionService>&){});
+        new AlarmMonitor(100,
+                         [](const shared_ptr<IStatsCompanionService>&, int64_t){},
+                         [](const shared_ptr<IStatsCompanionService>&){});
     Alarm alarm;
     alarm.set_offset_millis(15 * MS_PER_SEC);
     alarm.set_period_millis(60 * 60 * MS_PER_SEC);  // 1hr
diff --git a/cmds/statsd/tests/e2e/GaugeMetric_e2e_pull_test.cpp b/cmds/statsd/tests/e2e/GaugeMetric_e2e_pull_test.cpp
index 5e60aba..9127be8 100644
--- a/cmds/statsd/tests/e2e/GaugeMetric_e2e_pull_test.cpp
+++ b/cmds/statsd/tests/e2e/GaugeMetric_e2e_pull_test.cpp
@@ -12,6 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+#include <android/binder_interface_utils.h>
 #include <gtest/gtest.h>
 
 #include <vector>
@@ -20,6 +21,8 @@
 #include "src/stats_log_util.h"
 #include "tests/statsd_test_util.h"
 
+using ::ndk::SharedRefBase;
+
 namespace android {
 namespace os {
 namespace statsd {
@@ -71,7 +74,8 @@
 
     ConfigKey cfgKey;
     auto processor = CreateStatsLogProcessor(baseTimeNs, configAddedTimeNs, config, cfgKey,
-                                             new FakeSubsystemSleepCallback(), ATOM_TAG);
+                                             SharedRefBase::make<FakeSubsystemSleepCallback>(),
+                                             ATOM_TAG);
     EXPECT_EQ(processor->mMetricsManagers.size(), 1u);
     EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid());
     processor->mPullerManager->ForceClearPullerCache();
@@ -216,7 +220,8 @@
 
     ConfigKey cfgKey;
     auto processor = CreateStatsLogProcessor(baseTimeNs, configAddedTimeNs, config, cfgKey,
-                                             new FakeSubsystemSleepCallback(), ATOM_TAG);
+                                             SharedRefBase::make<FakeSubsystemSleepCallback>(),
+                                             ATOM_TAG);
     EXPECT_EQ(processor->mMetricsManagers.size(), 1u);
     EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid());
     processor->mPullerManager->ForceClearPullerCache();
@@ -317,7 +322,8 @@
 
     ConfigKey cfgKey;
     auto processor = CreateStatsLogProcessor(baseTimeNs, configAddedTimeNs, config, cfgKey,
-                                             new FakeSubsystemSleepCallback, ATOM_TAG);
+                                             SharedRefBase::make<FakeSubsystemSleepCallback>(),
+                                             ATOM_TAG);
     EXPECT_EQ(processor->mMetricsManagers.size(), 1u);
     EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid());
     processor->mPullerManager->ForceClearPullerCache();
@@ -426,7 +432,8 @@
 
     ConfigKey cfgKey;
     auto processor = CreateStatsLogProcessor(baseTimeNs, configAddedTimeNs, config, cfgKey,
-                                             new FakeSubsystemSleepCallback(), ATOM_TAG);
+                                             SharedRefBase::make<FakeSubsystemSleepCallback>(),
+                                             ATOM_TAG);
     EXPECT_EQ(processor->mMetricsManagers.size(), 1u);
     EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid());
     processor->mPullerManager->ForceClearPullerCache();
@@ -544,7 +551,8 @@
 
     ConfigKey cfgKey;
     auto processor = CreateStatsLogProcessor(baseTimeNs, configAddedTimeNs, config, cfgKey,
-                                             new FakeSubsystemSleepCallback(), ATOM_TAG);
+                                             SharedRefBase::make<FakeSubsystemSleepCallback>(),
+                                             ATOM_TAG);
     EXPECT_EQ(processor->mMetricsManagers.size(), 1u);
     EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid());
     processor->mPullerManager->ForceClearPullerCache();
diff --git a/cmds/statsd/tests/e2e/PartialBucket_e2e_test.cpp b/cmds/statsd/tests/e2e/PartialBucket_e2e_test.cpp
index 9d58867..1eecbe5 100644
--- a/cmds/statsd/tests/e2e/PartialBucket_e2e_test.cpp
+++ b/cmds/statsd/tests/e2e/PartialBucket_e2e_test.cpp
@@ -12,9 +12,8 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-#include <android/os/BnPullAtomCallback.h>
-#include <android/os/IPullAtomResultReceiver.h>
-#include <binder/IPCThreadState.h>
+#include <android/binder_ibinder.h>
+#include <android/binder_interface_utils.h>
 #include <gtest/gtest.h>
 
 #include <vector>
@@ -24,6 +23,9 @@
 #include "src/stats_log_util.h"
 #include "tests/statsd_test_util.h"
 
+using::ndk::SharedRefBase;
+using std::shared_ptr;
+
 namespace android {
 namespace os {
 namespace statsd {
@@ -34,19 +36,17 @@
 const int kConfigKey = 789130123;  // Randomly chosen to avoid collisions with existing configs.
 const int kCallingUid = 0; // Randomly chosen
 
-void SendConfig(StatsService& service, const StatsdConfig& config) {
+void SendConfig(shared_ptr<StatsService>& service, const StatsdConfig& config) {
     string str;
     config.SerializeToString(&str);
-    std::vector<uint8_t> configAsVec(str.begin(), str.end());
-    bool success;
-    service.addConfiguration(kConfigKey, configAsVec, kCallingUid);
+    std::vector<int8_t> configAsVec(str.begin(), str.end());
+    service->addConfiguration(kConfigKey, configAsVec, kCallingUid);
 }
 
 ConfigMetricsReport GetReports(sp<StatsLogProcessor> processor, int64_t timestamp,
                                bool include_current = false) {
     vector<uint8_t> output;
-    IPCThreadState* ipc = IPCThreadState::self();
-    ConfigKey configKey(ipc->getCallingUid(), kConfigKey);
+    ConfigKey configKey(AIBinder_getCallingUid(), kConfigKey);
     processor->onDumpReport(configKey, timestamp, include_current /* include_current_bucket*/,
                             true /* erase_data */, ADB_DUMP, NO_TIME_CONSTRAINTS, &output);
     ConfigMetricsReportList reports;
@@ -114,56 +114,56 @@
 }  // anonymous namespace
 
 TEST(PartialBucketE2eTest, TestCountMetricWithoutSplit) {
-    StatsService service(nullptr, nullptr);
+    shared_ptr<StatsService> service = SharedRefBase::make<StatsService>(nullptr, nullptr);
     SendConfig(service, MakeConfig());
     int64_t start = getElapsedRealtimeNs();  // This is the start-time the metrics producers are
                                              // initialized with.
 
-    service.mProcessor->OnLogEvent(CreateAppCrashEvent(100, start + 1).get());
-    service.mProcessor->OnLogEvent(CreateAppCrashEvent(100, start + 2).get());
+    service->mProcessor->OnLogEvent(CreateAppCrashEvent(100, start + 1).get());
+    service->mProcessor->OnLogEvent(CreateAppCrashEvent(100, start + 2).get());
 
-    ConfigMetricsReport report = GetReports(service.mProcessor, start + 3);
+    ConfigMetricsReport report = GetReports(service->mProcessor, start + 3);
     // Expect no metrics since the bucket has not finished yet.
     EXPECT_EQ(1, report.metrics_size());
     EXPECT_EQ(0, report.metrics(0).count_metrics().data_size());
 }
 
 TEST(PartialBucketE2eTest, TestCountMetricNoSplitOnNewApp) {
-    StatsService service(nullptr, nullptr);
+    shared_ptr<StatsService> service = SharedRefBase::make<StatsService>(nullptr, nullptr);
     SendConfig(service, MakeConfig());
     int64_t start = getElapsedRealtimeNs();  // This is the start-time the metrics producers are
                                              // initialized with.
 
     // Force the uidmap to update at timestamp 2.
-    service.mProcessor->OnLogEvent(CreateAppCrashEvent(100, start + 1).get());
+    service->mProcessor->OnLogEvent(CreateAppCrashEvent(100, start + 1).get());
     // This is a new installation, so there shouldn't be a split (should be same as the without
     // split case).
-    service.mUidMap->updateApp(start + 2, String16(kApp1.c_str()), 1, 2, String16("v2"),
+    service->mUidMap->updateApp(start + 2, String16(kApp1.c_str()), 1, 2, String16("v2"),
                                String16(""));
     // Goes into the second bucket.
-    service.mProcessor->OnLogEvent(CreateAppCrashEvent(100, start + 3).get());
+    service->mProcessor->OnLogEvent(CreateAppCrashEvent(100, start + 3).get());
 
-    ConfigMetricsReport report = GetReports(service.mProcessor, start + 4);
+    ConfigMetricsReport report = GetReports(service->mProcessor, start + 4);
     EXPECT_EQ(1, report.metrics_size());
     EXPECT_EQ(0, report.metrics(0).count_metrics().data_size());
 }
 
 TEST(PartialBucketE2eTest, TestCountMetricSplitOnUpgrade) {
-    StatsService service(nullptr, nullptr);
+    shared_ptr<StatsService> service = SharedRefBase::make<StatsService>(nullptr, nullptr);
     SendConfig(service, MakeConfig());
     int64_t start = getElapsedRealtimeNs();  // This is the start-time the metrics producers are
                                              // initialized with.
-    service.mUidMap->updateMap(start, {1}, {1}, {String16("v1")}, {String16(kApp1.c_str())},
-                               {String16("")});
+    service->mUidMap->updateMap(start, {1}, {1}, {String16("v1")}, {String16(kApp1.c_str())},
+                                {String16("")});
 
     // Force the uidmap to update at timestamp 2.
-    service.mProcessor->OnLogEvent(CreateAppCrashEvent(100, start + 1).get());
-    service.mUidMap->updateApp(start + 2, String16(kApp1.c_str()), 1, 2, String16("v2"),
+    service->mProcessor->OnLogEvent(CreateAppCrashEvent(100, start + 1).get());
+    service->mUidMap->updateApp(start + 2, String16(kApp1.c_str()), 1, 2, String16("v2"),
                                String16(""));
     // Goes into the second bucket.
-    service.mProcessor->OnLogEvent(CreateAppCrashEvent(100, start + 3).get());
+    service->mProcessor->OnLogEvent(CreateAppCrashEvent(100, start + 3).get());
 
-    ConfigMetricsReport report = GetReports(service.mProcessor, start + 4);
+    ConfigMetricsReport report = GetReports(service->mProcessor, start + 4);
     backfillStartEndTimestamp(&report);
 
     ASSERT_EQ(1, report.metrics_size());
@@ -177,20 +177,20 @@
 }
 
 TEST(PartialBucketE2eTest, TestCountMetricSplitOnRemoval) {
-    StatsService service(nullptr, nullptr);
+    shared_ptr<StatsService> service = SharedRefBase::make<StatsService>(nullptr, nullptr);
     SendConfig(service, MakeConfig());
     int64_t start = getElapsedRealtimeNs();  // This is the start-time the metrics producers are
                                              // initialized with.
-    service.mUidMap->updateMap(start, {1}, {1}, {String16("v1")}, {String16(kApp1.c_str())},
-                               {String16("")});
+    service->mUidMap->updateMap(start, {1}, {1}, {String16("v1")}, {String16(kApp1.c_str())},
+                                {String16("")});
 
     // Force the uidmap to update at timestamp 2.
-    service.mProcessor->OnLogEvent(CreateAppCrashEvent(100, start + 1).get());
-    service.mUidMap->removeApp(start + 2, String16(kApp1.c_str()), 1);
+    service->mProcessor->OnLogEvent(CreateAppCrashEvent(100, start + 1).get());
+    service->mUidMap->removeApp(start + 2, String16(kApp1.c_str()), 1);
     // Goes into the second bucket.
-    service.mProcessor->OnLogEvent(CreateAppCrashEvent(100, start + 3).get());
+    service->mProcessor->OnLogEvent(CreateAppCrashEvent(100, start + 3).get());
 
-    ConfigMetricsReport report = GetReports(service.mProcessor, start + 4);
+    ConfigMetricsReport report = GetReports(service->mProcessor, start + 4);
     backfillStartEndTimestamp(&report);
 
     ASSERT_EQ(1, report.metrics_size());
@@ -204,44 +204,44 @@
 }
 
 TEST(PartialBucketE2eTest, TestValueMetricWithoutMinPartialBucket) {
-    StatsService service(nullptr, nullptr);
-    service.mPullerManager->RegisterPullAtomCallback(
+    shared_ptr<StatsService> service = SharedRefBase::make<StatsService>(nullptr, nullptr);
+    service->mPullerManager->RegisterPullAtomCallback(
             /*uid=*/0, android::util::SUBSYSTEM_SLEEP_STATE, NS_PER_SEC, NS_PER_SEC * 10, {},
-            new FakeSubsystemSleepCallback());
+            SharedRefBase::make<FakeSubsystemSleepCallback>());
     // Partial buckets don't occur when app is first installed.
-    service.mUidMap->updateApp(1, String16(kApp1.c_str()), 1, 1, String16("v1"), String16(""));
+    service->mUidMap->updateApp(1, String16(kApp1.c_str()), 1, 1, String16("v1"), String16(""));
     SendConfig(service, MakeValueMetricConfig(0));
     int64_t start = getElapsedRealtimeNs();  // This is the start-time the metrics producers are
                                              // initialized with.
 
-    service.mProcessor->informPullAlarmFired(5 * 60 * NS_PER_SEC + start);
-    service.mUidMap->updateApp(5 * 60 * NS_PER_SEC + start + 2, String16(kApp1.c_str()), 1, 2,
-                               String16("v2"), String16(""));
+    service->mProcessor->informPullAlarmFired(5 * 60 * NS_PER_SEC + start);
+    service->mUidMap->updateApp(5 * 60 * NS_PER_SEC + start + 2, String16(kApp1.c_str()), 1, 2,
+                                String16("v2"), String16(""));
 
     ConfigMetricsReport report =
-            GetReports(service.mProcessor, 5 * 60 * NS_PER_SEC + start + 100, true);
+            GetReports(service->mProcessor, 5 * 60 * NS_PER_SEC + start + 100, true);
     EXPECT_EQ(1, report.metrics_size());
     EXPECT_EQ(0, report.metrics(0).value_metrics().skipped_size());
 }
 
 TEST(PartialBucketE2eTest, TestValueMetricWithMinPartialBucket) {
-    StatsService service(nullptr, nullptr);
-    service.mPullerManager->RegisterPullAtomCallback(
+    shared_ptr<StatsService> service = SharedRefBase::make<StatsService>(nullptr, nullptr);
+    service->mPullerManager->RegisterPullAtomCallback(
             /*uid=*/0, android::util::SUBSYSTEM_SLEEP_STATE, NS_PER_SEC, NS_PER_SEC * 10, {},
-            new FakeSubsystemSleepCallback());
+            SharedRefBase::make<FakeSubsystemSleepCallback>());
     // Partial buckets don't occur when app is first installed.
-    service.mUidMap->updateApp(1, String16(kApp1.c_str()), 1, 1, String16("v1"), String16(""));
+    service->mUidMap->updateApp(1, String16(kApp1.c_str()), 1, 1, String16("v1"), String16(""));
     SendConfig(service, MakeValueMetricConfig(60 * NS_PER_SEC /* One minute */));
     int64_t start = getElapsedRealtimeNs();  // This is the start-time the metrics producers are
                                              // initialized with.
 
     const int64_t endSkipped = 5 * 60 * NS_PER_SEC + start + 2;
-    service.mProcessor->informPullAlarmFired(5 * 60 * NS_PER_SEC + start);
-    service.mUidMap->updateApp(endSkipped, String16(kApp1.c_str()), 1, 2, String16("v2"),
+    service->mProcessor->informPullAlarmFired(5 * 60 * NS_PER_SEC + start);
+    service->mUidMap->updateApp(endSkipped, String16(kApp1.c_str()), 1, 2, String16("v2"),
                                String16(""));
 
     ConfigMetricsReport report =
-            GetReports(service.mProcessor, 5 * 60 * NS_PER_SEC + start + 100 * NS_PER_SEC, true);
+            GetReports(service->mProcessor, 5 * 60 * NS_PER_SEC + start + 100 * NS_PER_SEC, true);
     backfillStartEndTimestamp(&report);
 
     ASSERT_EQ(1, report.metrics_size());
@@ -253,38 +253,38 @@
 }
 
 TEST(PartialBucketE2eTest, TestGaugeMetricWithoutMinPartialBucket) {
-    StatsService service(nullptr, nullptr);
+    shared_ptr<StatsService> service = SharedRefBase::make<StatsService>(nullptr, nullptr);
     // Partial buckets don't occur when app is first installed.
-    service.mUidMap->updateApp(1, String16(kApp1.c_str()), 1, 1, String16("v1"), String16(""));
+    service->mUidMap->updateApp(1, String16(kApp1.c_str()), 1, 1, String16("v1"), String16(""));
     SendConfig(service, MakeGaugeMetricConfig(0));
     int64_t start = getElapsedRealtimeNs();  // This is the start-time the metrics producers are
                                              // initialized with.
 
-    service.mProcessor->informPullAlarmFired(5 * 60 * NS_PER_SEC + start);
-    service.mUidMap->updateApp(5 * 60 * NS_PER_SEC + start + 2, String16(kApp1.c_str()), 1, 2,
+    service->mProcessor->informPullAlarmFired(5 * 60 * NS_PER_SEC + start);
+    service->mUidMap->updateApp(5 * 60 * NS_PER_SEC + start + 2, String16(kApp1.c_str()), 1, 2,
                                String16("v2"), String16(""));
 
     ConfigMetricsReport report =
-            GetReports(service.mProcessor, 5 * 60 * NS_PER_SEC + start + 100, true);
+            GetReports(service->mProcessor, 5 * 60 * NS_PER_SEC + start + 100, true);
     EXPECT_EQ(1, report.metrics_size());
     EXPECT_EQ(0, report.metrics(0).gauge_metrics().skipped_size());
 }
 
 TEST(PartialBucketE2eTest, TestGaugeMetricWithMinPartialBucket) {
-    StatsService service(nullptr, nullptr);
+    shared_ptr<StatsService> service = SharedRefBase::make<StatsService>(nullptr, nullptr);
     // Partial buckets don't occur when app is first installed.
-    service.mUidMap->updateApp(1, String16(kApp1.c_str()), 1, 1, String16("v1"), String16(""));
+    service->mUidMap->updateApp(1, String16(kApp1.c_str()), 1, 1, String16("v1"), String16(""));
     SendConfig(service, MakeGaugeMetricConfig(60 * NS_PER_SEC /* One minute */));
     int64_t start = getElapsedRealtimeNs();  // This is the start-time the metrics producers are
                                              // initialized with.
 
     const int64_t endSkipped = 5 * 60 * NS_PER_SEC + start + 2;
-    service.mProcessor->informPullAlarmFired(5 * 60 * NS_PER_SEC + start);
-    service.mUidMap->updateApp(endSkipped, String16(kApp1.c_str()), 1, 2, String16("v2"),
-                               String16(""));
+    service->mProcessor->informPullAlarmFired(5 * 60 * NS_PER_SEC + start);
+    service->mUidMap->updateApp(endSkipped, String16(kApp1.c_str()), 1, 2, String16("v2"),
+                                String16(""));
 
     ConfigMetricsReport report =
-            GetReports(service.mProcessor, 5 * 60 * NS_PER_SEC + start + 100 * NS_PER_SEC, true);
+            GetReports(service->mProcessor, 5 * 60 * NS_PER_SEC + start + 100 * NS_PER_SEC, true);
     backfillStartEndTimestamp(&report);
     ASSERT_EQ(1, report.metrics_size());
     ASSERT_EQ(1, report.metrics(0).gauge_metrics().skipped_size());
diff --git a/cmds/statsd/tests/e2e/ValueMetric_pull_e2e_test.cpp b/cmds/statsd/tests/e2e/ValueMetric_pull_e2e_test.cpp
index a140af8..9d39f9c 100644
--- a/cmds/statsd/tests/e2e/ValueMetric_pull_e2e_test.cpp
+++ b/cmds/statsd/tests/e2e/ValueMetric_pull_e2e_test.cpp
@@ -12,6 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+#include <android/binder_interface_utils.h>
 #include <gtest/gtest.h>
 
 #include "src/StatsLogProcessor.h"
@@ -20,6 +21,8 @@
 
 #include <vector>
 
+using ::ndk::SharedRefBase;
+
 namespace android {
 namespace os {
 namespace statsd {
@@ -70,7 +73,7 @@
 
     ConfigKey cfgKey;
     auto processor = CreateStatsLogProcessor(baseTimeNs, configAddedTimeNs, config, cfgKey,
-                                             new FakeSubsystemSleepCallback(),
+                                             SharedRefBase::make<FakeSubsystemSleepCallback>(),
                                              android::util::SUBSYSTEM_SLEEP_STATE);
     EXPECT_EQ(processor->mMetricsManagers.size(), 1u);
     EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid());
@@ -175,7 +178,7 @@
 
     ConfigKey cfgKey;
     auto processor = CreateStatsLogProcessor(baseTimeNs, configAddedTimeNs, config, cfgKey,
-                                             new FakeSubsystemSleepCallback(),
+                                             SharedRefBase::make<FakeSubsystemSleepCallback>(),
                                              android::util::SUBSYSTEM_SLEEP_STATE);
     EXPECT_EQ(processor->mMetricsManagers.size(), 1u);
     EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid());
@@ -288,7 +291,7 @@
 
     ConfigKey cfgKey;
     auto processor = CreateStatsLogProcessor(baseTimeNs, configAddedTimeNs, config, cfgKey,
-                                             new FakeSubsystemSleepCallback(),
+                                             SharedRefBase::make<FakeSubsystemSleepCallback>(),
                                              android::util::SUBSYSTEM_SLEEP_STATE);
     EXPECT_EQ(processor->mMetricsManagers.size(), 1u);
     EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid());
diff --git a/cmds/statsd/tests/external/StatsCallbackPuller_test.cpp b/cmds/statsd/tests/external/StatsCallbackPuller_test.cpp
index a011692e..e416b4c 100644
--- a/cmds/statsd/tests/external/StatsCallbackPuller_test.cpp
+++ b/cmds/statsd/tests/external/StatsCallbackPuller_test.cpp
@@ -14,8 +14,10 @@
 
 #include "src/external/StatsCallbackPuller.h"
 
-#include <android/os/BnPullAtomCallback.h>
-#include <android/os/IPullAtomResultReceiver.h>
+#include <aidl/android/os/BnPullAtomCallback.h>
+#include <aidl/android/os/IPullAtomResultReceiver.h>
+#include <aidl/android/util/StatsEventParcel.h>
+#include <android/binder_interface_utils.h>
 #include <gmock/gmock.h>
 #include <gtest/gtest.h>
 #include <stdio.h>
@@ -35,6 +37,11 @@
 namespace statsd {
 
 using namespace testing;
+using Status = ::ndk::ScopedAStatus;
+using aidl::android::os::BnPullAtomCallback;
+using aidl::android::os::IPullAtomResultReceiver;
+using aidl::android::util::StatsEventParcel;
+using ::ndk::SharedRefBase;
 using std::make_shared;
 using std::shared_ptr;
 using std::vector;
@@ -58,15 +65,15 @@
     return event;
 }
 
-void executePull(const sp<IPullAtomResultReceiver>& resultReceiver) {
+void executePull(const shared_ptr<IPullAtomResultReceiver>& resultReceiver) {
     // Convert stats_events into StatsEventParcels.
-    std::vector<android::util::StatsEventParcel> parcels;
+    vector<StatsEventParcel> parcels;
     for (int i = 0; i < values.size(); i++) {
         AStatsEvent* event = createSimpleEvent(values[i]);
         size_t size;
         uint8_t* buffer = AStatsEvent_getBuffer(event, &size);
 
-        android::util::StatsEventParcel p;
+        StatsEventParcel p;
         // vector.assign() creates a copy, but this is inevitable unless
         // stats_event.h/c uses a vector as opposed to a buffer.
         p.buffer.assign(buffer, buffer + size);
@@ -80,11 +87,11 @@
 
 class FakePullAtomCallback : public BnPullAtomCallback {
 public:
-    binder::Status onPullAtom(int atomTag,
-                              const sp<IPullAtomResultReceiver>& resultReceiver) override {
+    Status onPullAtom(int atomTag,
+                      const shared_ptr<IPullAtomResultReceiver>& resultReceiver) override {
         // Force pull to happen in separate thread to simulate binder.
         pullThread = std::thread(executePull, resultReceiver);
-        return binder::Status::ok();
+        return Status::ok();
     }
 };
 
@@ -111,7 +118,7 @@
 }  // Anonymous namespace.
 
 TEST_F(StatsCallbackPullerTest, PullSuccess) {
-    sp<FakePullAtomCallback> cb = new FakePullAtomCallback();
+    shared_ptr<FakePullAtomCallback> cb = SharedRefBase::make<FakePullAtomCallback>();
     int64_t value = 43;
     pullSuccess = true;
     values.push_back(value);
@@ -132,20 +139,20 @@
 }
 
 TEST_F(StatsCallbackPullerTest, PullFail) {
-    sp<FakePullAtomCallback> cb = new FakePullAtomCallback();
+    shared_ptr<FakePullAtomCallback> cb = SharedRefBase::make<FakePullAtomCallback>();
     pullSuccess = false;
     int64_t value = 1234;
     values.push_back(value);
 
     StatsCallbackPuller puller(pullTagId, cb, pullCoolDownNs, pullTimeoutNs, {});
 
-    vector<std::shared_ptr<LogEvent>> dataHolder;
+    vector<shared_ptr<LogEvent>> dataHolder;
     EXPECT_FALSE(puller.PullInternal(&dataHolder));
     EXPECT_EQ(0, dataHolder.size());
 }
 
 TEST_F(StatsCallbackPullerTest, PullTimeout) {
-    sp<FakePullAtomCallback> cb = new FakePullAtomCallback();
+    shared_ptr<FakePullAtomCallback> cb = SharedRefBase::make<FakePullAtomCallback>();
     pullSuccess = true;
     pullDelayNs = 500000000;  // 500ms.
     pullTimeoutNs = 10000;    // 10 microseconds.
@@ -154,7 +161,7 @@
 
     StatsCallbackPuller puller(pullTagId, cb, pullCoolDownNs, pullTimeoutNs, {});
 
-    vector<std::shared_ptr<LogEvent>> dataHolder;
+    vector<shared_ptr<LogEvent>> dataHolder;
     int64_t startTimeNs = getElapsedRealtimeNs();
     // Returns true to let StatsPuller code evaluate the timeout.
     EXPECT_TRUE(puller.PullInternal(&dataHolder));
@@ -174,7 +181,7 @@
 
 // Register a puller and ensure that the timeout logic works.
 TEST_F(StatsCallbackPullerTest, RegisterAndTimeout) {
-    sp<FakePullAtomCallback> cb = new FakePullAtomCallback();
+    shared_ptr<FakePullAtomCallback> cb = SharedRefBase::make<FakePullAtomCallback>();
     pullSuccess = true;
     pullDelayNs = 500000000;  // 500 ms.
     pullTimeoutNs = 10000;    // 10 microsseconds.
@@ -184,7 +191,7 @@
     StatsPullerManager pullerManager;
     pullerManager.RegisterPullAtomCallback(/*uid=*/-1, pullTagId, pullCoolDownNs, pullTimeoutNs,
                                            vector<int32_t>(), cb);
-    vector<std::shared_ptr<LogEvent>> dataHolder;
+    vector<shared_ptr<LogEvent>> dataHolder;
     int64_t startTimeNs = getElapsedRealtimeNs();
     // Returns false, since StatsPuller code will evaluate the timeout.
     EXPECT_FALSE(pullerManager.Pull(pullTagId, &dataHolder));
diff --git a/cmds/statsd/tests/shell/ShellSubscriber_test.cpp b/cmds/statsd/tests/shell/ShellSubscriber_test.cpp
index 73d1fd7..dac5f33 100644
--- a/cmds/statsd/tests/shell/ShellSubscriber_test.cpp
+++ b/cmds/statsd/tests/shell/ShellSubscriber_test.cpp
@@ -34,27 +34,6 @@
 
 #ifdef __ANDROID__
 
-class MyResultReceiver : public BnResultReceiver {
-public:
-    Mutex mMutex;
-    Condition mCondition;
-    bool mHaveResult = false;
-    int32_t mResult = 0;
-
-    virtual void send(int32_t resultCode) {
-        AutoMutex _l(mMutex);
-        mResult = resultCode;
-        mHaveResult = true;
-        mCondition.signal();
-    }
-
-    int32_t waitForResult() {
-        AutoMutex _l(mMutex);
-        mCondition.waitRelative(mMutex, 1000000000);
-        return mResult;
-    }
-};
-
 void runShellTest(ShellSubscription config, sp<MockUidMap> uidMap,
                   sp<MockStatsPullerManager> pullerManager,
                   const vector<std::shared_ptr<LogEvent>>& pushedEvents,
@@ -67,10 +46,7 @@
     ASSERT_EQ(0, pipe(fds_data));
 
     size_t bufferSize = config.ByteSize();
-
     // write the config to pipe, first write size of the config
-    vector<uint8_t> size_buffer(sizeof(bufferSize));
-    std::memcpy(size_buffer.data(), &bufferSize, sizeof(bufferSize));
     write(fds_config[1], &bufferSize, sizeof(bufferSize));
     // then write config itself
     vector<uint8_t> buffer(bufferSize);
@@ -79,11 +55,10 @@
     close(fds_config[1]);
 
     sp<ShellSubscriber> shellClient = new ShellSubscriber(uidMap, pullerManager);
-    sp<MyResultReceiver> resultReceiver = new MyResultReceiver();
 
     // mimic a binder thread that a shell subscriber runs on. it would block.
-    std::thread reader([&resultReceiver, &fds_config, &fds_data, &shellClient] {
-        shellClient->startNewSubscription(fds_config[0], fds_data[1], resultReceiver, -1);
+    std::thread reader([&shellClient, &fds_config, &fds_data] {
+        shellClient->startNewSubscription(fds_config[0], fds_data[1], /*timeoutSec=*/-1);
     });
     reader.detach();
 
diff --git a/cmds/statsd/tests/statsd_test_util.cpp b/cmds/statsd/tests/statsd_test_util.cpp
index db09ee9..6958218 100644
--- a/cmds/statsd/tests/statsd_test_util.cpp
+++ b/cmds/statsd/tests/statsd_test_util.cpp
@@ -14,6 +14,11 @@
 
 #include "statsd_test_util.h"
 
+#include <aidl/android/util/StatsEventParcel.h>
+
+using aidl::android::util::StatsEventParcel;
+using std::shared_ptr;
+
 namespace android {
 namespace os {
 namespace statsd {
@@ -581,7 +586,7 @@
 
 sp<StatsLogProcessor> CreateStatsLogProcessor(const int64_t timeBaseNs, const int64_t currentTimeNs,
                                               const StatsdConfig& config, const ConfigKey& key,
-                                              const sp<IPullAtomCallback>& puller,
+                                              const shared_ptr<IPullAtomCallback>& puller,
                                               const int32_t atomTag) {
     sp<UidMap> uidMap = new UidMap();
     sp<StatsPullerManager> pullerManager = new StatsPullerManager();
@@ -590,11 +595,13 @@
                                                 puller);
     }
     sp<AlarmMonitor> anomalyAlarmMonitor =
-        new AlarmMonitor(1,  [](const sp<IStatsCompanionService>&, int64_t){},
-                [](const sp<IStatsCompanionService>&){});
+        new AlarmMonitor(1,
+                         [](const shared_ptr<IStatsCompanionService>&, int64_t){},
+                         [](const shared_ptr<IStatsCompanionService>&){});
     sp<AlarmMonitor> periodicAlarmMonitor =
-        new AlarmMonitor(1,  [](const sp<IStatsCompanionService>&, int64_t){},
-                [](const sp<IStatsCompanionService>&){});
+        new AlarmMonitor(1,
+                         [](const shared_ptr<IStatsCompanionService>&, int64_t){},
+                         [](const shared_ptr<IStatsCompanionService>&){});
     sp<StatsLogProcessor> processor =
             new StatsLogProcessor(uidMap, pullerManager, anomalyAlarmMonitor, periodicAlarmMonitor,
                                   timeBaseNs, [](const ConfigKey&) { return true; },
@@ -948,10 +955,10 @@
     }
 }
 
-binder::Status FakeSubsystemSleepCallback::onPullAtom(
-        int atomTag, const sp<IPullAtomResultReceiver>& resultReceiver) {
+Status FakeSubsystemSleepCallback::onPullAtom(int atomTag,
+        const shared_ptr<IPullAtomResultReceiver>& resultReceiver) {
     // Convert stats_events into StatsEventParcels.
-    std::vector<android::util::StatsEventParcel> parcels;
+    std::vector<StatsEventParcel> parcels;
     for (int i = 1; i < 3; i++) {
         AStatsEvent* event = AStatsEvent_obtain();
         AStatsEvent_setAtomId(event, atomTag);
@@ -965,7 +972,7 @@
         size_t size;
         uint8_t* buffer = AStatsEvent_getBuffer(event, &size);
 
-        android::util::StatsEventParcel p;
+        StatsEventParcel p;
         // vector.assign() creates a copy, but this is inevitable unless
         // stats_event.h/c uses a vector as opposed to a buffer.
         p.buffer.assign(buffer, buffer + size);
@@ -973,7 +980,7 @@
         AStatsEvent_write(event);
     }
     resultReceiver->pullFinished(atomTag, /*success=*/true, parcels);
-    return binder::Status::ok();
+    return Status::ok();
 }
 
 }  // namespace statsd
diff --git a/cmds/statsd/tests/statsd_test_util.h b/cmds/statsd/tests/statsd_test_util.h
index 576a491..c8326ee 100644
--- a/cmds/statsd/tests/statsd_test_util.h
+++ b/cmds/statsd/tests/statsd_test_util.h
@@ -14,9 +14,9 @@
 
 #pragma once
 
-#include <android/os/BnPullAtomCallback.h>
-#include <android/os/IPullAtomCallback.h>
-#include <android/os/IPullAtomResultReceiver.h>
+#include <aidl/android/os/BnPullAtomCallback.h>
+#include <aidl/android/os/IPullAtomCallback.h>
+#include <aidl/android/os/IPullAtomResultReceiver.h>
 #include <gtest/gtest.h>
 
 #include "frameworks/base/cmds/statsd/src/stats_log.pb.h"
@@ -31,8 +31,12 @@
 namespace os {
 namespace statsd {
 
+using ::aidl::android::os::BnPullAtomCallback;
+using ::aidl::android::os::IPullAtomCallback;
+using ::aidl::android::os::IPullAtomResultReceiver;
 using android::util::ProtoReader;
 using google::protobuf::RepeatedPtrField;
+using Status = ::ndk::ScopedAStatus;
 
 const int SCREEN_STATE_ATOM_ID = android::util::SCREEN_STATE_CHANGED;
 const int UID_PROCESS_STATE_ATOM_ID = android::util::UID_PROCESS_STATE_CHANGED;
@@ -230,7 +234,7 @@
 // Create a statsd log event processor upon the start time in seconds, config and key.
 sp<StatsLogProcessor> CreateStatsLogProcessor(const int64_t timeBaseNs, const int64_t currentTimeNs,
                                               const StatsdConfig& config, const ConfigKey& key,
-                                              const sp<IPullAtomCallback>& puller = nullptr,
+                                              const shared_ptr<IPullAtomCallback>& puller = nullptr,
                                               const int32_t atomTag = 0 /*for puller only*/);
 
 // Util function to sort the log events by timestamp.
@@ -285,8 +289,8 @@
 
 class FakeSubsystemSleepCallback : public BnPullAtomCallback {
 public:
-    binder::Status onPullAtom(int atomTag,
-                              const sp<IPullAtomResultReceiver>& resultReceiver) override;
+    Status onPullAtom(int atomTag,
+                      const shared_ptr<IPullAtomResultReceiver>& resultReceiver) override;
 };
 
 template <typename T>
diff --git a/core/java/android/content/pm/parsing/AndroidPackage.java b/core/java/android/content/pm/parsing/AndroidPackage.java
index da17ff3..b93c3a2 100644
--- a/core/java/android/content/pm/parsing/AndroidPackage.java
+++ b/core/java/android/content/pm/parsing/AndroidPackage.java
@@ -397,6 +397,8 @@
 
     boolean hasComponentClassName(String className);
 
+    boolean hasPreserveLegacyExternalStorage();
+
     // App Info
 
     boolean hasRequestedLegacyExternalStorage();
diff --git a/core/java/android/content/pm/parsing/ApkParseUtils.java b/core/java/android/content/pm/parsing/ApkParseUtils.java
index a267113..905794b 100644
--- a/core/java/android/content/pm/parsing/ApkParseUtils.java
+++ b/core/java/android/content/pm/parsing/ApkParseUtils.java
@@ -2101,6 +2101,10 @@
             parsingPackage.setAllowNativeHeapPointerTagging(sa.getBoolean(
                     R.styleable.AndroidManifestApplication_allowNativeHeapPointerTagging, true));
 
+            parsingPackage.setPreserveLegacyExternalStorage(sa.getBoolean(
+                            R.styleable.AndroidManifestApplication_preserveLegacyExternalStorage,
+                            false));
+
             parsingPackage
                     .setMaxAspectRatio(
                             sa.getFloat(R.styleable.AndroidManifestApplication_maxAspectRatio, 0))
diff --git a/core/java/android/content/pm/parsing/PackageImpl.java b/core/java/android/content/pm/parsing/PackageImpl.java
index 778d7b8..180714a 100644
--- a/core/java/android/content/pm/parsing/PackageImpl.java
+++ b/core/java/android/content/pm/parsing/PackageImpl.java
@@ -289,6 +289,7 @@
     private String[] usesLibraryFiles;
     private List<SharedLibraryInfo> usesLibraryInfos;
     private String zygotePreloadName;
+    private boolean preserveLegacyExternalStorage;
 
     @VisibleForTesting
     public PackageImpl(
@@ -1519,6 +1520,12 @@
     }
 
     @Override
+    public PackageImpl setPreserveLegacyExternalStorage(boolean preserveLegacyExternalStorage) {
+        this.preserveLegacyExternalStorage = preserveLegacyExternalStorage;
+        return this;
+    }
+
+    @Override
     public PackageImpl setUsesNonSdkApi(boolean usesNonSdkApi) {
         this.privateFlags = usesNonSdkApi
                 ? this.privateFlags | ApplicationInfo.PRIVATE_FLAG_USES_NON_SDK_API
@@ -2999,6 +3006,11 @@
         return queriesProviders;
     }
 
+    @Override
+    public boolean hasPreserveLegacyExternalStorage() {
+        return preserveLegacyExternalStorage;
+    }
+
     private static void internStringArrayList(List<String> list) {
         if (list != null) {
             final int N = list.size();
diff --git a/core/java/android/content/pm/parsing/ParsingPackage.java b/core/java/android/content/pm/parsing/ParsingPackage.java
index 954d65c..74a2640 100644
--- a/core/java/android/content/pm/parsing/ParsingPackage.java
+++ b/core/java/android/content/pm/parsing/ParsingPackage.java
@@ -192,6 +192,8 @@
     ParsingPackage setRequestLegacyExternalStorage(boolean requestLegacyExternalStorage);
 
     ParsingPackage setAllowNativeHeapPointerTagging(boolean allowNativeHeapPointerTagging);
+  
+    ParsingPackage setPreserveLegacyExternalStorage(boolean preserveLegacyExternalStorage);
 
     ParsingPackage setRestoreAnyVersion(boolean restoreAnyVersion);
 
diff --git a/core/java/android/inputmethodservice/InlineSuggestionSession.java b/core/java/android/inputmethodservice/InlineSuggestionSession.java
new file mode 100644
index 0000000..edae06a
--- /dev/null
+++ b/core/java/android/inputmethodservice/InlineSuggestionSession.java
@@ -0,0 +1,158 @@
+/*
+ * 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 android.inputmethodservice;
+
+import static android.inputmethodservice.InputMethodService.DEBUG;
+
+import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage;
+
+import android.annotation.NonNull;
+import android.content.ComponentName;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Looper;
+import android.os.RemoteException;
+import android.util.Log;
+import android.view.inputmethod.InlineSuggestionsRequest;
+import android.view.inputmethod.InlineSuggestionsResponse;
+
+import com.android.internal.view.IInlineSuggestionsRequestCallback;
+import com.android.internal.view.IInlineSuggestionsResponseCallback;
+
+import java.lang.ref.WeakReference;
+import java.util.function.Consumer;
+import java.util.function.Supplier;
+
+/**
+ * Maintains an active inline suggestion session.
+ *
+ * <p>
+ * Each session corresponds to one inline suggestion request, but there may be multiple callbacks
+ * with the inline suggestions response.
+ */
+class InlineSuggestionSession {
+
+    private static final String TAG = InlineSuggestionSession.class.getSimpleName();
+
+    private final Handler mHandler = new Handler(Looper.getMainLooper(), null, true);
+
+    @NonNull
+    private final ComponentName mComponentName;
+    @NonNull
+    private final IInlineSuggestionsRequestCallback mCallback;
+    @NonNull
+    private final InlineSuggestionsResponseCallbackImpl mResponseCallback;
+    @NonNull
+    private final Supplier<String> mClientPackageNameSupplier;
+    @NonNull
+    private final Supplier<InlineSuggestionsRequest> mRequestSupplier;
+    @NonNull
+    private final Supplier<IBinder> mHostInputTokenSupplier;
+    @NonNull
+    private final Consumer<InlineSuggestionsResponse> mResponseConsumer;
+
+    private volatile boolean mInvalidated = false;
+
+    InlineSuggestionSession(@NonNull ComponentName componentName,
+            @NonNull IInlineSuggestionsRequestCallback callback,
+            @NonNull Supplier<String> clientPackageNameSupplier,
+            @NonNull Supplier<InlineSuggestionsRequest> requestSupplier,
+            @NonNull Supplier<IBinder> hostInputTokenSupplier,
+            @NonNull Consumer<InlineSuggestionsResponse> responseConsumer) {
+        mComponentName = componentName;
+        mCallback = callback;
+        mResponseCallback = new InlineSuggestionsResponseCallbackImpl(this);
+        mClientPackageNameSupplier = clientPackageNameSupplier;
+        mRequestSupplier = requestSupplier;
+        mHostInputTokenSupplier = hostInputTokenSupplier;
+        mResponseConsumer = responseConsumer;
+
+        makeInlineSuggestionsRequest();
+    }
+
+    /**
+     * This needs to be called before creating a new session, such that the later response callbacks
+     * will be discarded.
+     */
+    void invalidateSession() {
+        mInvalidated = true;
+    }
+
+    /**
+     * Sends an {@link InlineSuggestionsRequest} obtained from {@cocde supplier} to the current
+     * Autofill Session through
+     * {@link IInlineSuggestionsRequestCallback#onInlineSuggestionsRequest}.
+     */
+    private void makeInlineSuggestionsRequest() {
+        try {
+            final InlineSuggestionsRequest request = mRequestSupplier.get();
+            if (request == null) {
+                if (DEBUG) {
+                    Log.d(TAG, "onCreateInlineSuggestionsRequest() returned null request");
+                }
+                mCallback.onInlineSuggestionsUnsupported();
+            } else {
+                request.setHostInputToken(mHostInputTokenSupplier.get());
+                mCallback.onInlineSuggestionsRequest(request, mResponseCallback);
+            }
+        } catch (RemoteException e) {
+            Log.w(TAG, "makeInlinedSuggestionsRequest() remote exception:" + e);
+        }
+    }
+
+    private void handleOnInlineSuggestionsResponse(@NonNull InlineSuggestionsResponse response) {
+        if (mInvalidated) {
+            if (DEBUG) {
+                Log.d(TAG, "handleOnInlineSuggestionsResponse() called on invalid session");
+            }
+            return;
+        }
+        // TODO(b/149522488): checking the current focused input field to make sure we don't send
+        //  inline responses for previous input field
+        if (!mComponentName.getPackageName().equals(mClientPackageNameSupplier.get())) {
+            if (DEBUG) {
+                Log.d(TAG, "handleOnInlineSuggestionsResponse() called on the wrong package name");
+            }
+            return;
+        }
+        mResponseConsumer.accept(response);
+    }
+
+    /**
+     * Internal implementation of {@link IInlineSuggestionsResponseCallback}.
+     */
+    static final class InlineSuggestionsResponseCallbackImpl
+            extends IInlineSuggestionsResponseCallback.Stub {
+        private final WeakReference<InlineSuggestionSession> mInlineSuggestionSession;
+
+        private InlineSuggestionsResponseCallbackImpl(
+                InlineSuggestionSession inlineSuggestionSession) {
+            mInlineSuggestionSession = new WeakReference<>(inlineSuggestionSession);
+        }
+
+        @Override
+        public void onInlineSuggestionsResponse(InlineSuggestionsResponse response)
+                throws RemoteException {
+            final InlineSuggestionSession session = mInlineSuggestionSession.get();
+            if (session != null) {
+                session.mHandler.sendMessage(obtainMessage(
+                        InlineSuggestionSession::handleOnInlineSuggestionsResponse, session,
+                        response));
+            }
+        }
+    }
+}
diff --git a/core/java/android/inputmethodservice/InputMethodService.java b/core/java/android/inputmethodservice/InputMethodService.java
index a95938b..b1aa67e 100644
--- a/core/java/android/inputmethodservice/InputMethodService.java
+++ b/core/java/android/inputmethodservice/InputMethodService.java
@@ -22,8 +22,6 @@
 import static android.view.WindowInsets.Type.navigationBars;
 import static android.view.WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
 
-import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage;
-
 import static java.lang.annotation.RetentionPolicy.SOURCE;
 
 import android.annotation.AnyThread;
@@ -51,7 +49,6 @@
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.IBinder;
-import android.os.Looper;
 import android.os.RemoteException;
 import android.os.ResultReceiver;
 import android.os.SystemClock;
@@ -102,13 +99,11 @@
 import com.android.internal.inputmethod.InputMethodPrivilegedOperations;
 import com.android.internal.inputmethod.InputMethodPrivilegedOperationsRegistry;
 import com.android.internal.view.IInlineSuggestionsRequestCallback;
-import com.android.internal.view.IInlineSuggestionsResponseCallback;
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
-import java.lang.ref.WeakReference;
 import java.util.Collections;
 
 /**
@@ -450,7 +445,7 @@
     final int[] mTmpLocation = new int[2];
 
     @Nullable
-    private InlineSuggestionsRequestInfo mInlineSuggestionsRequestInfo = null;
+    private InlineSuggestionSession mInlineSuggestionSession;
 
     private boolean mAutomotiveHideNavBarForKeyboard;
     private boolean mIsAutomotive;
@@ -465,8 +460,6 @@
      */
     private IBinder mCurShowInputToken;
 
-    private final Handler mHandler = new Handler(Looper.getMainLooper(), null, true);
-
     final ViewTreeObserver.OnComputeInternalInsetsListener mInsetsComputer = info -> {
         onComputeInsets(mTmpInsets);
         if (isExtractViewShown()) {
@@ -538,7 +531,7 @@
             if (DEBUG) {
                 Log.d(TAG, "InputMethodService received onCreateInlineSuggestionsRequest()");
             }
-            handleOnCreateInlineSuggestionsRequest(componentName, autofillId, cb);
+            handleOnCreateInlineSuggestionsRequest(componentName, cb);
         }
 
         /**
@@ -770,40 +763,9 @@
         return false;
     }
 
-    /**
-     * Sends an {@link InlineSuggestionsRequest} obtained from
-     * {@link #onCreateInlineSuggestionsRequest()} to the current Autofill Session through
-     * {@link IInlineSuggestionsRequestCallback#onInlineSuggestionsRequest}.
-     */
-    private void makeInlineSuggestionsRequest() {
-        if (mInlineSuggestionsRequestInfo == null) {
-            Log.w(TAG, "makeInlineSuggestionsRequest() called with null requestInfo cache");
-            return;
-        }
-
-        final IInlineSuggestionsRequestCallback requestCallback =
-                mInlineSuggestionsRequestInfo.mCallback;
-        try {
-            final InlineSuggestionsRequest request = onCreateInlineSuggestionsRequest();
-            if (request == null) {
-                Log.w(TAG, "onCreateInlineSuggestionsRequest() returned null request");
-                requestCallback.onInlineSuggestionsUnsupported();
-            } else {
-                request.setHostInputToken(getHostInputToken());
-                final IInlineSuggestionsResponseCallback inlineSuggestionsResponseCallback =
-                        new InlineSuggestionsResponseCallbackImpl(this,
-                                mInlineSuggestionsRequestInfo.mComponentName,
-                                mInlineSuggestionsRequestInfo.mFocusedId);
-                requestCallback.onInlineSuggestionsRequest(request,
-                        inlineSuggestionsResponseCallback);
-            }
-        } catch (RemoteException e) {
-            Log.w(TAG, "makeInlinedSuggestionsRequest() remote exception:" + e);
-        }
-    }
-
+    @MainThread
     private void handleOnCreateInlineSuggestionsRequest(@NonNull ComponentName componentName,
-            @NonNull AutofillId autofillId, @NonNull IInlineSuggestionsRequestCallback callback) {
+            @NonNull IInlineSuggestionsRequestCallback callback) {
         if (!mInputStarted) {
             try {
                 Log.w(TAG, "onStartInput() not called yet");
@@ -814,24 +776,20 @@
             return;
         }
 
-        mInlineSuggestionsRequestInfo = new InlineSuggestionsRequestInfo(componentName, autofillId,
-                callback);
-
-        makeInlineSuggestionsRequest();
+        if (mInlineSuggestionSession != null) {
+            mInlineSuggestionSession.invalidateSession();
+        }
+        mInlineSuggestionSession = new InlineSuggestionSession(componentName, callback,
+                this::getEditorInfoPackageName, this::onCreateInlineSuggestionsRequest,
+                this::getHostInputToken, this::onInlineSuggestionsResponse);
     }
 
-    private void handleOnInlineSuggestionsResponse(@NonNull ComponentName componentName,
-            @NonNull AutofillId autofillId, @NonNull InlineSuggestionsResponse response) {
-        if (!mInlineSuggestionsRequestInfo.validate(componentName)) {
-            if (DEBUG) {
-                Log.d(TAG,
-                        "Response component=" + componentName + " differs from request component="
-                                + mInlineSuggestionsRequestInfo.mComponentName
-                                + ", ignoring response");
-            }
-            return;
+    @Nullable
+    private String getEditorInfoPackageName() {
+        if (mInputEditorInfo != null) {
+            return mInputEditorInfo.packageName;
         }
-        onInlineSuggestionsResponse(response);
+        return null;
     }
 
     /**
@@ -864,63 +822,6 @@
     }
 
     /**
-     * Internal implementation of {@link IInlineSuggestionsResponseCallback}.
-     */
-    private static final class InlineSuggestionsResponseCallbackImpl
-            extends IInlineSuggestionsResponseCallback.Stub {
-        private final WeakReference<InputMethodService> mInputMethodService;
-
-        private final ComponentName mRequestComponentName;
-        private final AutofillId mRequestAutofillId;
-
-        private InlineSuggestionsResponseCallbackImpl(InputMethodService inputMethodService,
-                ComponentName componentName, AutofillId autofillId) {
-            mInputMethodService = new WeakReference<>(inputMethodService);
-            mRequestComponentName = componentName;
-            mRequestAutofillId = autofillId;
-        }
-
-        @Override
-        public void onInlineSuggestionsResponse(InlineSuggestionsResponse response)
-                throws RemoteException {
-            final InputMethodService service = mInputMethodService.get();
-            if (service != null) {
-                service.mHandler.sendMessage(obtainMessage(
-                        InputMethodService::handleOnInlineSuggestionsResponse, service,
-                        mRequestComponentName, mRequestAutofillId, response));
-            }
-        }
-    }
-
-    /**
-     * Information about incoming requests from Autofill Frameworks for inline suggestions.
-     */
-    private static final class InlineSuggestionsRequestInfo {
-        final ComponentName mComponentName;
-        final AutofillId mFocusedId;
-        final IInlineSuggestionsRequestCallback mCallback;
-
-        InlineSuggestionsRequestInfo(ComponentName componentName, AutofillId focusedId,
-                IInlineSuggestionsRequestCallback callback) {
-            this.mComponentName = componentName;
-            this.mFocusedId = focusedId;
-            this.mCallback = callback;
-        }
-
-        /**
-         * Returns whether the cached {@link ComponentName} matches the passed in activity.
-         */
-        public boolean validate(ComponentName componentName) {
-            final boolean result = componentName.equals(mComponentName);
-            if (DEBUG && !result) {
-                Log.d(TAG, "Cached request info ComponentName=" + mComponentName
-                        + " differs from received ComponentName=" + componentName);
-            }
-            return result;
-        }
-    }
-
-    /**
      * Concrete implementation of
      * {@link AbstractInputMethodService.AbstractInputMethodSessionImpl} that provides
      * all of the standard behavior for an input method session.
diff --git a/core/java/android/os/storage/StorageManagerInternal.java b/core/java/android/os/storage/StorageManagerInternal.java
index 5cac5f5..e78aef5 100644
--- a/core/java/android/os/storage/StorageManagerInternal.java
+++ b/core/java/android/os/storage/StorageManagerInternal.java
@@ -118,4 +118,10 @@
      * @param userId the userId for which to reset storage
      */
     public abstract void resetUser(int userId);
+
+    /**
+     * Returns {@code true} if the immediate last installed version of an app with {@code uid} had
+     * legacy storage, {@code false} otherwise.
+     */
+    public abstract boolean hasLegacyExternalStorage(int uid);
 }
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index a8f88d4..f6072f9 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -6462,6 +6462,7 @@
          * @hide
          */
         @SystemApi
+        @TestApi
         public static final String LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS =
                 "lock_screen_allow_private_notifications";
 
@@ -7880,6 +7881,7 @@
          * @hide
          */
         @UnsupportedAppUsage
+        @TestApi
         public static final String NFC_PAYMENT_DEFAULT_COMPONENT = "nfc_payment_default_component";
 
         /**
@@ -8082,6 +8084,7 @@
          * @hide
          */
         @SystemApi
+        @TestApi
         public static final String LOCK_SCREEN_SHOW_NOTIFICATIONS =
                 "lock_screen_show_notifications";
 
diff --git a/core/java/android/service/autofill/augmented/AugmentedAutofillService.java b/core/java/android/service/autofill/augmented/AugmentedAutofillService.java
index 79852d3..672b501 100644
--- a/core/java/android/service/autofill/augmented/AugmentedAutofillService.java
+++ b/core/java/android/service/autofill/augmented/AugmentedAutofillService.java
@@ -89,6 +89,8 @@
 
     private SparseArray<AutofillProxy> mAutofillProxies;
 
+    private AutofillProxy mAutofillProxyForLastRequest;
+
     // Used for metrics / debug only
     private ComponentName mServiceComponentName;
 
@@ -158,6 +160,38 @@
     }
 
     /**
+     * The child class of the service can call this method to initiate an Autofill flow.
+     *
+     * <p> The request would be respected only if the previous augmented autofill request was
+     * made for the same {@code activityComponent} and {@code autofillId}, and the field is
+     * currently on focus.
+     *
+     * <p> The request would start a new autofill flow. It doesn't guarantee that the
+     * {@link AutofillManager} will proceed with the request.
+     *
+     * @param activityComponent the client component for which the autofill is requested for
+     * @param autofillId        the client field id for which the autofill is requested for
+     * @return true if the request makes the {@link AutofillManager} start a new Autofill flow,
+     * false otherwise.
+     */
+    public final boolean requestAutofill(@NonNull ComponentName activityComponent,
+            @NonNull AutofillId autofillId) {
+        // TODO(b/149531989): revisit this. The request should start a new autofill session
+        //  rather than reusing the existing session.
+        final AutofillProxy proxy = mAutofillProxyForLastRequest;
+        if (proxy == null || !proxy.mComponentName.equals(activityComponent)
+                || !proxy.mFocusedId.equals(autofillId)) {
+            return false;
+        }
+        try {
+            return proxy.requestAutofill();
+        } catch (RemoteException e) {
+            e.rethrowFromSystemServer();
+        }
+        return false;
+    }
+
+    /**
      * Asks the service to handle an "augmented" autofill request.
      *
      * <p>This method is called when the "stantard" autofill service cannot handle a request, which
@@ -241,6 +275,7 @@
         } catch (RemoteException e) {
             e.rethrowFromSystemServer();
         }
+        mAutofillProxyForLastRequest = proxy;
         onFillRequest(new FillRequest(proxy, inlineSuggestionsRequest), cancellationSignal,
                 new FillController(proxy), new FillCallback(proxy));
     }
@@ -268,6 +303,7 @@
                 proxy.destroy();
             }
             mAutofillProxies.clear();
+            mAutofillProxyForLastRequest = null;
         }
     }
 
@@ -287,6 +323,7 @@
             }
         }
         mAutofillProxies = null;
+        mAutofillProxyForLastRequest = null;
     }
 
     @Override
@@ -481,6 +518,11 @@
             mClient.requestHideFillUi(mSessionId, mFocusedId);
         }
 
+
+        private boolean requestAutofill() throws RemoteException {
+            return mClient.requestAutofill(mSessionId, mFocusedId);
+        }
+
         private void update(@NonNull AutofillId focusedId, @NonNull AutofillValue focusedValue,
                 @NonNull IFillCallback callback, @NonNull CancellationSignal cancellationSignal) {
             synchronized (mLock) {
diff --git a/core/java/android/service/quickaccesswallet/GetWalletCardsCallback.java b/core/java/android/service/quickaccesswallet/GetWalletCardsCallback.java
index 9d210cd..f6a86d9 100644
--- a/core/java/android/service/quickaccesswallet/GetWalletCardsCallback.java
+++ b/core/java/android/service/quickaccesswallet/GetWalletCardsCallback.java
@@ -17,28 +17,11 @@
 package android.service.quickaccesswallet;
 
 import android.annotation.NonNull;
-import android.os.Handler;
-import android.os.RemoteException;
-import android.util.Log;
 
 /**
  * Handles response from the {@link QuickAccessWalletService} for {@link GetWalletCardsRequest}
  */
-public final class GetWalletCardsCallback {
-
-    private static final String TAG = "QAWalletCallback";
-
-    private final IQuickAccessWalletServiceCallbacks mCallback;
-    private final Handler mHandler;
-    private boolean mCalled;
-
-    /**
-     * @hide
-     */
-    GetWalletCardsCallback(IQuickAccessWalletServiceCallbacks callback, Handler handler) {
-        mCallback = callback;
-        mHandler = handler;
-    }
+public interface GetWalletCardsCallback {
 
     /**
      * Notifies the Android System that an {@link QuickAccessWalletService#onWalletCardsRequested}
@@ -46,11 +29,10 @@
      *
      * @param response The response contains the list of {@link WalletCard walletCards} to be shown
      *                 to the user as well as the index of the card that should initially be
-     *                 presented as the selected card.
+     *                 presented as the selected card. The list should not contain more than the
+     *                 maximum number of cards requested.
      */
-    public void onSuccess(@NonNull GetWalletCardsResponse response) {
-        mHandler.post(() -> onSuccessInternal(response));
-    }
+    void onSuccess(@NonNull GetWalletCardsResponse response);
 
     /**
      * Notifies the Android System that an {@link QuickAccessWalletService#onWalletCardsRequested}
@@ -60,33 +42,5 @@
      *              (Personally Identifiable Information, such as username or email address).
      * @throws IllegalStateException if this method or {@link #onSuccess} was already called.
      */
-    public void onFailure(@NonNull GetWalletCardsError error) {
-        mHandler.post(() -> onFailureInternal(error));
-    }
-
-    private void onSuccessInternal(GetWalletCardsResponse response) {
-        if (mCalled) {
-            Log.w(TAG, "already called");
-            return;
-        }
-        mCalled = true;
-        try {
-            mCallback.onGetWalletCardsSuccess(response);
-        } catch (RemoteException e) {
-            Log.e(TAG, "Error returning wallet cards", e);
-        }
-    }
-
-    private void onFailureInternal(GetWalletCardsError error) {
-        if (mCalled) {
-            Log.w(TAG, "already called");
-            return;
-        }
-        mCalled = true;
-        try {
-            mCallback.onGetWalletCardsFailure(error);
-        } catch (RemoteException e) {
-            Log.e(TAG, "Error returning failure message", e);
-        }
-    }
+    void onFailure(@NonNull GetWalletCardsError error);
 }
diff --git a/core/java/android/service/quickaccesswallet/GetWalletCardsCallbackImpl.java b/core/java/android/service/quickaccesswallet/GetWalletCardsCallbackImpl.java
new file mode 100644
index 0000000..d2494a5
--- /dev/null
+++ b/core/java/android/service/quickaccesswallet/GetWalletCardsCallbackImpl.java
@@ -0,0 +1,136 @@
+/*
+ * 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 android.service.quickaccesswallet;
+
+import android.annotation.NonNull;
+import android.graphics.Bitmap;
+import android.graphics.drawable.Icon;
+import android.os.Handler;
+import android.os.RemoteException;
+import android.text.TextUtils;
+import android.util.Log;
+
+import java.util.List;
+
+/**
+ * Handles response from the {@link QuickAccessWalletService} for {@link GetWalletCardsRequest}
+ *
+ * @hide
+ */
+final class GetWalletCardsCallbackImpl implements GetWalletCardsCallback {
+
+    private static final String TAG = "QAWalletCallback";
+
+    private final IQuickAccessWalletServiceCallbacks mCallback;
+    private final GetWalletCardsRequest mRequest;
+    private final Handler mHandler;
+    private boolean mCalled;
+
+    GetWalletCardsCallbackImpl(GetWalletCardsRequest request,
+            IQuickAccessWalletServiceCallbacks callback, Handler handler) {
+        mRequest = request;
+        mCallback = callback;
+        mHandler = handler;
+    }
+
+    /**
+     * Notifies the Android System that an {@link QuickAccessWalletService#onWalletCardsRequested}
+     * was successfully handled by the service.
+     *
+     * @param response The response contains the list of {@link WalletCard walletCards} to be shown
+     *                 to the user as well as the index of the card that should initially be
+     *                 presented as the selected card.
+     */
+    public void onSuccess(@NonNull GetWalletCardsResponse response) {
+        Log.i(TAG, "onSuccess");
+        if (isValidResponse(response)) {
+            mHandler.post(() -> onSuccessInternal(response));
+        } else {
+            Log.w(TAG, "Invalid GetWalletCards response");
+            mHandler.post(() -> onFailureInternal(new GetWalletCardsError(null, null)));
+        }
+    }
+
+    /**
+     * Notifies the Android System that an {@link QuickAccessWalletService#onWalletCardsRequested}
+     * could not be handled by the service.
+     *
+     * @param error The error message. <b>Note: </b> this message should <b>not</b> contain PII
+     *              (Personally Identifiable Information, such as username or email address).
+     * @throws IllegalStateException if this method or {@link #onSuccess} was already called.
+     */
+    public void onFailure(@NonNull GetWalletCardsError error) {
+        mHandler.post(() -> onFailureInternal(error));
+    }
+
+    private void onSuccessInternal(GetWalletCardsResponse response) {
+        Log.i(TAG, "onSuccessInternal");
+        if (mCalled) {
+            Log.w(TAG, "already called");
+            return;
+        }
+        mCalled = true;
+        try {
+            mCallback.onGetWalletCardsSuccess(response);
+            Log.i(TAG, "onSuccessInternal: returned response");
+        } catch (RemoteException e) {
+            Log.w(TAG, "Error returning wallet cards", e);
+        }
+    }
+
+    private void onFailureInternal(GetWalletCardsError error) {
+        if (mCalled) {
+            Log.w(TAG, "already called");
+            return;
+        }
+        mCalled = true;
+        try {
+            mCallback.onGetWalletCardsFailure(error);
+        } catch (RemoteException e) {
+            Log.e(TAG, "Error returning failure message", e);
+        }
+    }
+
+    private boolean isValidResponse(@NonNull GetWalletCardsResponse response) {
+        return response != null
+                && response.getWalletCards() != null
+                && response.getSelectedIndex() >= 0
+                && (response.getWalletCards().isEmpty() // selectedIndex may be 0 when list is empty
+                || response.getSelectedIndex() < response.getWalletCards().size())
+                && response.getWalletCards().size() < mRequest.getMaxCards()
+                && areValidCards(response.getWalletCards());
+    }
+
+    private boolean areValidCards(List<WalletCard> walletCards) {
+        for (WalletCard walletCard : walletCards) {
+            if (walletCard == null
+                    || walletCard.getCardId() == null
+                    || walletCard.getCardImage() == null
+                    || TextUtils.isEmpty(walletCard.getContentDescription())
+                    || walletCard.getPendingIntent() == null) {
+                return false;
+            }
+            Icon cardImage = walletCard.getCardImage();
+            if (cardImage.getType() == Icon.TYPE_BITMAP
+                    && walletCard.getCardImage().getBitmap().getConfig()
+                    != Bitmap.Config.HARDWARE) {
+                Log.w(TAG, "WalletCard bitmaps should be hardware bitmaps");
+            }
+        }
+        return true;
+    }
+}
diff --git a/core/java/android/service/quickaccesswallet/GetWalletCardsResponse.java b/core/java/android/service/quickaccesswallet/GetWalletCardsResponse.java
index 996622a..0551e27 100644
--- a/core/java/android/service/quickaccesswallet/GetWalletCardsResponse.java
+++ b/core/java/android/service/quickaccesswallet/GetWalletCardsResponse.java
@@ -35,9 +35,13 @@
     /**
      * Construct a new response.
      *
-     * @param walletCards   The list of wallet cards.
+     * @param walletCards   The list of wallet cards. The list may be empty but must NOT be larger
+     *                      than {@link GetWalletCardsRequest#getMaxCards()}. The list may not
+     *                      contain null values.
      * @param selectedIndex The index of the card that should be presented as the initially
-     *                      'selected' card
+     *                      'selected' card. The index must be greater than or equal to zero and
+     *                      less than the size of the list of walletCards (unless the list is empty
+     *                      in which case the value may be 0).
      */
     public GetWalletCardsResponse(@NonNull List<WalletCard> walletCards, int selectedIndex) {
         this.mWalletCards = walletCards;
diff --git a/core/java/android/service/quickaccesswallet/QuickAccessWalletClient.java b/core/java/android/service/quickaccesswallet/QuickAccessWalletClient.java
index cfc6d57..be9ab11 100644
--- a/core/java/android/service/quickaccesswallet/QuickAccessWalletClient.java
+++ b/core/java/android/service/quickaccesswallet/QuickAccessWalletClient.java
@@ -18,16 +18,16 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.annotation.TestApi;
 import android.content.Context;
 import android.content.Intent;
 
-import java.util.function.Consumer;
-
 /**
  * Facilitates accessing cards from the {@link QuickAccessWalletService}.
  *
  * @hide
  */
+@TestApi
 public interface QuickAccessWalletClient {
 
     /**
@@ -40,17 +40,65 @@
     }
 
     /**
-     * @return true if the {@link QuickAccessWalletService} is available.
+     * @return true if the {@link QuickAccessWalletService} is available. This means that the
+     * default NFC payment application has an exported service that can provide cards to the Quick
+     * Access Wallet. However, it does not mean that (1) the call will necessarily be successful,
+     * nor does it mean that cards may be displayed at this time. Addition checks are required:
+     * <ul>
+     *     <li>If {@link #isWalletFeatureAvailable()} is false, cards should not be displayed
+     *     <li>If the device is locked and {@link #isWalletFeatureAvailableWhenDeviceLocked} is
+     *     false, cards should not be displayed while the device remains locked. (A message
+     *     prompting the user to unlock to view cards may be appropriate).</li>
+     * </ul>
      */
     boolean isWalletServiceAvailable();
 
     /**
+     * Wallet cards should not be displayed if:
+     * <ul>
+     *     <li>The wallet service is unavailable</li>
+     *     <li>The device is not provisioned, ie user setup is incomplete</li>
+     *     <li>If the wallet feature has been disabled by the user</li>
+     *     <li>If the phone has been put into lockdown mode</li>
+     * </ul>
+     * <p>
+     * Quick Access Wallet implementers should call this method before calling
+     * {@link #getWalletCards} to ensure that cards may be displayed.
+     */
+    boolean isWalletFeatureAvailable();
+
+    /**
+     * Wallet cards may not be displayed on the lock screen if the user has opted to hide
+     * notifications or sensitive content on the lock screen.
+     * <ul>
+     *     <li>The device is not provisioned, ie user setup is incomplete</li>
+     *     <li>If the wallet feature has been disabled by the user</li>
+     *     <li>If the phone has been put into lockdown mode</li>
+     * </ul>
+     *
+     * <p>
+     * Quick Access Wallet implementers should call this method before calling
+     * {@link #getWalletCards} if the device is currently locked.
+     *
+     * @return true if cards may be displayed on the lock screen.
+     */
+    boolean isWalletFeatureAvailableWhenDeviceLocked();
+
+    /**
      * Get wallet cards from the {@link QuickAccessWalletService}.
      */
     void getWalletCards(
             @NonNull GetWalletCardsRequest request,
-            @NonNull Consumer<GetWalletCardsResponse> onSuccessListener,
-            @NonNull Consumer<GetWalletCardsError> onFailureListener);
+            @NonNull OnWalletCardsRetrievedCallback callback);
+
+    /**
+     * Callback for getWalletCards
+     */
+    interface OnWalletCardsRetrievedCallback {
+        void onWalletCardsRetrieved(@NonNull GetWalletCardsResponse response);
+
+        void onWalletCardRetrievalError(@NonNull GetWalletCardsError error);
+    }
 
     /**
      * Notify the {@link QuickAccessWalletService} service that a wallet card was selected.
@@ -65,12 +113,24 @@
     /**
      * Unregister event listener.
      */
-    void registerWalletServiceEventListener(Consumer<WalletServiceEvent> listener);
+    void addWalletServiceEventListener(@NonNull WalletServiceEventListener listener);
 
     /**
      * Unregister event listener
      */
-    void unregisterWalletServiceEventListener(Consumer<WalletServiceEvent> listener);
+    void removeWalletServiceEventListener(@NonNull WalletServiceEventListener listener);
+
+    /**
+     * A listener for {@link WalletServiceEvent walletServiceEvents}
+     */
+    interface WalletServiceEventListener {
+        void onWalletServiceEvent(@NonNull WalletServiceEvent event);
+    }
+
+    /**
+     * Unregister all event listeners and disconnect from the service.
+     */
+    void disconnect();
 
     /**
      * The manifest entry for the QuickAccessWalletService may also publish information about the
@@ -78,12 +138,12 @@
      * application.
      */
     @Nullable
-    Intent getWalletActivity();
+    Intent createWalletIntent();
 
     /**
      * The manifest entry for the {@link QuickAccessWalletService} may publish the activity that
      * hosts the settings
      */
     @Nullable
-    Intent getSettingsActivity();
+    Intent createWalletSettingsIntent();
 }
diff --git a/core/java/android/service/quickaccesswallet/QuickAccessWalletClientImpl.java b/core/java/android/service/quickaccesswallet/QuickAccessWalletClientImpl.java
index 17c287f..37a8703 100644
--- a/core/java/android/service/quickaccesswallet/QuickAccessWalletClientImpl.java
+++ b/core/java/android/service/quickaccesswallet/QuickAccessWalletClientImpl.java
@@ -20,6 +20,7 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.app.ActivityManager;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
@@ -27,79 +28,225 @@
 import android.os.Handler;
 import android.os.IBinder;
 import android.os.Looper;
-import android.os.Message;
 import android.os.RemoteException;
+import android.os.UserHandle;
+import android.provider.Settings;
 import android.text.TextUtils;
 import android.util.Log;
 
+import com.android.internal.widget.LockPatternUtils;
+
 import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.LinkedList;
 import java.util.Map;
 import java.util.Queue;
 import java.util.UUID;
-import java.util.function.Consumer;
 
 /**
+ * Implements {@link QuickAccessWalletClient}. The client connects, performs requests, waits for
+ * responses, and disconnects automatically after a short period of time. The client may
  * @hide
  */
-@SuppressWarnings("AndroidJdkLibsChecker")
-class QuickAccessWalletClientImpl implements QuickAccessWalletClient, Handler.Callback,
-        ServiceConnection {
+public class QuickAccessWalletClientImpl implements QuickAccessWalletClient, ServiceConnection {
 
     private static final String TAG = "QAWalletSClient";
     private final Handler mHandler;
     private final Context mContext;
     private final Queue<ApiCaller> mRequestQueue;
-    private final Map<Consumer<WalletServiceEvent>, String> mEventListeners;
+    private final Map<WalletServiceEventListener, String> mEventListeners;
     private boolean mIsConnected;
+    /**
+     * Timeout for active service connections (1 minute)
+     */
+    private static final long SERVICE_CONNECTION_TIMEOUT_MS = 60 * 1000;
     @Nullable
     private IQuickAccessWalletService mService;
 
-
     @Nullable
     private final QuickAccessWalletServiceInfo mServiceInfo;
 
-    private static final int MSG_CONNECT = 1;
-    private static final int MSG_CONNECTED = 2;
-    private static final int MSG_EXECUTE = 3;
-    private static final int MSG_DISCONNECT = 4;
+    private static final int MSG_TIMEOUT_SERVICE = 5;
 
     QuickAccessWalletClientImpl(@NonNull Context context) {
         mContext = context.getApplicationContext();
         mServiceInfo = QuickAccessWalletServiceInfo.tryCreate(context);
-        mHandler = new Handler(Looper.getMainLooper(), this);
+        mHandler = new Handler(Looper.getMainLooper());
         mRequestQueue = new LinkedList<>();
         mEventListeners = new HashMap<>(1);
     }
 
     @Override
-    public boolean handleMessage(Message msg) {
-        switch (msg.what) {
-            case MSG_CONNECT:
-                connectInternal();
-                break;
-            case MSG_CONNECTED:
-                onConnectedInternal((IQuickAccessWalletService) msg.obj);
-                break;
-            case MSG_EXECUTE:
-                executeInternal((ApiCaller) msg.obj);
-                break;
-            case MSG_DISCONNECT:
-                disconnectInternal();
-                break;
-            default:
-                Log.w(TAG, "Unknown what: " + msg.what);
-                return false;
+    public boolean isWalletServiceAvailable() {
+        boolean available = mServiceInfo != null;
+        Log.i(TAG, "isWalletServiceAvailable: " + available);
+        return available;
+    }
+
+    @Override
+    public boolean isWalletFeatureAvailable() {
+        int currentUser = ActivityManager.getCurrentUser();
+        return checkUserSetupComplete()
+                && checkSecureSetting(Settings.Secure.GLOBAL_ACTIONS_PANEL_ENABLED)
+                && !new LockPatternUtils(mContext).isUserInLockdown(currentUser);
+    }
+
+    @Override
+    public boolean isWalletFeatureAvailableWhenDeviceLocked() {
+        return checkSecureSetting(Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS)
+                && checkSecureSetting(Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS);
+    }
+
+    @Override
+    public void getWalletCards(
+            @NonNull GetWalletCardsRequest request,
+            @NonNull OnWalletCardsRetrievedCallback callback) {
+
+        Log.i(TAG, "getWalletCards");
+
+        if (!isWalletServiceAvailable()) {
+            callback.onWalletCardRetrievalError(new GetWalletCardsError(null, null));
+            return;
         }
-        return true;
+
+        BaseCallbacks serviceCallback = new BaseCallbacks() {
+            @Override
+            public void onGetWalletCardsSuccess(GetWalletCardsResponse response) {
+                mHandler.post(() -> callback.onWalletCardsRetrieved(response));
+            }
+
+            @Override
+            public void onGetWalletCardsFailure(GetWalletCardsError error) {
+                mHandler.post(() -> callback.onWalletCardRetrievalError(error));
+            }
+        };
+
+        executeApiCall(new ApiCaller("onWalletCardsRequested") {
+            @Override
+            public void performApiCall(IQuickAccessWalletService service) throws RemoteException {
+                service.onWalletCardsRequested(request, serviceCallback);
+            }
+
+            @Override
+            public void onApiError() {
+                serviceCallback.onGetWalletCardsFailure(new GetWalletCardsError(null, null));
+            }
+        });
+    }
+
+    @Override
+    public void selectWalletCard(@NonNull SelectWalletCardRequest request) {
+        Log.i(TAG, "selectWalletCard");
+        if (!isWalletServiceAvailable()) {
+            return;
+        }
+        executeApiCall(new ApiCaller("onWalletCardSelected") {
+            @Override
+            public void performApiCall(IQuickAccessWalletService service) throws RemoteException {
+                service.onWalletCardSelected(request);
+            }
+        });
+    }
+
+    @Override
+    public void notifyWalletDismissed() {
+        if (!isWalletServiceAvailable()) {
+            return;
+        }
+        Log.i(TAG, "notifyWalletDismissed");
+        executeApiCall(new ApiCaller("onWalletDismissed") {
+            @Override
+            public void performApiCall(IQuickAccessWalletService service) throws RemoteException {
+                service.onWalletDismissed();
+            }
+        });
+    }
+
+    @Override
+    public void addWalletServiceEventListener(WalletServiceEventListener listener) {
+        if (!isWalletServiceAvailable()) {
+            return;
+        }
+        Log.i(TAG, "registerWalletServiceEventListener");
+        BaseCallbacks callback = new BaseCallbacks() {
+            @Override
+            public void onWalletServiceEvent(WalletServiceEvent event) {
+                Log.i(TAG, "onWalletServiceEvent");
+                mHandler.post(() -> listener.onWalletServiceEvent(event));
+            }
+        };
+
+        executeApiCall(new ApiCaller("registerListener") {
+            @Override
+            public void performApiCall(IQuickAccessWalletService service) throws RemoteException {
+                String listenerId = UUID.randomUUID().toString();
+                WalletServiceEventListenerRequest request =
+                        new WalletServiceEventListenerRequest(listenerId);
+                mEventListeners.put(listener, listenerId);
+                service.registerWalletServiceEventListener(request, callback);
+            }
+        });
+    }
+
+    @Override
+    public void removeWalletServiceEventListener(WalletServiceEventListener listener) {
+        if (!isWalletServiceAvailable()) {
+            return;
+        }
+        Log.i(TAG, "unregisterWalletServiceEventListener");
+        executeApiCall(new ApiCaller("unregisterListener") {
+            @Override
+            public void performApiCall(IQuickAccessWalletService service) throws RemoteException {
+                String listenerId = mEventListeners.remove(listener);
+                if (listenerId == null) {
+                    return;
+                }
+                WalletServiceEventListenerRequest request =
+                        new WalletServiceEventListenerRequest(listenerId);
+                service.unregisterWalletServiceEventListener(request);
+            }
+        });
+    }
+
+    @Override
+    public void disconnect() {
+        Log.i(TAG, "disconnect");
+        mHandler.post(() -> disconnectInternal(true));
+    }
+
+    @Override
+    @Nullable
+    public Intent createWalletIntent() {
+        if (mServiceInfo == null || TextUtils.isEmpty(mServiceInfo.getWalletActivity())) {
+            return null;
+        }
+        return new Intent(QuickAccessWalletService.ACTION_VIEW_WALLET)
+                .setComponent(
+                        new ComponentName(
+                                mServiceInfo.getComponentName().getPackageName(),
+                                mServiceInfo.getWalletActivity()));
+    }
+
+    @Override
+    @Nullable
+    public Intent createWalletSettingsIntent() {
+        if (mServiceInfo == null || TextUtils.isEmpty(mServiceInfo.getSettingsActivity())) {
+            return null;
+        }
+        return new Intent(QuickAccessWalletService.ACTION_VIEW_WALLET_SETTINGS)
+                .setComponent(
+                        new ComponentName(
+                                mServiceInfo.getComponentName().getPackageName(),
+                                mServiceInfo.getSettingsActivity()));
     }
 
     private void connect() {
-        mHandler.sendMessage(mHandler.obtainMessage(MSG_CONNECT));
+        Log.i(TAG, "connect");
+        mHandler.post(this::connectInternal);
     }
 
     private void connectInternal() {
+        Log.i(TAG, "connectInternal");
         if (mServiceInfo == null) {
             Log.w(TAG, "Wallet service unavailable");
             return;
@@ -113,15 +260,18 @@
         intent.setComponent(mServiceInfo.getComponentName());
         int flags = Context.BIND_AUTO_CREATE | Context.BIND_WAIVE_PRIORITY;
         mContext.bindService(intent, this, flags);
+        resetServiceConnectionTimeout();
     }
 
     private void onConnectedInternal(IQuickAccessWalletService service) {
+        Log.i(TAG, "onConnectedInternal");
         if (!mIsConnected) {
             Log.w(TAG, "onConnectInternal but connection closed");
             mService = null;
             return;
         }
         mService = service;
+        Log.i(TAG, "onConnectedInternal success: request queue size " + mRequestQueue.size());
         for (ApiCaller apiCaller : new ArrayList<>(mRequestQueue)) {
             try {
                 apiCaller.performApiCall(mService);
@@ -135,15 +285,33 @@
         }
     }
 
-    private void disconnect() {
-        mHandler.sendMessage(mHandler.obtainMessage(MSG_DISCONNECT));
+    /**
+     * Resets the idle timeout for this connection by removing any pending timeout messages and
+     * posting a new delayed message.
+     */
+    private void resetServiceConnectionTimeout() {
+        Log.i(TAG, "resetServiceConnectionTimeout");
+        mHandler.removeMessages(MSG_TIMEOUT_SERVICE);
+        mHandler.postDelayed(
+                () -> disconnectInternal(true),
+                MSG_TIMEOUT_SERVICE,
+                SERVICE_CONNECTION_TIMEOUT_MS);
     }
 
-    private void disconnectInternal() {
+    private void disconnectInternal(boolean clearEventListeners) {
+        Log.i(TAG, "disconnectInternal: " + clearEventListeners);
         if (!mIsConnected) {
             Log.w(TAG, "already disconnected");
             return;
         }
+        if (clearEventListeners && !mEventListeners.isEmpty()) {
+            Log.i(TAG, "disconnectInternal: clear event listeners");
+            for (WalletServiceEventListener listener : mEventListeners.keySet()) {
+                removeWalletServiceEventListener(listener);
+            }
+            mHandler.post(() -> disconnectInternal(false));
+            return;
+        }
         mIsConnected = false;
         mContext.unbindService(/*conn=*/this);
         mService = null;
@@ -151,181 +319,81 @@
         mRequestQueue.clear();
     }
 
-    private void execute(ApiCaller apiCaller) {
-        mHandler.sendMessage(mHandler.obtainMessage(MSG_EXECUTE, apiCaller));
+    private void executeApiCall(ApiCaller apiCaller) {
+        Log.i(TAG, "execute: " + apiCaller.mDesc);
+        mHandler.post(() -> executeInternal(apiCaller));
     }
 
     private void executeInternal(ApiCaller apiCall) {
+        Log.i(TAG, "executeInternal: " + apiCall.mDesc);
         if (mIsConnected && mService != null) {
             try {
                 apiCall.performApiCall(mService);
+                Log.i(TAG, "executeInternal success: " + apiCall.mDesc);
+                resetServiceConnectionTimeout();
             } catch (RemoteException e) {
-                Log.w(TAG, "executeInternal error", e);
+                Log.w(TAG, "executeInternal error: " + apiCall.mDesc, e);
                 apiCall.onApiError();
                 disconnect();
             }
         } else {
+            Log.i(TAG, "executeInternal: queued" + apiCall.mDesc);
             mRequestQueue.add(apiCall);
             connect();
         }
     }
 
-    public boolean isWalletServiceAvailable() {
-        return mServiceInfo != null;
-    }
-
     private abstract static class ApiCaller {
+        private final String mDesc;
+
+        private ApiCaller(String desc) {
+            this.mDesc = desc;
+        }
+
         abstract void performApiCall(IQuickAccessWalletService service) throws RemoteException;
 
         void onApiError() {
-            Log.w(TAG, "api error");
+            Log.w(TAG, "api error: " + mDesc);
         }
     }
 
-    public void getWalletCards(
-            @NonNull GetWalletCardsRequest request,
-            @NonNull Consumer<GetWalletCardsResponse> onSuccessListener,
-            @NonNull Consumer<GetWalletCardsError> onFailureListener) {
-
-        BaseCallbacks callback = new BaseCallbacks() {
-            @Override
-            public void onGetWalletCardsSuccess(GetWalletCardsResponse response) {
-                mHandler.post(() -> onSuccessListener.accept(response));
-            }
-
-            @Override
-            public void onGetWalletCardsFailure(GetWalletCardsError error) {
-                mHandler.post(() -> onFailureListener.accept(error));
-            }
-        };
-
-        execute(new ApiCaller() {
-            @Override
-            public void performApiCall(IQuickAccessWalletService service) throws RemoteException {
-                service.onWalletCardsRequested(request, callback);
-            }
-
-            @Override
-            public void onApiError() {
-                callback.onGetWalletCardsFailure(new GetWalletCardsError(null, null));
-            }
-        });
-    }
-
-    public void selectWalletCard(@NonNull SelectWalletCardRequest request) {
-        execute(new ApiCaller() {
-            @Override
-            public void performApiCall(IQuickAccessWalletService service) throws RemoteException {
-                service.onWalletCardSelected(request);
-            }
-        });
-    }
-
-    public void notifyWalletDismissed() {
-        execute(new ApiCaller() {
-            @Override
-            public void performApiCall(IQuickAccessWalletService service) throws RemoteException {
-                service.onWalletDismissed();
-                mHandler.sendMessage(mHandler.obtainMessage(MSG_DISCONNECT));
-            }
-        });
-    }
-
-    @Override
-    public void registerWalletServiceEventListener(Consumer<WalletServiceEvent> listener) {
-
-        BaseCallbacks callback = new BaseCallbacks() {
-            @Override
-            public void onWalletServiceEvent(WalletServiceEvent event) {
-                Log.i(TAG, "onWalletServiceEvent");
-                mHandler.post(() -> listener.accept(event));
-            }
-        };
-
-        execute(new ApiCaller() {
-            @Override
-            public void performApiCall(IQuickAccessWalletService service) throws RemoteException {
-                String listenerId = UUID.randomUUID().toString();
-                WalletServiceEventListenerRequest request =
-                        new WalletServiceEventListenerRequest(listenerId);
-                mEventListeners.put(listener, listenerId);
-                service.registerWalletServiceEventListener(request, callback);
-            }
-        });
-    }
-
-    @Override
-    public void unregisterWalletServiceEventListener(Consumer<WalletServiceEvent> listener) {
-        execute(new ApiCaller() {
-            @Override
-            public void performApiCall(IQuickAccessWalletService service) throws RemoteException {
-                String listenerId = mEventListeners.get(listener);
-                if (listenerId == null) {
-                    return;
-                }
-                WalletServiceEventListenerRequest request =
-                        new WalletServiceEventListenerRequest(listenerId);
-                service.unregisterWalletServiceEventListener(request);
-            }
-        });
-    }
-
-    @Override
-    @Nullable
-    public Intent getWalletActivity() {
-        if (mServiceInfo == null || TextUtils.isEmpty(mServiceInfo.getWalletActivity())) {
-            return null;
-        }
-        return new Intent(QuickAccessWalletService.ACTION_VIEW_WALLET)
-                .setComponent(
-                        new ComponentName(
-                                mServiceInfo.getComponentName().getPackageName(),
-                                mServiceInfo.getWalletActivity()));
-    }
-
-    @Override
-    @Nullable
-    public Intent getSettingsActivity() {
-        if (mServiceInfo == null || TextUtils.isEmpty(mServiceInfo.getSettingsActivity())) {
-            return null;
-        }
-        return new Intent(QuickAccessWalletService.ACTION_VIEW_WALLET_SETTINGS)
-                .setComponent(
-                        new ComponentName(
-                                mServiceInfo.getComponentName().getPackageName(),
-                                mServiceInfo.getSettingsActivity()));
-    }
-
-    /**
-     * Connection to the {@link QuickAccessWalletService}
-     */
-
-
-    @Override
+    @Override // ServiceConnection
     public void onServiceConnected(ComponentName name, IBinder binder) {
+        Log.i(TAG, "onServiceConnected: " + name);
         IQuickAccessWalletService service = IQuickAccessWalletService.Stub.asInterface(binder);
-        mHandler.sendMessage(mHandler.obtainMessage(MSG_CONNECTED, service));
+        mHandler.post(() -> onConnectedInternal(service));
     }
 
-    @Override
+    @Override // ServiceConnection
     public void onServiceDisconnected(ComponentName name) {
         // Do not disconnect, as we may later be re-connected
         Log.w(TAG, "onServiceDisconnected");
     }
 
-    @Override
+    @Override // ServiceConnection
     public void onBindingDied(ComponentName name) {
         // This is a recoverable error but the client will need to reconnect.
         Log.w(TAG, "onBindingDied");
         disconnect();
     }
 
-    @Override
+    @Override // ServiceConnection
     public void onNullBinding(ComponentName name) {
         Log.w(TAG, "onNullBinding");
         disconnect();
     }
 
+    private boolean checkSecureSetting(String name) {
+        return Settings.Secure.getInt(mContext.getContentResolver(), name, 0) == 1;
+    }
+
+    private boolean checkUserSetupComplete() {
+        return Settings.Secure.getIntForUser(
+                mContext.getContentResolver(),
+                Settings.Secure.USER_SETUP_COMPLETE, 0,
+                UserHandle.USER_CURRENT) == 1;
+    }
+
     private static class BaseCallbacks extends IQuickAccessWalletServiceCallbacks.Stub {
         public void onGetWalletCardsSuccess(GetWalletCardsResponse response) {
             throw new IllegalStateException();
diff --git a/core/java/android/service/quickaccesswallet/QuickAccessWalletService.java b/core/java/android/service/quickaccesswallet/QuickAccessWalletService.java
index d968405..aba5611 100644
--- a/core/java/android/service/quickaccesswallet/QuickAccessWalletService.java
+++ b/core/java/android/service/quickaccesswallet/QuickAccessWalletService.java
@@ -45,8 +45,9 @@
  *   android.permission.BIND_NFC_SERVICE permission.
  *   <li>The user explicitly selected the application as the default payment application in
  *   the Tap &amp; pay settings screen.
- *   <li>The application requires the {@code android.permission.BIND_QUICK_ACCESS_WALLET_SERVICE}
- *   permission in its manifest.
+ *   <li>The QuickAccessWalletService requires that the binding application hold the
+ *   {@code android.permission.BIND_QUICK_ACCESS_WALLET_SERVICE} permission, which only the System
+ *   Service can hold.
  *   <li>The user explicitly enables it using Android Settings (the
  *       {@link Settings#ACTION_QUICK_ACCESS_WALLET_SETTINGS} intent can be used to launch it).
  * </ol>
@@ -73,6 +74,11 @@
  *   a new card is selected, the Android System will notify the service through
  *   {@link #onWalletCardSelected} and will provide the {@link WalletCard#getCardId() cardId} of the
  *   card that is now selected.
+ *   <li>If the user commences an NFC payment, the service may send a {@link WalletServiceEvent}
+ *   to the System indicating that the wallet application now needs to show the activity associated
+ *   with making a payment. Sending a {@link WalletServiceEvent} of type
+ *   {@link WalletServiceEvent#TYPE_NFC_PAYMENT_STARTED} should cause the quick access wallet UI
+ *   to be dismissed.
  *   <li>When the wallet is dismissed, the Android System will notify the service through
  *   {@link #onWalletDismissed}.
  * </ol>
@@ -110,6 +116,7 @@
  *     android:permission="android.permission.BIND_QUICK_ACCESS_WALLET_SERVICE">
  *     <intent-filter>
  *         <action android:name="android.service.quickaccesswallet.QuickAccessWalletService" />
+ *         <category android:name="android.intent.category.DEFAULT"/>
  *     </intent-filter>
  *     <meta-data android:name="android.quickaccesswallet"
  *          android:resource="@xml/quickaccesswallet_configuration" />;
@@ -166,29 +173,6 @@
             "android.service.quickaccesswallet.action.VIEW_WALLET_SETTINGS";
 
     /**
-     * Broadcast Action: Sent by the wallet application to dismiss the Quick Access Wallet.
-     * <p>
-     * The Quick Access Wallet may be shown in a system window on top of other Activities. If the
-     * user selects a payment card from the Quick Access Wallet and then holds their phone to an NFC
-     * terminal, the wallet application will need to show a payment Activity. But if the Quick
-     * Access Wallet is still being shown, it may obscure the payment Activity. To avoid this, the
-     * wallet application can send a broadcast to the Android System with this action to request
-     * that the Quick Access Wallet be dismissed.
-     * <p>
-     * This broadcast must use the {@code android.permission.BIND_QUICK_ACCESS_WALLET_SERVICE}
-     * permission to ensure that it is only delivered to System UI. Furthermore, your application
-     * must require the {@code android.permission.DISMISS_QUICK_ACCESS_WALLET}
-     * <p>
-     * <pre class="prettyprint">
-     * context.sendBroadcast(
-     *     new Intent(ACTION_DISMISS_WALLET), Manifest.permission.BIND_QUICK_ACCESS_WALLET_SERVICE);
-     * </pre>
-     */
-    @SdkConstant(SdkConstant.SdkConstantType.BROADCAST_INTENT_ACTION)
-    public static final String ACTION_DISMISS_WALLET =
-            "android.service.quickaccesswallet.action.DISMISS_WALLET";
-
-    /**
      * Name under which a QuickAccessWalletService component publishes information about itself.
      * This meta-data should reference an XML resource containing a
      * <code>&lt;{@link
@@ -202,8 +186,20 @@
     public static final String SERVICE_META_DATA = "android.quickaccesswallet";
 
     private final Handler mHandler = new Handler(Looper.getMainLooper());
+
+    /**
+     * The service currently only supports one listener at a time. Multiple connections that
+     * register different listeners will clobber the listener. This field may only be accessed from
+     * the main thread.
+     */
     @Nullable
     private String mEventListenerId;
+
+    /**
+     * The service currently only supports one listener at a time. Multiple connections that
+     * register different listeners will clobber the listener. This field may only be accessed from
+     * the main thread.
+     */
     @Nullable
     private IQuickAccessWalletServiceCallbacks mEventListener;
 
@@ -240,7 +236,8 @@
     private void onWalletCardsRequestedInternal(
             GetWalletCardsRequest request,
             IQuickAccessWalletServiceCallbacks callback) {
-        onWalletCardsRequested(request, new GetWalletCardsCallback(callback, mHandler));
+        onWalletCardsRequested(request,
+                new GetWalletCardsCallbackImpl(request, callback, mHandler));
     }
 
     @Override
@@ -250,14 +247,14 @@
             // Binding to the QuickAccessWalletService is protected by the
             // android.permission.BIND_QUICK_ACCESS_WALLET_SERVICE permission, which is defined in
             // R. Pre-R devices can have other side-loaded applications that claim this permission.
-            // This ensures that the service is only available when properly permission protected.
+            // Ensures that the service is only enabled when properly permission protected.
             Log.w(TAG, "Warning: binding on pre-R device");
         }
-        if (SERVICE_INTERFACE.equals(intent.getAction())) {
-            return mInterface.asBinder();
+        if (!SERVICE_INTERFACE.equals(intent.getAction())) {
+            Log.w(TAG, "Wrong action");
+            return null;
         }
-        Log.w(TAG, "Tried to bind to wrong intent (should be " + SERVICE_INTERFACE + ": " + intent);
-        return null;
+        return mInterface.asBinder();
     }
 
     /**
diff --git a/core/java/android/service/quickaccesswallet/QuickAccessWalletServiceInfo.java b/core/java/android/service/quickaccesswallet/QuickAccessWalletServiceInfo.java
index 8793f28..23173a8 100644
--- a/core/java/android/service/quickaccesswallet/QuickAccessWalletServiceInfo.java
+++ b/core/java/android/service/quickaccesswallet/QuickAccessWalletServiceInfo.java
@@ -77,8 +77,9 @@
         }
 
         if (!Manifest.permission.BIND_QUICK_ACCESS_WALLET_SERVICE.equals(serviceInfo.permission)) {
-            Log.w(TAG, String.format("QuickAccessWalletService from %s does not have permission %s",
-                    serviceInfo.packageName, Manifest.permission.BIND_QUICK_ACCESS_WALLET_SERVICE));
+            Log.w(TAG, String.format("%s.%s does not require permission %s",
+                    serviceInfo.packageName, serviceInfo.name,
+                    Manifest.permission.BIND_QUICK_ACCESS_WALLET_SERVICE));
             return null;
         }
 
diff --git a/core/java/android/service/quickaccesswallet/WalletCard.java b/core/java/android/service/quickaccesswallet/WalletCard.java
index c3b1a4b..e6ae0ab 100644
--- a/core/java/android/service/quickaccesswallet/WalletCard.java
+++ b/core/java/android/service/quickaccesswallet/WalletCard.java
@@ -174,22 +174,25 @@
          * @param cardId             The card id must be non-null and unique within the list of
          *                           cards returned. <b>Note:
          *                           </b> this card ID should <b>not</b> contain PII (Personally
-         *                           Identifiable Information, * such as username or email
-         *                           address).
+         *                           Identifiable Information, such as username or email address).
          * @param cardImage          The visual representation of the card. If the card image Icon
          *                           is a bitmap, it should have a width of {@link
          *                           GetWalletCardsRequest#getCardWidthPx()} and a height of {@link
          *                           GetWalletCardsRequest#getCardHeightPx()}. If the card image
          *                           does not have these dimensions, it may appear distorted when it
-         *                           is scaled to fit these dimensions on screen.
+         *                           is scaled to fit these dimensions on screen. Bitmaps should be
+         *                           of type {@link android.graphics.Bitmap.Config#HARDWARE} for
+         *                           performance reasons.
          * @param contentDescription The content description of the card image. This field is
-         *                           required.
+         *                           required and may not be null or empty.
          *                           <b>Note: </b> this message should <b>not</b> contain PII
          *                           (Personally Identifiable Information, such as username or email
          *                           address).
          * @param pendingIntent      If the user performs a click on the card, this PendingIntent
          *                           will be sent. If the device is locked, the wallet will first
-         *                           request device unlock before sending the pending intent.
+         *                           request device unlock before sending the pending intent. It is
+         *                           recommended that the pending intent be immutable (use {@link
+         *                           PendingIntent#FLAG_IMMUTABLE}).
          */
         public Builder(@NonNull String cardId,
                 @NonNull Icon cardImage,
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index b971a20..688517d 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -1705,12 +1705,13 @@
     }
 
     Surface getOrCreateBLASTSurface(int width, int height) {
-        if (mSurfaceControl == null || !mSurfaceControl.isValid()) {
+        if (mSurfaceControl == null
+                || !mSurfaceControl.isValid()
+                || mBlastSurfaceControl == null
+                || !mBlastSurfaceControl.isValid()) {
             return null;
         }
-        if ((mBlastSurfaceControl != null)
-                && (mBlastBufferQueue == null)
-                && mBlastSurfaceControl.isValid()) {
+        if (mBlastBufferQueue == null) {
             mBlastBufferQueue = new BLASTBufferQueue(
                 mBlastSurfaceControl, width, height);
         }
diff --git a/core/java/android/view/autofill/AutofillManager.java b/core/java/android/view/autofill/AutofillManager.java
index e9a8a73..54bdb88 100644
--- a/core/java/android/view/autofill/AutofillManager.java
+++ b/core/java/android/view/autofill/AutofillManager.java
@@ -3366,14 +3366,8 @@
             final AutofillManager afm = mAfm.get();
             if (afm == null) return null;
 
-            final AutofillClient client = afm.getClient();
-            if (client == null) {
-                Log.w(TAG, "getViewCoordinates(" + id + "): no autofill client");
-                return null;
-            }
-            final View view = client.autofillClientFindViewByAutofillIdTraversal(id);
+            final View view = getView(afm, id);
             if (view == null) {
-                Log.w(TAG, "getViewCoordinates(" + id + "): could not find view");
                 return null;
             }
             final Rect windowVisibleDisplayFrame = new Rect();
@@ -3414,5 +3408,42 @@
                 afm.post(() -> afm.requestHideFillUi(id, false));
             }
         }
+
+        @Override
+        public boolean requestAutofill(int sessionId, AutofillId id) {
+            final AutofillManager afm = mAfm.get();
+            if (afm == null || afm.mSessionId != sessionId) {
+                if (sDebug) {
+                    Slog.d(TAG, "Autofill not available or sessionId doesn't match");
+                }
+                return false;
+            }
+            final View view = getView(afm, id);
+            if (view == null || !view.isFocused()) {
+                if (sDebug) {
+                    Slog.d(TAG, "View not available or is not on focus");
+                }
+                return false;
+            }
+            if (sVerbose) {
+                Log.v(TAG, "requestAutofill() by AugmentedAutofillService.");
+            }
+            afm.post(() -> afm.requestAutofill(view));
+            return true;
+        }
+
+        @Nullable
+        private View getView(@NonNull AutofillManager afm, @NonNull AutofillId id) {
+            final AutofillClient client = afm.getClient();
+            if (client == null) {
+                Log.w(TAG, "getView(" + id + "): no autofill client");
+                return null;
+            }
+            View view = client.autofillClientFindViewByAutofillIdTraversal(id);
+            if (view == null) {
+                Log.w(TAG, "getView(" + id + "): could not find view");
+            }
+            return view;
+        }
     }
 }
diff --git a/core/java/android/view/autofill/IAugmentedAutofillManagerClient.aidl b/core/java/android/view/autofill/IAugmentedAutofillManagerClient.aidl
index 140507c..03054df 100644
--- a/core/java/android/view/autofill/IAugmentedAutofillManagerClient.aidl
+++ b/core/java/android/view/autofill/IAugmentedAutofillManagerClient.aidl
@@ -50,4 +50,10 @@
       * Requests hiding the fill UI.
       */
     void requestHideFillUi(int sessionId, in AutofillId id);
+
+    /**
+      * Requests to start a new autofill flow. Returns true if the autofill request is made to
+      * {@link AutofillManager#requestAutofill(View)}.
+      */
+    boolean requestAutofill(int sessionId, in AutofillId id);
 }
diff --git a/core/res/res/layout/autofill_inline_suggestion.xml b/core/res/res/layout/autofill_inline_suggestion.xml
deleted file mode 100644
index 27faea4..0000000
--- a/core/res/res/layout/autofill_inline_suggestion.xml
+++ /dev/null
@@ -1,69 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- 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.
--->
-
-<LinearLayout
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    style="?android:attr/autofillInlineSuggestionChip"
-    android:layout_width="wrap_content"
-    android:layout_height="wrap_content"
-    android:gravity="center"
-    android:paddingTop="4dp"
-    android:paddingBottom="4dp"
-    android:orientation="horizontal">
-
-    <ImageView
-        android:id="@+id/autofill_inline_suggestion_start_icon"
-        android:layout_width="wrap_content"
-        android:layout_height="match_parent"
-        android:layout_gravity="center"
-        android:scaleType="fitCenter"
-        android:contentDescription="autofill_inline_suggestion_start_icon" />
-
-    <LinearLayout
-        android:layout_width="0dp"
-        android:layout_height="match_parent"
-        android:layout_gravity="center"
-        android:layout_weight="1"
-        android:paddingStart="4dp"
-        android:paddingEnd="4dp"
-        android:orientation="vertical"
-        android:gravity="center">
-
-        <TextView
-            style="?android:attr/autofillInlineSuggestionTitle"
-            android:id="@+id/autofill_inline_suggestion_title"
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:ellipsize="end"
-            android:maxLines="1"/>
-
-        <TextView
-            style="?android:attr/autofillInlineSuggestionSubtitle"
-            android:id="@+id/autofill_inline_suggestion_subtitle"
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:ellipsize="end"
-            android:maxLines="1"/>
-    </LinearLayout>
-
-    <ImageView
-        android:id="@+id/autofill_inline_suggestion_end_icon"
-        android:layout_width="wrap_content"
-        android:layout_height="match_parent"
-        android:layout_gravity="center"
-        android:scaleType="fitCenter"
-        android:contentDescription="autofill_inline_suggestion_end_icon" />
-</LinearLayout>
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index 7fc041c..9cdd3ed 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -9207,12 +9207,4 @@
     </declare-styleable>
 
     <attr name="autoSizePresetSizes" />
-
-    <declare-styleable name="AutofillInlineSuggestion">
-        <!-- @hide @SystemApi -->
-        <attr name="isAutofillInlineSuggestionTheme" format="boolean" />
-        <attr name="autofillInlineSuggestionChip" format="reference" />
-        <attr name="autofillInlineSuggestionTitle" format="reference" />
-        <attr name="autofillInlineSuggestionSubtitle" format="reference" />
-    </declare-styleable>
 </resources>
diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml
index e231c3a..16bed503 100644
--- a/core/res/res/values/attrs_manifest.xml
+++ b/core/res/res/values/attrs_manifest.xml
@@ -1770,6 +1770,21 @@
              -->
         <attr name="requestLegacyExternalStorage" format="boolean" />
 
+        <!-- If {@code true} this app would like to preserve the legacy storage
+             model from a previously installed version. Note that this may not always be
+             respected due to policy or backwards compatibility reasons.
+
+             <p>This has no effect on the first install of an app on a device.
+             For an updating app, setting this to {@code true} will preserve the legacy behaviour
+             configured by the {@code requestLegacyExternalStorage} flag. If on an update, this
+             flag is set to {@code false} then the legacy access is not preserved, such an app can
+             only have legacy access with the {@code requestLegacyExternalStorage} flag.
+             <p>
+
+             The default value is {@code false}.
+             -->
+        <attr name="preserveLegacyExternalStorage" format="boolean" />
+
         <!-- If {@code true} this app declares that it should be visible to all other apps on
              device, regardless of what they declare via the {@code queries} tags in their
              manifest.
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index 0edad3b..80e5d1b 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -3012,19 +3012,14 @@
       <public name="sdkVersion" />
       <!-- @hide @SystemApi -->
       <public name="minExtensionVersion" />
-      <public name="autofillInlineSuggestionChip" />
-      <public name="autofillInlineSuggestionTitle" />
-      <public name="autofillInlineSuggestionSubtitle" />
-      <!-- @hide @SystemApi -->
-      <public name="isAutofillInlineSuggestionTheme" />
       <public name="allowNativeHeapPointerTagging" />
+      <public name="preserveLegacyExternalStorage" />
     </public-group>
 
     <public-group type="drawable" first-id="0x010800b5">
     </public-group>
 
     <public-group type="style" first-id="0x010302e5">
-      <public name="Theme.AutofillInlineSuggestion" />
     </public-group>
 
     <public-group type="id" first-id="0x0102004a">
diff --git a/core/res/res/values/styles.xml b/core/res/res/values/styles.xml
index 751eca0..bcce1f0 100644
--- a/core/res/res/values/styles.xml
+++ b/core/res/res/values/styles.xml
@@ -1482,22 +1482,6 @@
         <item name="android:windowExitAnimation">@anim/slide_out_down</item>
     </style>
 
-    <!-- The style for the Autofill inline suggestion chip. -->
-    <!-- @hide -->
-    <style name="AutofillInlineSuggestionChip">
-        <item name="background">@drawable/autofill_dataset_picker_background</item>
-    </style>
-
-    <!-- @hide -->
-    <style name="AutofillInlineSuggestionTitle">
-        <item name="android:textAppearance">@style/TextAppearance</item>
-    </style>
-
-    <!-- @hide -->
-    <style name="AutofillInlineSuggestionSubtitle">
-        <item name="android:textAppearance">@style/TextAppearance.Small</item>
-    </style>
-
     <!-- The style for the container of media actions in a notification. -->
     <!-- @hide -->
     <style name="NotificationMediaActionContainer">
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 500bfe9..02d90a7 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -3261,7 +3261,6 @@
   <java-symbol type="layout" name="autofill_dataset_picker"/>
   <java-symbol type="layout" name="autofill_dataset_picker_fullscreen"/>
   <java-symbol type="layout" name="autofill_dataset_picker_header_footer"/>
-  <java-symbol type="layout" name="autofill_inline_suggestion" />
   <java-symbol type="id" name="autofill" />
   <java-symbol type="id" name="autofill_dataset_footer"/>
   <java-symbol type="id" name="autofill_dataset_header"/>
@@ -3269,10 +3268,6 @@
   <java-symbol type="id" name="autofill_dataset_list"/>
   <java-symbol type="id" name="autofill_dataset_picker"/>
   <java-symbol type="id" name="autofill_dataset_title" />
-  <java-symbol type="id" name="autofill_inline_suggestion_end_icon" />
-  <java-symbol type="id" name="autofill_inline_suggestion_start_icon" />
-  <java-symbol type="id" name="autofill_inline_suggestion_subtitle" />
-  <java-symbol type="id" name="autofill_inline_suggestion_title" />
   <java-symbol type="id" name="autofill_save_custom_subtitle" />
   <java-symbol type="id" name="autofill_save_icon" />
   <java-symbol type="id" name="autofill_save_no" />
diff --git a/core/res/res/values/themes.xml b/core/res/res/values/themes.xml
index 5e6dd82..5d9cb48 100644
--- a/core/res/res/values/themes.xml
+++ b/core/res/res/values/themes.xml
@@ -892,12 +892,4 @@
         <item name="windowContentTransitions">false</item>
         <item name="windowActivityTransitions">false</item>
     </style>
-
-    <!-- Theme for the Autofill inline suggestion on IME -->
-    <style name="Theme.AutofillInlineSuggestion" parent="Theme.DeviceDefault">
-        <item name="isAutofillInlineSuggestionTheme">true</item>
-        <item name="autofillInlineSuggestionChip">@style/AutofillInlineSuggestionChip</item>
-        <item name="autofillInlineSuggestionTitle">@style/AutofillInlineSuggestionTitle</item>
-        <item name="autofillInlineSuggestionSubtitle">@style/AutofillInlineSuggestionSubtitle</item>
-    </style>
 </resources>
diff --git a/media/java/android/media/tv/tuner/TunerConstants.java b/media/java/android/media/tv/tuner/TunerConstants.java
index f54808d..82af658 100644
--- a/media/java/android/media/tv/tuner/TunerConstants.java
+++ b/media/java/android/media/tv/tuner/TunerConstants.java
@@ -38,6 +38,14 @@
      * Invalid stream ID.
      */
     public static final int INVALID_STREAM_ID = Constants.Constant.INVALID_STREAM_ID;
+    /**
+     * Invalid filter ID.
+     */
+    public static final int INVALID_FILTER_ID = Constants.Constant.INVALID_FILTER_ID;
+    /**
+     * Invalid AV Sync ID.
+     */
+    public static final int INVALID_AV_SYNC_ID = Constants.Constant.INVALID_AV_SYNC_ID;
 
     /** @hide */
     @IntDef(prefix = "SCAN_TYPE_", value = {SCAN_TYPE_UNDEFINED, SCAN_TYPE_AUTO, SCAN_TYPE_BLIND})
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/Bubble.java b/packages/SystemUI/src/com/android/systemui/bubbles/Bubble.java
index 1e39954..cdeb5c3 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/Bubble.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/Bubble.java
@@ -38,9 +38,11 @@
 import android.os.Bundle;
 import android.os.UserHandle;
 import android.provider.Settings;
+import android.service.notification.StatusBarNotification;
 import android.util.Log;
 
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.systemui.shared.system.SysUiStatsLog;
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 
 import java.io.FileDescriptor;
@@ -50,7 +52,7 @@
 /**
  * Encapsulates the data and UI elements of a bubble.
  */
-class Bubble {
+class Bubble implements BubbleViewProvider {
     private static final String TAG = "Bubble";
 
     private NotificationEntry mEntry;
@@ -148,12 +150,12 @@
     }
 
     @Nullable
-    BadgedImageView getIconView() {
+    public BadgedImageView getIconView() {
         return mIconView;
     }
 
     @Nullable
-    BubbleExpandedView getExpandedView() {
+    public BubbleExpandedView getExpandedView() {
         return mExpandedView;
     }
 
@@ -238,7 +240,7 @@
      * Note that this contents visibility doesn't affect visibility at {@link android.view.View},
      * and setting {@code false} actually means rendering the expanded view in transparent.
      */
-    void setContentVisibility(boolean visibility) {
+    public void setContentVisibility(boolean visibility) {
         if (mExpandedView != null) {
             mExpandedView.setContentVisibility(visibility);
         }
@@ -481,4 +483,36 @@
     public int hashCode() {
         return Objects.hash(mKey);
     }
+
+    public void logUIEvent(int bubbleCount, int action, float normalX, float normalY, int index) {
+        if (this.getEntry() == null
+                || this.getEntry().getSbn() == null) {
+            SysUiStatsLog.write(SysUiStatsLog.BUBBLE_UI_CHANGED,
+                    null /* package name */,
+                    null /* notification channel */,
+                    0 /* notification ID */,
+                    0 /* bubble position */,
+                    bubbleCount,
+                    action,
+                    normalX,
+                    normalY,
+                    false /* unread bubble */,
+                    false /* on-going bubble */,
+                    false /* isAppForeground (unused) */);
+        } else {
+            StatusBarNotification notification = this.getEntry().getSbn();
+            SysUiStatsLog.write(SysUiStatsLog.BUBBLE_UI_CHANGED,
+                    notification.getPackageName(),
+                    notification.getNotification().getChannelId(),
+                    notification.getId(),
+                    index,
+                    bubbleCount,
+                    action,
+                    normalX,
+                    normalY,
+                    this.showInShade(),
+                    this.isOngoing(),
+                    false /* isAppForeground (unused) */);
+        }
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleOverflow.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleOverflow.java
new file mode 100644
index 0000000..6cf1086
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleOverflow.java
@@ -0,0 +1,107 @@
+/*
+ * 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.systemui.bubbles;
+
+import static android.view.View.GONE;
+import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
+
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.graphics.Color;
+import android.graphics.drawable.AdaptiveIconDrawable;
+import android.graphics.drawable.ColorDrawable;
+import android.graphics.drawable.InsetDrawable;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.FrameLayout;
+import android.widget.ImageView;
+
+import com.android.systemui.R;
+
+/**
+ * Class for showing aged out bubbles.
+ */
+public class BubbleOverflow implements BubbleViewProvider {
+
+    private ImageView mOverflowBtn;
+    private BubbleExpandedView mOverflowExpandedView;
+    private LayoutInflater mInflater;
+    private Context mContext;
+
+    public BubbleOverflow(Context context) {
+        mContext = context;
+        mInflater = LayoutInflater.from(context);
+    }
+
+    public void setUpOverflow(ViewGroup parentViewGroup) {
+        mOverflowExpandedView = (BubbleExpandedView) mInflater.inflate(
+                R.layout.bubble_expanded_view, parentViewGroup /* root */,
+                false /* attachToRoot */);
+        mOverflowExpandedView.setOverflow(true);
+
+        mOverflowBtn = (ImageView) mInflater.inflate(R.layout.bubble_overflow_button,
+                parentViewGroup /* root */,
+                false /* attachToRoot */);
+
+        setOverflowBtnTheme();
+        mOverflowBtn.setVisibility(GONE);
+    }
+
+    ImageView getBtn() {
+        return mOverflowBtn;
+    }
+
+    void setBtnVisible(int visible) {
+        mOverflowBtn.setVisibility(visible);
+    }
+
+    // TODO(b/149146374) Propagate theme change to bubbles in overflow.
+    void setOverflowBtnTheme() {
+        TypedArray ta = mContext.obtainStyledAttributes(
+                new int[]{android.R.attr.colorBackgroundFloating});
+        int bgColor = ta.getColor(0, Color.WHITE /* default */);
+        ta.recycle();
+
+        InsetDrawable fg = new InsetDrawable(mOverflowBtn.getDrawable(), 28);
+        ColorDrawable bg = new ColorDrawable(bgColor);
+        AdaptiveIconDrawable adaptiveIcon = new AdaptiveIconDrawable(bg, fg);
+        mOverflowBtn.setImageDrawable(adaptiveIcon);
+    }
+
+
+    public BubbleExpandedView getExpandedView() {
+        return mOverflowExpandedView;
+    }
+
+    public void setContentVisibility(boolean visible) {
+        mOverflowExpandedView.setContentVisibility(visible);
+    }
+
+    public void logUIEvent(int bubbleCount, int action, float normalX, float normalY,
+            int index) {
+        // TODO(b/149133814) Log overflow UI events.
+    }
+
+    public View getIconView() {
+        return mOverflowBtn;
+    }
+
+    public String getKey() {
+        return BubbleOverflowActivity.KEY;
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleOverflowActivity.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleOverflowActivity.java
index f3cfa83..756c598 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleOverflowActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleOverflowActivity.java
@@ -47,6 +47,7 @@
  * Must be public to be accessible to androidx...AppComponentFactory
  */
 public class BubbleOverflowActivity extends Activity {
+    public static final String KEY = "Overflow";
     private static final String TAG = TAG_WITH_CLASS_NAME ? "BubbleOverflowActivity" : TAG_BUBBLES;
 
     private LinearLayout mEmptyState;
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
index cff78cf..9a6295a 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
@@ -33,8 +33,6 @@
 import android.content.Context;
 import android.content.res.Configuration;
 import android.content.res.Resources;
-import android.content.res.TypedArray;
-import android.graphics.Color;
 import android.graphics.ColorMatrix;
 import android.graphics.ColorMatrixColorFilter;
 import android.graphics.Paint;
@@ -42,13 +40,9 @@
 import android.graphics.PointF;
 import android.graphics.Rect;
 import android.graphics.RectF;
-import android.graphics.drawable.AdaptiveIconDrawable;
-import android.graphics.drawable.ColorDrawable;
-import android.graphics.drawable.InsetDrawable;
 import android.os.Bundle;
 import android.os.VibrationEffect;
 import android.os.Vibrator;
-import android.service.notification.StatusBarNotification;
 import android.util.Log;
 import android.view.Choreographer;
 import android.view.DisplayCutout;
@@ -63,7 +57,6 @@
 import android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction;
 import android.view.animation.AccelerateDecelerateInterpolator;
 import android.widget.FrameLayout;
-import android.widget.ImageView;
 
 import androidx.annotation.MainThread;
 import androidx.annotation.Nullable;
@@ -199,7 +192,7 @@
     private int mPointerHeight;
     private int mStatusBarHeight;
     private int mImeOffset;
-    private Bubble mExpandedBubble;
+    private BubbleViewProvider mExpandedBubble;
     private boolean mIsExpanded;
 
     /** Whether the stack is currently on the left side of the screen, or animating there. */
@@ -322,8 +315,8 @@
     private Runnable mAfterMagnet;
 
     private int mOrientation = Configuration.ORIENTATION_UNDEFINED;
-    private BubbleExpandedView mOverflowExpandedView;
-    private ImageView mOverflowBtn;
+
+    private BubbleOverflow mBubbleOverflow;
 
     public BubbleStackView(Context context, BubbleData data,
             @Nullable SurfaceSynchronizer synchronizer) {
@@ -333,7 +326,6 @@
         mInflater = LayoutInflater.from(context);
         mTouchHandler = new BubbleTouchHandler(this, data, context);
         setOnTouchListener(mTouchHandler);
-        mInflater = LayoutInflater.from(context);
 
         Resources res = getResources();
         mMaxBubbles = res.getInteger(R.integer.bubbles_max_rendered);
@@ -407,12 +399,8 @@
                         .setStiffness(SpringForce.STIFFNESS_LOW)
                         .setDampingRatio(SpringForce.DAMPING_RATIO_LOW_BOUNCY));
         mExpandedViewYAnim.addEndListener((anim, cancelled, value, velocity) -> {
-            if (mIsExpanded) {
-                if (mExpandedBubble == null) {
-                    mOverflowExpandedView.updateView();
-                } else {
-                    mExpandedBubble.getExpandedView().updateView();
-                }
+            if (mIsExpanded && mExpandedBubble != null) {
+                mExpandedBubble.getExpandedView().updateView();
             }
         });
 
@@ -420,8 +408,12 @@
         setFocusable(true);
         mBubbleContainer.bringToFront();
 
+        mBubbleOverflow = new BubbleOverflow(mContext);
         if (BubbleExperimentConfig.allowBubbleOverflow(mContext)) {
-            setUpOverflow();
+            mBubbleOverflow.setUpOverflow(this);
+            mBubbleContainer.addView(mBubbleOverflow.getBtn(), 0,
+                    new FrameLayout.LayoutParams(WRAP_CONTENT, WRAP_CONTENT));
+
         }
 
         setOnApplyWindowInsetsListener((View view, WindowInsets insets) -> {
@@ -432,9 +424,7 @@
                     // Update the insets after we're done translating otherwise position
                     // calculation for them won't be correct.
                     () -> {
-                        if (mExpandedBubble == null) {
-                            mOverflowExpandedView.updateInsets(insets);
-                        } else {
+                        if (mExpandedBubble != null) {
                             mExpandedBubble.getExpandedView().updateInsets(insets);
                         }
                     });
@@ -449,9 +439,7 @@
                     // Reposition & adjust the height for new orientation
                     if (mIsExpanded) {
                         mExpandedViewContainer.setTranslationY(getExpandedViewY());
-                        if (mExpandedBubble == null) {
-                            mOverflowExpandedView.updateView();
-                        } else {
+                        if (mExpandedBubble != null) {
                             mExpandedBubble.getExpandedView().updateView();
                         }
                     }
@@ -516,40 +504,8 @@
         });
     }
 
-    private void setUpOverflow() {
-        mOverflowExpandedView = (BubbleExpandedView) mInflater.inflate(
-                R.layout.bubble_expanded_view, this /* root */, false /* attachToRoot */);
-        mOverflowExpandedView.setOverflow(true);
-
-        mOverflowBtn = (ImageView) mInflater.inflate(R.layout.bubble_overflow_button,
-                this /* root */,
-                false /* attachToRoot */);
-
-        mBubbleContainer.addView(mOverflowBtn, 0,
-                new FrameLayout.LayoutParams(WRAP_CONTENT, WRAP_CONTENT));
-
-        setOverflowBtnTheme();
-        mOverflowBtn.setVisibility(GONE);
-    }
-
-    // TODO(b/149146374) Propagate theme change to bubbles in overflow.
-    private void setOverflowBtnTheme() {
-        TypedArray ta = mContext.obtainStyledAttributes(
-                new int[]{android.R.attr.colorBackgroundFloating});
-        int bgColor = ta.getColor(0, Color.WHITE /* default */);
-        ta.recycle();
-
-        InsetDrawable fg = new InsetDrawable(mOverflowBtn.getDrawable(), 28);
-        ColorDrawable bg = new ColorDrawable(bgColor);
-        AdaptiveIconDrawable adaptiveIcon = new AdaptiveIconDrawable(bg, fg);
-        mOverflowBtn.setImageDrawable(adaptiveIcon);
-    }
-
     void showExpandedViewContents(int displayId) {
-        if (mOverflowExpandedView != null
-                && mOverflowExpandedView.getVirtualDisplayId() == displayId) {
-            mOverflowExpandedView.setContentVisibility(true);
-        } else if (mExpandedBubble != null
+        if (mExpandedBubble != null
                 && mExpandedBubble.getExpandedView().getVirtualDisplayId() == displayId) {
             mExpandedBubble.setContentVisibility(true);
         }
@@ -573,7 +529,7 @@
     public void onThemeChanged() {
         setUpFlyout();
         if (BubbleExperimentConfig.allowBubbleOverflow(mContext)) {
-            setOverflowBtnTheme();
+            mBubbleOverflow.setOverflowBtnTheme();
         }
     }
 
@@ -756,15 +712,22 @@
     /**
      * The {@link BadgedImageView} that is expanded, null if one does not exist.
      */
-    BadgedImageView getExpandedBubbleView() {
+    View getExpandedBubbleView() {
         return mExpandedBubble != null ? mExpandedBubble.getIconView() : null;
     }
 
     /**
      * The {@link Bubble} that is expanded, null if one does not exist.
      */
+    @Nullable
     Bubble getExpandedBubble() {
-        return mExpandedBubble;
+        if (mExpandedBubble == null
+                || (BubbleExperimentConfig.allowBubbleOverflow(mContext)
+                    && mExpandedBubble.getIconView() == mBubbleOverflow.getBtn()
+                    && mExpandedBubble.getKey() == BubbleOverflowActivity.KEY)) {
+            return null;
+        }
+        return (Bubble) mExpandedBubble;
     }
 
     // via BubbleData.Listener
@@ -818,7 +781,7 @@
             if (DEBUG_BUBBLE_STACK_VIEW) {
                 Log.d(TAG, "Show overflow button.");
             }
-            mOverflowBtn.setVisibility(VISIBLE);
+            mBubbleOverflow.setBtnVisible(VISIBLE);
             if (apply) {
                 mExpandedAnimationController.expandFromStack(() -> {
                     updatePointerPosition();
@@ -828,7 +791,7 @@
             if (DEBUG_BUBBLE_STACK_VIEW) {
                 Log.d(TAG, "Collapsed. Hide overflow button.");
             }
-            mOverflowBtn.setVisibility(GONE);
+            mBubbleOverflow.setBtnVisible(GONE);
         }
     }
 
@@ -849,7 +812,7 @@
     }
 
     void showOverflow() {
-        setSelectedBubble(null);
+        setSelectedBubble(mBubbleOverflow);
     }
 
     /**
@@ -858,14 +821,14 @@
      * position of any bubble.
      */
     // via BubbleData.Listener
-    public void setSelectedBubble(@Nullable Bubble bubbleToSelect) {
+    public void setSelectedBubble(@Nullable BubbleViewProvider bubbleToSelect) {
         if (DEBUG_BUBBLE_STACK_VIEW) {
             Log.d(TAG, "setSelectedBubble: " + bubbleToSelect);
         }
         if (mExpandedBubble != null && mExpandedBubble.equals(bubbleToSelect)) {
             return;
         }
-        final Bubble previouslySelected = mExpandedBubble;
+        final BubbleViewProvider previouslySelected = mExpandedBubble;
         mExpandedBubble = bubbleToSelect;
 
         if (mIsExpanded) {
@@ -874,14 +837,11 @@
             // expanded view becomes visible on the screen. See b/126856255
             mExpandedViewContainer.setAlpha(0.0f);
             mSurfaceSynchronizer.syncSurfaceAndRun(() -> {
-                if (previouslySelected == null) {
-                    mOverflowExpandedView.setContentVisibility(false);
-                } else {
-                    previouslySelected.setContentVisibility(false);
-                }
+                previouslySelected.setContentVisibility(false);
                 updateExpandedBubble();
                 updatePointerPosition();
                 requestUpdate();
+
                 logBubbleEvent(previouslySelected,
                         SysUiStatsLog.BUBBLE_UICHANGED__ACTION__COLLAPSED);
                 logBubbleEvent(bubbleToSelect, SysUiStatsLog.BUBBLE_UICHANGED__ACTION__EXPANDED);
@@ -941,8 +901,8 @@
         if (mIsExpanded) {
             if (isIntersecting(mBubbleContainer, x, y)) {
                 if (BubbleExperimentConfig.allowBubbleOverflow(mContext)
-                        && isIntersecting(mOverflowBtn, x, y)) {
-                    return mOverflowBtn;
+                        && isIntersecting(mBubbleOverflow.getBtn(), x, y)) {
+                    return mBubbleOverflow.getBtn();
                 }
                 // Could be tapping or dragging a bubble while expanded
                 for (int i = 0; i < getBubbleCount(); i++) {
@@ -1030,7 +990,7 @@
 
     private void animateCollapse() {
         mIsExpanded = false;
-        final Bubble previouslySelected = mExpandedBubble;
+        final BubbleViewProvider previouslySelected = mExpandedBubble;
         beforeExpandedViewAnimation();
 
         if (DEBUG_BUBBLE_STACK_VIEW) {
@@ -1046,11 +1006,7 @@
                 () -> {
                     mBubbleContainer.setActiveController(mStackAnimationController);
                     afterExpandedViewAnimation();
-                    if (previouslySelected == null) {
-                        mOverflowExpandedView.setContentVisibility(false);
-                    } else {
-                        previouslySelected.setContentVisibility(false);
-                    }
+                    previouslySelected.setContentVisibility(false);
                 });
 
         mExpandedViewXAnim.animateToFinalPosition(getCollapsedX());
@@ -1093,7 +1049,7 @@
                 mExpandedAnimateYDistance);
     }
 
-    private void notifyExpansionChanged(Bubble bubble, boolean expanded) {
+    private void notifyExpansionChanged(BubbleViewProvider bubble, boolean expanded) {
         if (mExpandListener != null && bubble != null) {
             mExpandListener.onBubbleExpandChanged(expanded, bubble.getKey());
         }
@@ -1613,11 +1569,8 @@
             Log.d(TAG, "updateExpandedBubble()");
         }
         mExpandedViewContainer.removeAllViews();
-        if (mIsExpanded) {
-            BubbleExpandedView bev = mOverflowExpandedView;
-            if (mExpandedBubble != null) {
-                bev = mExpandedBubble.getExpandedView();
-            }
+        if (mIsExpanded && mExpandedBubble != null) {
+            BubbleExpandedView bev = mExpandedBubble.getExpandedView();
             mExpandedViewContainer.addView(bev);
             bev.populateExpandedView();
             mExpandedViewContainer.setVisibility(VISIBLE);
@@ -1636,9 +1589,7 @@
             if (!mExpandedViewYAnim.isRunning()) {
                 // We're not animating so set the value
                 mExpandedViewContainer.setTranslationY(y);
-                if (mExpandedBubble == null) {
-                    mOverflowExpandedView.updateView();
-                } else {
+                if (mExpandedBubble != null) {
                     mExpandedBubble.getExpandedView().updateView();
                 }
             } else {
@@ -1693,15 +1644,16 @@
     /**
      * Finds the bubble index within the stack.
      *
-     * @param bubble the bubble to look up.
+     * @param provider the bubble view provider with the bubble to look up.
      * @return the index of the bubble view within the bubble stack. The range of the position
      * is between 0 and the bubble count minus 1.
      */
-    int getBubbleIndex(@Nullable Bubble bubble) {
-        if (bubble == null) {
+    int getBubbleIndex(@Nullable BubbleViewProvider provider) {
+        if (provider == null || provider.getKey() == BubbleOverflowActivity.KEY) {
             return 0;
         }
-        return mBubbleContainer.indexOfChild(bubble.getIconView());
+        Bubble b = (Bubble) provider;
+        return mBubbleContainer.indexOfChild(b.getIconView());
     }
 
     /**
@@ -1733,36 +1685,12 @@
      *               the user interaction is not specific to one bubble.
      * @param action the user interaction enum.
      */
-    private void logBubbleEvent(@Nullable Bubble bubble, int action) {
-        if (bubble == null || bubble.getEntry() == null
-                || bubble.getEntry().getSbn() == null) {
-            SysUiStatsLog.write(SysUiStatsLog.BUBBLE_UI_CHANGED,
-                    null /* package name */,
-                    null /* notification channel */,
-                    0 /* notification ID */,
-                    0 /* bubble position */,
-                    getBubbleCount(),
-                    action,
-                    getNormalizedXPosition(),
-                    getNormalizedYPosition(),
-                    false /* unread bubble */,
-                    false /* on-going bubble */,
-                    false /* isAppForeground (unused) */);
-        } else {
-            StatusBarNotification notification = bubble.getEntry().getSbn();
-            SysUiStatsLog.write(SysUiStatsLog.BUBBLE_UI_CHANGED,
-                    notification.getPackageName(),
-                    notification.getNotification().getChannelId(),
-                    notification.getId(),
-                    getBubbleIndex(bubble),
-                    getBubbleCount(),
-                    action,
-                    getNormalizedXPosition(),
-                    getNormalizedYPosition(),
-                    bubble.showInShade(),
-                    bubble.isOngoing(),
-                    false /* isAppForeground (unused) */);
+    private void logBubbleEvent(@Nullable BubbleViewProvider bubble, int action) {
+        if (bubble == null) {
+            return;
         }
+        bubble.logUIEvent(getBubbleCount(), action, getNormalizedXPosition(),
+                getNormalizedYPosition(), getBubbleIndex(bubble));
     }
 
     /**
@@ -1770,14 +1698,10 @@
      * a back key down/up event pair is forwarded to the bubble Activity.
      */
     boolean performBackPressIfNeeded() {
-        if (!isExpanded()) {
+        if (!isExpanded() || mExpandedBubble == null) {
             return false;
         }
-        if (mExpandedBubble == null) {
-            return mOverflowExpandedView.performBackPressIfNeeded();
-        } else {
-            return mExpandedBubble.getExpandedView().performBackPressIfNeeded();
-        }
+        return mExpandedBubble.getExpandedView().performBackPressIfNeeded();
     }
 
     /** For debugging only */
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleViewProvider.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleViewProvider.java
new file mode 100644
index 0000000..59fc435
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleViewProvider.java
@@ -0,0 +1,30 @@
+/*
+ * 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.systemui.bubbles;
+
+import android.view.View;
+
+/**
+ * Interface to represent actual Bubbles and UI elements that act like bubbles, like BubbleOverflow.
+ */
+interface BubbleViewProvider {
+    BubbleExpandedView getExpandedView();
+    void setContentVisibility(boolean visible);
+    View getIconView();
+    void logUIEvent(int bubbleCount, int action, float normalX, float normalY, int index);
+    String getKey();
+}
diff --git a/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsProviderLifecycleManager.kt b/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsProviderLifecycleManager.kt
index e09d20b..9ec71c7 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsProviderLifecycleManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsProviderLifecycleManager.kt
@@ -86,7 +86,7 @@
         private const val MSG_ACTION = 2
         private const val MSG_UNBIND = 3
         private const val BIND_RETRY_DELAY = 1000L // ms
-        private const val LOAD_TIMEOUT = 5000L // ms
+        private const val LOAD_TIMEOUT_SECONDS = 30L // seconds
         private const val MAX_BIND_RETRIES = 5
         private const val MAX_CONTROLS_REQUEST = 100000L
         private const val DEBUG = true
@@ -231,7 +231,7 @@
             // Don't accept load callbacks after this
             lastLoadCallback = null
             unbindService()
-        }, LOAD_TIMEOUT, TimeUnit.MILLISECONDS)
+        }, LOAD_TIMEOUT_SECONDS, TimeUnit.SECONDS)
 
         invokeOrQueue(::load, Message.Load)
     }
diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/Behavior.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/Behavior.kt
index 44f3fbc..275c778 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/ui/Behavior.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/ui/Behavior.kt
@@ -16,6 +16,20 @@
 
 package com.android.systemui.controls.ui
 
+/**
+ * All controls need to respond to changes in state and handle user-generated events.
+ * Implementations of this interface provide these different means by adding their own
+ * event handlers, and will update the control ui as they see fit.
+ */
 interface Behavior {
-    fun apply(cvh: ControlViewHolder, cws: ControlWithState)
+
+    /**
+     * Only called once per instance
+     */
+    fun initialize(cvh: ControlViewHolder)
+
+    /**
+     * Will be invoked on every update provided to the Control
+     */
+    fun bind(cws: ControlWithState)
 }
diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlViewHolder.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlViewHolder.kt
index aec20751..feaea7c 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlViewHolder.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlViewHolder.kt
@@ -37,6 +37,8 @@
 import com.android.systemui.util.concurrency.DelayableExecutor
 import com.android.systemui.R
 
+import kotlin.reflect.KClass
+
 private const val UPDATE_DELAY_IN_MILLIS = 3000L
 
 class ControlViewHolder(
@@ -54,6 +56,7 @@
     val clipLayer: ClipDrawable
     lateinit var cws: ControlWithState
     var cancelUpdate: Runnable? = null
+    var behavior: Behavior? = null
 
     init {
         val ld = layout.getBackground() as LayerDrawable
@@ -83,7 +86,14 @@
             })
         }
 
-        findBehavior(status, template).apply(this, cws)
+        val clazz = findBehavior(status, template)
+        if (behavior == null || behavior!!::class != clazz) {
+            // Behavior changes can signal a change in template from the app or
+            // first time setup
+            behavior = clazz.java.newInstance()
+            behavior?.initialize(this)
+        }
+        behavior?.bind(cws)
     }
 
     fun actionResponse(@ControlAction.ResponseResult response: Int) {
@@ -115,21 +125,14 @@
         controlsController.action(cws.ci, action)
     }
 
-    private fun findBehavior(status: Int, template: ControlTemplate): Behavior {
+    private fun findBehavior(status: Int, template: ControlTemplate): KClass<out Behavior> {
         return when {
-            status == Control.STATUS_UNKNOWN -> UnknownBehavior()
-            template is ToggleTemplate -> ToggleBehavior()
-            template is ToggleRangeTemplate -> ToggleRangeBehavior()
-            template is TemperatureControlTemplate -> TemperatureControlBehavior()
-            template is ThumbnailTemplate -> StaticBehavior(uiExecutor, bgExecutor)
-            else -> {
-                object : Behavior {
-                    override fun apply(cvh: ControlViewHolder, cws: ControlWithState) {
-                        cvh.status.setText(cws.control?.getStatusText())
-                        cvh.applyRenderInfo(RenderInfo.lookup(cws.ci.deviceType, false))
-                    }
-                }
-            }
+            status == Control.STATUS_UNKNOWN -> UnknownBehavior::class
+            template is ToggleTemplate -> ToggleBehavior::class
+            template is ToggleRangeTemplate -> ToggleRangeBehavior::class
+            template is TemperatureControlTemplate -> TemperatureControlBehavior::class
+            template is ThumbnailTemplate -> StaticBehavior::class
+            else -> DefaultBehavior::class
         }
     }
 
@@ -142,9 +145,12 @@
         icon.setImageIcon(Icon.createWithResource(context, ri.iconResourceId))
         icon.setImageTintList(fg)
 
-        clipLayer.getDrawable().setTintBlendMode(BlendMode.HUE)
-        clipLayer.getDrawable().setTintList(bg)
+        clipLayer.getDrawable().apply {
+            setTintBlendMode(BlendMode.HUE)
+            setTintList(bg)
+        }
     }
+
     fun setEnabled(enabled: Boolean) {
         status.setEnabled(enabled)
         icon.setEnabled(enabled)
diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/DefaultBehavior.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/DefaultBehavior.kt
new file mode 100644
index 0000000..1747929
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/controls/ui/DefaultBehavior.kt
@@ -0,0 +1,31 @@
+/*
+ * 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.systemui.controls.ui
+
+class DefaultBehavior : Behavior {
+    lateinit var cvh: ControlViewHolder
+
+    override fun initialize(cvh: ControlViewHolder) {
+        this.cvh = cvh
+    }
+
+    override fun bind(cws: ControlWithState) {
+        cvh.status.setText(cws.control?.getStatusText() ?: "")
+        cvh.setEnabled(false)
+        cvh.applyRenderInfo(RenderInfo.lookup(cws.ci.deviceType, false))
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/StaticBehavior.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/StaticBehavior.kt
index 340cdc0..c006d6f 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/ui/StaticBehavior.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/ui/StaticBehavior.kt
@@ -24,20 +24,20 @@
 import com.android.systemui.R
 import com.android.systemui.controls.ui.ControlActionCoordinator.MAX_LEVEL
 
-import java.util.concurrent.Executor
-
 /**
  * Used for controls that cannot be interacted with. Information is presented to the user
  * but no actions can be taken. If using a ThumbnailTemplate, the background image will
  * be changed.
  */
-class StaticBehavior(
-    val uiExecutor: Executor,
-    val bgExecutor: Executor
-) : Behavior {
+class StaticBehavior() : Behavior {
     lateinit var control: Control
+    lateinit var cvh: ControlViewHolder
 
-    override fun apply(cvh: ControlViewHolder, cws: ControlWithState) {
+    override fun initialize(cvh: ControlViewHolder) {
+        this.cvh = cvh
+    }
+
+    override fun bind(cws: ControlWithState) {
         this.control = cws.control!!
 
         cvh.status.setText(control.getStatusText())
@@ -51,12 +51,12 @@
 
         val template = control.getControlTemplate()
         if (template is ThumbnailTemplate) {
-            bgExecutor.execute {
+            cvh.bgExecutor.execute {
                 // clear the default tinting in favor of only using alpha
                 val drawable = template.getThumbnail().loadDrawable(cvh.context)
                 drawable.setTintList(null)
                 drawable.setAlpha((0.45 * 255).toInt())
-                uiExecutor.execute {
+                cvh.uiExecutor.execute {
                     val radius = cvh.context.getResources()
                             .getDimensionPixelSize(R.dimen.control_corner_radius).toFloat()
                     clipLayer.setDrawable(CornerDrawable(drawable, radius))
diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/TemperatureControlBehavior.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/TemperatureControlBehavior.kt
index 7661c6f..239d2e5 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/ui/TemperatureControlBehavior.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/ui/TemperatureControlBehavior.kt
@@ -16,12 +16,10 @@
 
 package com.android.systemui.controls.ui
 
-import android.content.Context
 import android.graphics.drawable.Drawable
 import android.graphics.drawable.LayerDrawable
 import android.service.controls.Control
 import android.service.controls.templates.TemperatureControlTemplate
-import android.widget.TextView
 
 import com.android.systemui.R
 import com.android.systemui.controls.ui.ControlActionCoordinator.MIN_LEVEL
@@ -32,15 +30,15 @@
     lateinit var control: Control
     lateinit var cvh: ControlViewHolder
     lateinit var template: TemperatureControlTemplate
-    lateinit var status: TextView
-    lateinit var context: Context
 
-    override fun apply(cvh: ControlViewHolder, cws: ControlWithState) {
-        this.control = cws.control!!
+    override fun initialize(cvh: ControlViewHolder) {
         this.cvh = cvh
-        status = cvh.status
+    }
 
-        status.setText(control.getStatusText())
+    override fun bind(cws: ControlWithState) {
+        this.control = cws.control!!
+
+        cvh.status.setText(control.getStatusText())
 
         val ld = cvh.layout.getBackground() as LayerDrawable
         clipLayer = ld.findDrawableByLayerId(R.id.clip_layer)
diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/ToggleBehavior.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/ToggleBehavior.kt
index 4c35d26..d306d7c 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/ui/ToggleBehavior.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/ui/ToggleBehavior.kt
@@ -16,11 +16,9 @@
 
 package com.android.systemui.controls.ui
 
-import android.content.Context
 import android.graphics.drawable.Drawable
 import android.graphics.drawable.LayerDrawable
 import android.view.View
-import android.widget.TextView
 import android.service.controls.Control
 import android.service.controls.templates.ToggleTemplate
 
@@ -33,21 +31,21 @@
     lateinit var template: ToggleTemplate
     lateinit var control: Control
     lateinit var cvh: ControlViewHolder
-    lateinit var context: Context
-    lateinit var status: TextView
 
-    override fun apply(cvh: ControlViewHolder, cws: ControlWithState) {
-        this.control = cws.control!!
+    override fun initialize(cvh: ControlViewHolder) {
         this.cvh = cvh
-        status = cvh.status
-
-        status.setText(control.getStatusText())
-
-        template = control.getControlTemplate() as ToggleTemplate
+        cvh.setEnabled(false)
 
         cvh.layout.setOnClickListener(View.OnClickListener() {
             ControlActionCoordinator.toggle(cvh, template.getTemplateId(), template.isChecked())
         })
+    }
+
+    override fun bind(cws: ControlWithState) {
+        this.control = cws.control!!
+
+        cvh.status.setText(control.getStatusText())
+        template = control.getControlTemplate() as ToggleTemplate
 
         val ld = cvh.layout.getBackground() as LayerDrawable
         clipLayer = ld.findDrawableByLayerId(R.id.clip_layer)
diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/ToggleRangeBehavior.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/ToggleRangeBehavior.kt
index 8d65ca3..45d7397 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/ui/ToggleRangeBehavior.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/ui/ToggleRangeBehavior.kt
@@ -51,17 +51,13 @@
         private const val DEFAULT_FORMAT = "%.1f"
     }
 
-    override fun apply(cvh: ControlViewHolder, cws: ControlWithState) {
-        this.control = cws.control!!
+    override fun initialize(cvh: ControlViewHolder) {
         this.cvh = cvh
-
-        statusExtra = cvh.statusExtra
         status = cvh.status
-
-        status.setText(control.getStatusText())
-
         context = status.getContext()
 
+        cvh.setEnabled(false)
+
         val gestureListener = ToggleRangeGestureListener(cvh.layout)
         val gestureDetector = GestureDetector(context, gestureListener)
         cvh.layout.setOnTouchListener({ v: View, e: MotionEvent ->
@@ -77,6 +73,13 @@
 
             return@setOnTouchListener false
         })
+    }
+
+    override fun bind(cws: ControlWithState) {
+        this.control = cws.control!!
+
+        statusExtra = cvh.statusExtra
+        status.setText(control.getStatusText())
 
         val ld = cvh.layout.getBackground() as LayerDrawable
         clipLayer = ld.findDrawableByLayerId(R.id.clip_layer)
diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/UnknownBehavior.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/UnknownBehavior.kt
index 27f1b33..1f33b85 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/ui/UnknownBehavior.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/ui/UnknownBehavior.kt
@@ -17,7 +17,13 @@
 package com.android.systemui.controls.ui
 
 class UnknownBehavior : Behavior {
-    override fun apply(cvh: ControlViewHolder, cws: ControlWithState) {
+    lateinit var cvh: ControlViewHolder
+
+    override fun initialize(cvh: ControlViewHolder) {
+        this.cvh = cvh
+    }
+
+    override fun bind(cws: ControlWithState) {
         cvh.status.setText(cvh.context.getString(com.android.internal.R.string.loading))
         cvh.setEnabled(false)
         cvh.applyRenderInfo(RenderInfo.lookup(cws.ci.deviceType, false))
diff --git a/services/core/java/com/android/server/StorageManagerService.java b/services/core/java/com/android/server/StorageManagerService.java
index 8a095af..033f63b 100644
--- a/services/core/java/com/android/server/StorageManagerService.java
+++ b/services/core/java/com/android/server/StorageManagerService.java
@@ -28,6 +28,10 @@
 import static android.app.AppOpsManager.OP_READ_EXTERNAL_STORAGE;
 import static android.app.AppOpsManager.OP_REQUEST_INSTALL_PACKAGES;
 import static android.app.AppOpsManager.OP_WRITE_EXTERNAL_STORAGE;
+import static android.content.pm.PackageManager.MATCH_ANY_USER;
+import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_AWARE;
+import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_UNAWARE;
+import static android.content.pm.PackageManager.MATCH_UNINSTALLED_PACKAGES;
 import static android.content.pm.PackageManager.PERMISSION_GRANTED;
 import static android.os.ParcelFileDescriptor.MODE_READ_WRITE;
 import static android.os.storage.OnObbStateChangeListener.ERROR_ALREADY_MOUNTED;
@@ -136,6 +140,7 @@
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.app.IAppOpsCallback;
 import com.android.internal.app.IAppOpsService;
+import com.android.internal.content.PackageMonitor;
 import com.android.internal.os.AppFuseMount;
 import com.android.internal.os.BackgroundThread;
 import com.android.internal.os.FuseUnavailableMountException;
@@ -149,6 +154,7 @@
 import com.android.internal.util.IndentingPrintWriter;
 import com.android.internal.util.Preconditions;
 import com.android.internal.widget.LockPatternUtils;
+import com.android.server.SystemService.TargetUser;
 import com.android.server.pm.Installer;
 import com.android.server.storage.AppFuseBridge;
 import com.android.server.storage.StorageSessionController;
@@ -184,6 +190,7 @@
 import java.util.Map;
 import java.util.Map.Entry;
 import java.util.Objects;
+import java.util.Set;
 import java.util.concurrent.CopyOnWriteArrayList;
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.TimeUnit;
@@ -270,6 +277,11 @@
         public void onStopUser(int userHandle) {
             mStorageManagerService.onStopUser(userHandle);
         }
+
+        @Override
+        public void onStartUser(TargetUser user) {
+            mStorageManagerService.snapshotAndMonitorLegacyStorageAppOp(user.getUserHandle());
+        }
     }
 
     private static final boolean DEBUG_EVENTS = false;
@@ -578,6 +590,12 @@
 
     private final boolean mIsFuseEnabled;
 
+    @GuardedBy("mLock")
+    private final Set<Integer> mUidsWithLegacyExternalStorage = new ArraySet<>();
+    // Not guarded by lock, always used on the ActivityManager thread
+    private final Map<Integer, PackageMonitor> mPackageMonitorsForUser = new ArrayMap<>();
+
+
     class ObbState implements IBinder.DeathRecipient {
         public ObbState(String rawPath, String canonicalPath, int callingUid,
                 IObbActionListener token, int nonce, String volId) {
@@ -1145,6 +1163,10 @@
         } catch (Exception e) {
             Slog.wtf(TAG, e);
         }
+        PackageMonitor monitor = mPackageMonitorsForUser.remove(userId);
+        if (monitor != null) {
+            monitor.unregister();
+        }
     }
 
     private boolean supportsBlockCheckpoint() throws RemoteException {
@@ -1836,6 +1858,49 @@
         }
     }
 
+    private void updateLegacyStorageApps(String packageName, int uid, boolean hasLegacy) {
+        synchronized (mLock) {
+            if (hasLegacy) {
+                Slog.v(TAG, "Package " + packageName + " has legacy storage");
+                mUidsWithLegacyExternalStorage.add(uid);
+            } else {
+                // TODO(b/149391976): Handle shared user id. Check if there's any other
+                // installed app with legacy external storage before removing
+                Slog.v(TAG, "Package " + packageName + " does not have legacy storage");
+                mUidsWithLegacyExternalStorage.remove(uid);
+            }
+        }
+    }
+
+    private void snapshotAndMonitorLegacyStorageAppOp(UserHandle user) {
+        int userId = user.getIdentifier();
+
+        // TODO(b/149391976): Use mIAppOpsService.getPackagesForOps instead of iterating below
+        // It should improve performance but the AppOps method doesn't return any app here :(
+        // This operation currently takes about ~20ms on a freshly flashed device
+        for (ApplicationInfo ai : mPmInternal.getInstalledApplications(MATCH_DIRECT_BOOT_AWARE
+                        | MATCH_DIRECT_BOOT_UNAWARE | MATCH_UNINSTALLED_PACKAGES | MATCH_ANY_USER,
+                        userId, Process.myUid())) {
+            try {
+                boolean hasLegacy = mIAppOpsService.checkOperation(OP_LEGACY_STORAGE, ai.uid,
+                        ai.packageName) == MODE_ALLOWED;
+                updateLegacyStorageApps(ai.packageName, ai.uid, hasLegacy);
+            } catch (RemoteException e) {
+                Slog.e(TAG, "Failed to check legacy op for package " + ai.packageName, e);
+            }
+        }
+
+        PackageMonitor monitor = new PackageMonitor() {
+                @Override
+                public void onPackageRemoved(String packageName, int uid) {
+                    updateLegacyStorageApps(packageName, uid, false);
+                }
+            };
+        // TODO(b/149391976): Use different handler?
+        monitor.register(mContext, user, true, mHandler);
+        mPackageMonitorsForUser.put(userId, monitor);
+    }
+
     private static long getLastAccessTime(AppOpsManager manager,
             int uid, String packageName, int[] ops) {
         long maxTime = 0;
@@ -4337,6 +4402,13 @@
             mHandler.obtainMessage(H_RESET).sendToTarget();
         }
 
+        @Override
+        public boolean hasLegacyExternalStorage(int uid) {
+            synchronized (mLock) {
+                return mUidsWithLegacyExternalStorage.contains(uid);
+            }
+        }
+
         public boolean hasExternalStorage(int uid, String packageName) {
             // No need to check for system uid. This avoids a deadlock between
             // PackageManagerService and AppOpsService.
@@ -4382,8 +4454,11 @@
                             // volumes, USB OTGs that are rarely mounted. The app will get the
                             // external_storage gid on next organic restart.
                             killAppForOpChange(code, uid, packageName);
-                            return;
                         }
+                        return;
+                    case OP_LEGACY_STORAGE:
+                        updateLegacyStorageApps(packageName, uid, mode == MODE_ALLOWED);
+                        return;
                 }
             }
 
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index d6d24e7..1da07ef 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -7156,7 +7156,7 @@
                     } finally {
                         Binder.restoreCallingIdentity(ident);
                     }
-                } else if (dyingProc == cpr.proc) {
+                } else if (dyingProc == cpr.proc && dyingProc != null) {
                     // The old stable connection's client should be killed during proc cleaning up,
                     // so do not re-use the old ContentProviderRecord, otherwise the new clients
                     // could get killed unexpectedly.
diff --git a/services/core/java/com/android/server/media/BluetoothRouteProvider.java b/services/core/java/com/android/server/media/BluetoothRouteProvider.java
index 6474303..8379614 100644
--- a/services/core/java/com/android/server/media/BluetoothRouteProvider.java
+++ b/services/core/java/com/android/server/media/BluetoothRouteProvider.java
@@ -29,7 +29,6 @@
 import android.content.IntentFilter;
 import android.media.AudioManager;
 import android.media.MediaRoute2Info;
-import android.text.TextUtils;
 import android.util.Slog;
 import android.util.SparseBooleanArray;
 
@@ -49,6 +48,8 @@
     @SuppressWarnings("WeakerAccess") /* synthetic access */
     final Map<String, BluetoothRouteInfo> mBluetoothRoutes = new HashMap<>();
     @SuppressWarnings("WeakerAccess") /* synthetic access */
+    BluetoothRouteInfo mSelectedRoute = null;
+    @SuppressWarnings("WeakerAccess") /* synthetic access */
     BluetoothA2dp mA2dpProfile;
     @SuppressWarnings("WeakerAccess") /* synthetic access */
     BluetoothHearingAid mHearingAidProfile;
@@ -62,8 +63,6 @@
     private final BroadcastReceiver mBroadcastReceiver = new BluetoothBroadcastReceiver();
     private final BluetoothProfileListener mProfileListener = new BluetoothProfileListener();
 
-    private BluetoothDevice mActiveDevice = null;
-
     static synchronized BluetoothRouteProvider getInstance(@NonNull Context context,
             @NonNull BluetoothRoutesUpdatedListener listener) {
         Objects.requireNonNull(context);
@@ -108,27 +107,21 @@
     }
 
     /**
-     * Clears the active device for all known profiles.
+     * Transfers to a given bluetooth route.
+     * The dedicated BT device with the route would be activated.
+     *
+     * @param routeId the id of the Bluetooth device. {@code null} denotes to clear the use of
+     *               BT routes.
      */
-    public void clearActiveDevices() {
-        BluetoothA2dp a2dpProfile = mA2dpProfile;
-        BluetoothHearingAid hearingAidProfile = mHearingAidProfile;
-        if (a2dpProfile != null) {
-            a2dpProfile.setActiveDevice(null);
+    public void transferTo(@Nullable String routeId) {
+        if (routeId == null) {
+            clearActiveDevices();
+            return;
         }
-        if (hearingAidProfile != null) {
-            hearingAidProfile.setActiveDevice(null);
-        }
-    }
 
-    /**
-     * Sets the active device.
-     * @param deviceId the id of the Bluetooth device
-     */
-    public void setActiveDevice(@NonNull String deviceId) {
-        BluetoothRouteInfo btRouteInfo = mBluetoothRoutes.get(deviceId);
+        BluetoothRouteInfo btRouteInfo = mBluetoothRoutes.get(routeId);
         if (btRouteInfo == null) {
-            Slog.w(TAG, "setActiveDevice: unknown device id=" + deviceId);
+            Slog.w(TAG, "setActiveDevice: unknown route id=" + routeId);
             return;
         }
         BluetoothA2dp a2dpProfile = mA2dpProfile;
@@ -144,6 +137,20 @@
         }
     }
 
+    /**
+     * Clears the active device for all known profiles.
+     */
+    private void clearActiveDevices() {
+        BluetoothA2dp a2dpProfile = mA2dpProfile;
+        BluetoothHearingAid hearingAidProfile = mHearingAidProfile;
+        if (a2dpProfile != null) {
+            a2dpProfile.setActiveDevice(null);
+        }
+        if (hearingAidProfile != null) {
+            hearingAidProfile.setActiveDevice(null);
+        }
+    }
+
     private void addEventReceiver(String action, BluetoothEventReceiver eventReceiver) {
         mEventReceiverMap.put(action, eventReceiver);
         mIntentFilter.addAction(action);
@@ -159,7 +166,8 @@
         }
     }
 
-    @NonNull List<MediaRoute2Info> getBluetoothRoutes() {
+    @NonNull
+    List<MediaRoute2Info> getAllBluetoothRoutes() {
         ArrayList<MediaRoute2Info> routes = new ArrayList<>();
         for (BluetoothRouteInfo btRoute : mBluetoothRoutes.values()) {
             routes.add(btRoute.route);
@@ -167,17 +175,14 @@
         return routes;
     }
 
-    @Nullable String getActiveDeviceAddress() {
-        BluetoothDevice device = mActiveDevice;
-        if (device == null) {
-            return null;
-        }
-        return device.getAddress();
+    @Nullable
+    String getSelectedRouteId() {
+        return mSelectedRoute == null ? null : mSelectedRoute.route.getId();
     }
 
     private void notifyBluetoothRoutesUpdated() {
         if (mListener != null) {
-            mListener.onBluetoothRoutesUpdated(getBluetoothRoutes());
+            mListener.onBluetoothRoutesUpdated(getAllBluetoothRoutes());
         }
     }
 
@@ -198,15 +203,10 @@
         return newBtRoute;
     }
 
-    private void setRouteConnectionStateForDevice(BluetoothDevice device,
+    private void setRouteConnectionState(@NonNull BluetoothRouteInfo btRoute,
             @MediaRoute2Info.ConnectionState int state) {
-        if (device == null) {
-            Slog.w(TAG, "setRouteConnectionStateForDevice: device shouldn't be null");
-            return;
-        }
-        BluetoothRouteInfo btRoute = mBluetoothRoutes.get(device.getAddress());
         if (btRoute == null) {
-            Slog.w(TAG, "setRouteConnectionStateForDevice: route shouldn't be null");
+            Slog.w(TAG, "setRouteConnectionState: route shouldn't be null");
             return;
         }
         if (btRoute.route.getConnectionState() == state) {
@@ -260,8 +260,8 @@
                     mBluetoothRoutes.put(device.getAddress(), btRoute);
                 }
                 if (activeDevices.contains(device)) {
-                    mActiveDevice = device;
-                    setRouteConnectionStateForDevice(device,
+                    mSelectedRoute = btRoute;
+                    setRouteConnectionState(mSelectedRoute,
                             MediaRoute2Info.CONNECTION_STATE_CONNECTED);
                 }
 
@@ -344,20 +344,18 @@
         public void onReceive(Context context, Intent intent, BluetoothDevice device) {
             switch (intent.getAction()) {
                 case BluetoothA2dp.ACTION_ACTIVE_DEVICE_CHANGED:
-                    String prevActiveDeviceAddress =
-                            (mActiveDevice == null) ? null : mActiveDevice.getAddress();
-                    String curActiveDeviceAddress =
-                            (device == null) ? null : device.getAddress();
-                    if (!TextUtils.equals(prevActiveDeviceAddress, curActiveDeviceAddress)) {
-                        if (mActiveDevice != null) {
-                            setRouteConnectionStateForDevice(mActiveDevice,
+                    if (mSelectedRoute == null
+                            || !mSelectedRoute.btDevice.equals(device)) {
+                        if (mSelectedRoute != null) {
+                            setRouteConnectionState(mSelectedRoute,
                                     MediaRoute2Info.CONNECTION_STATE_DISCONNECTED);
                         }
-                        if (device != null) {
-                            setRouteConnectionStateForDevice(device,
+                        mSelectedRoute = (device == null) ? null
+                                : mBluetoothRoutes.get(device.getAddress());
+                        if (mSelectedRoute != null) {
+                            setRouteConnectionState(mSelectedRoute,
                                     MediaRoute2Info.CONNECTION_STATE_CONNECTED);
                         }
-                        mActiveDevice = device;
                         notifyBluetoothRoutesUpdated();
                     }
                     break;
@@ -386,10 +384,8 @@
                     btRoute.connectedProfiles.delete(profile);
                     if (btRoute.connectedProfiles.size() == 0) {
                         mBluetoothRoutes.remove(device.getAddress());
-                        if (mActiveDevice != null
-                                && TextUtils.equals(mActiveDevice.getAddress(),
-                                device.getAddress())) {
-                            mActiveDevice = null;
+                        if (mSelectedRoute != null && mSelectedRoute.btDevice.equals(device)) {
+                            mSelectedRoute = null;
                         }
                         notifyBluetoothRoutesUpdated();
                     }
diff --git a/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java b/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java
index ae11c70..7f7a663 100644
--- a/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java
+++ b/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java
@@ -145,9 +145,9 @@
     @Override
     public void transferToRoute(String sessionId, String routeId) {
         if (TextUtils.equals(routeId, mDefaultRoute.getId())) {
-            mBtRouteProvider.clearActiveDevices();
+            mBtRouteProvider.transferTo(null);
         } else {
-            mBtRouteProvider.setActiveDevice(routeId);
+            mBtRouteProvider.transferTo(routeId);
         }
     }
 
@@ -196,7 +196,7 @@
     }
 
     private void initializeSessionInfo() {
-        mBluetoothRoutes = mBtRouteProvider.getBluetoothRoutes();
+        mBluetoothRoutes = mBtRouteProvider.getAllBluetoothRoutes();
 
         MediaRoute2ProviderInfo.Builder builder = new MediaRoute2ProviderInfo.Builder();
         builder.addRoute(mDefaultRoute);
@@ -251,7 +251,7 @@
         RoutingSessionInfo.Builder builder = new RoutingSessionInfo.Builder(
                 SYSTEM_SESSION_ID, "" /* clientPackageName */)
                 .setSystemSession(true);
-        String activeBtDeviceAddress = mBtRouteProvider.getActiveDeviceAddress();
+        String activeBtDeviceAddress = mBtRouteProvider.getSelectedRouteId();
         mSelectedRouteId = TextUtils.isEmpty(activeBtDeviceAddress) ? mDefaultRoute.getId()
                 : activeBtDeviceAddress;
         builder.addSelectedRoute(mSelectedRouteId);
@@ -311,7 +311,7 @@
                     AudioManager.EXTRA_PREV_VOLUME_STREAM_VALUE, 0);
 
             if (newVolume != oldVolume) {
-                String activeBtDeviceAddress = mBtRouteProvider.getActiveDeviceAddress();
+                String activeBtDeviceAddress = mBtRouteProvider.getSelectedRouteId();
                 if (!TextUtils.isEmpty(activeBtDeviceAddress)) {
                     for (int i = mBluetoothRoutes.size() - 1; i >= 0; i--) {
                         MediaRoute2Info route = mBluetoothRoutes.get(i);
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index a45a996..19e6062 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -45,7 +45,6 @@
 import static android.view.WindowManager.LayoutParams.LAST_APPLICATION_WINDOW;
 import static android.view.WindowManager.LayoutParams.LAST_SUB_WINDOW;
 import static android.view.WindowManager.LayoutParams.LAST_SYSTEM_WINDOW;
-import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_IS_ROUNDED_CORNERS_OVERLAY;
 import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_SYSTEM_ERROR;
 import static android.view.WindowManager.LayoutParams.TYPE_ACCESSIBILITY_OVERLAY;
 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
@@ -2058,11 +2057,8 @@
 
     /** {@inheritDoc} */
     @Override
-    public int checkAddPermission(WindowManager.LayoutParams attrs, int[] outAppOp) {
-        final int type = attrs.type;
-        final boolean isRoundedCornerOverlay =
-                (attrs.privateFlags & PRIVATE_FLAG_IS_ROUNDED_CORNERS_OVERLAY) != 0;
-
+    public int checkAddPermission(int type, boolean isRoundedCornerOverlay, String packageName,
+            int[] outAppOp) {
         if (isRoundedCornerOverlay && mContext.checkCallingOrSelfPermission(INTERNAL_SYSTEM_WINDOW)
                 != PERMISSION_GRANTED) {
             return ADD_PERMISSION_DENIED;
@@ -2119,7 +2115,7 @@
         ApplicationInfo appInfo;
         try {
             appInfo = mPackageManager.getApplicationInfoAsUser(
-                            attrs.packageName,
+                            packageName,
                             0 /* flags */,
                             UserHandle.getUserId(callingUid));
         } catch (PackageManager.NameNotFoundException e) {
@@ -2139,7 +2135,7 @@
 
         // check if user has enabled this operation. SecurityException will be thrown if this app
         // has not been allowed by the user
-        final int mode = mAppOpsManager.noteOpNoThrow(outAppOp[0], callingUid, attrs.packageName);
+        final int mode = mAppOpsManager.noteOpNoThrow(outAppOp[0], callingUid, packageName);
         switch (mode) {
             case AppOpsManager.MODE_ALLOWED:
             case AppOpsManager.MODE_IGNORED:
diff --git a/services/core/java/com/android/server/policy/SoftRestrictedPermissionPolicy.java b/services/core/java/com/android/server/policy/SoftRestrictedPermissionPolicy.java
index a83c58d..740472e 100644
--- a/services/core/java/com/android/server/policy/SoftRestrictedPermissionPolicy.java
+++ b/services/core/java/com/android/server/policy/SoftRestrictedPermissionPolicy.java
@@ -35,14 +35,17 @@
 import android.content.Context;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageManager;
+import android.content.pm.PackageManagerInternal;
 import android.os.Binder;
 import android.os.IBinder;
 import android.os.RemoteException;
 import android.os.ServiceManager;
 import android.os.UserHandle;
+import android.os.storage.StorageManagerInternal;
 import android.util.Log;
 
 import com.android.internal.compat.IPlatformCompat;
+import com.android.server.LocalServices;
 
 /**
  * The behavior of soft restricted permissions is different for each permission. This class collects
@@ -107,11 +110,16 @@
                 final boolean isWhiteListed;
                 boolean shouldApplyRestriction;
                 final boolean hasRequestedLegacyExternalStorage;
+                final boolean shouldPreserveLegacyExternalStorage;
                 final boolean hasWriteMediaStorageGrantedForUid;
                 final boolean isScopedStorageEnabled;
 
                 if (appInfo != null) {
                     PackageManager pm = context.getPackageManager();
+                    PackageManagerInternal pmInternal =
+                            LocalServices.getService(PackageManagerInternal.class);
+                    StorageManagerInternal smInternal =
+                            LocalServices.getService(StorageManagerInternal.class);
                     int flags = pm.getPermissionFlags(permission, appInfo.packageName, user);
                     isWhiteListed = (flags & FLAGS_PERMISSION_RESTRICTION_ANY_EXEMPT) != 0;
                     hasRequestedLegacyExternalStorage = hasUidRequestedLegacyExternalStorage(
@@ -123,12 +131,16 @@
                     isScopedStorageEnabled =
                             isChangeEnabledForUid(context, appInfo, user, ENABLE_SCOPED_STORAGE)
                             || isScopedStorageRequired;
+                    shouldPreserveLegacyExternalStorage = pmInternal.getPackage(
+                            appInfo.packageName).hasPreserveLegacyExternalStorage()
+                            && smInternal.hasLegacyExternalStorage(appInfo.uid);
                     shouldApplyRestriction = (flags & FLAG_PERMISSION_APPLY_RESTRICTION) != 0
-                            || isScopedStorageRequired;
+                            || (isScopedStorageRequired && !shouldPreserveLegacyExternalStorage);
                 } else {
                     isWhiteListed = false;
                     shouldApplyRestriction = false;
                     hasRequestedLegacyExternalStorage = false;
+                    shouldPreserveLegacyExternalStorage = false;
                     hasWriteMediaStorageGrantedForUid = false;
                     isScopedStorageEnabled = false;
                 }
@@ -150,7 +162,8 @@
                     public boolean mayAllowExtraAppOp() {
                         return !shouldApplyRestriction
                                 && (hasRequestedLegacyExternalStorage
-                                        || hasWriteMediaStorageGrantedForUid);
+                                        || hasWriteMediaStorageGrantedForUid
+                                        || shouldPreserveLegacyExternalStorage);
                     }
                     @Override
                     public boolean mayDenyExtraAppOpIfGranted() {
diff --git a/services/core/java/com/android/server/policy/WindowManagerPolicy.java b/services/core/java/com/android/server/policy/WindowManagerPolicy.java
index 39093ae..9d417c9 100644
--- a/services/core/java/com/android/server/policy/WindowManagerPolicy.java
+++ b/services/core/java/com/android/server/policy/WindowManagerPolicy.java
@@ -686,17 +686,25 @@
             WindowManagerFuncs windowManagerFuncs);
 
     /**
-     * Check permissions when adding a window.
+     * Check permissions when adding a window or a window token from
+     * {@link android.app.WindowContext}.
      *
-     * @param attrs The window's LayoutParams.
+     * @param type The window type
+     * @param isRoundedCornerOverlay {@code true} to indicate the adding window is
+     *                                           round corner overlay.
+     * @param packageName package name
      * @param outAppOp First element will be filled with the app op corresponding to
      *                 this window, or OP_NONE.
      *
      * @return {@link WindowManagerGlobal#ADD_OKAY} if the add can proceed;
      *      else an error code, usually
      *      {@link WindowManagerGlobal#ADD_PERMISSION_DENIED}, to abort the add.
+     *
+     * @see IWindowManager#addWindowTokenWithOptions(IBinder, int, int, Bundle, String)
+     * @see WindowManager.LayoutParams#PRIVATE_FLAG_IS_ROUNDED_CORNERS_OVERLAY
      */
-    public int checkAddPermission(WindowManager.LayoutParams attrs, int[] outAppOp);
+    int checkAddPermission(int type, boolean isRoundedCornerOverlay, String packageName,
+            int[] outAppOp);
 
     /**
      * After the window manager has computed the current configuration based
diff --git a/services/core/java/com/android/server/wm/AppTransitionController.java b/services/core/java/com/android/server/wm/AppTransitionController.java
index f72020e..3f4e791 100644
--- a/services/core/java/com/android/server/wm/AppTransitionController.java
+++ b/services/core/java/com/android/server/wm/AppTransitionController.java
@@ -713,7 +713,11 @@
 
         final WindowState wallpaperTarget = mWallpaperControllerLocked.getWallpaperTarget();
         final boolean showWallpaper = wallpaperTarget != null
-                && (wallpaperTarget.mAttrs.flags & FLAG_SHOW_WALLPAPER) != 0;
+                && ((wallpaperTarget.mAttrs.flags & FLAG_SHOW_WALLPAPER) != 0
+                // Update task open transition to wallpaper transition when wallpaper is visible.
+                // (i.e.launching app info activity from recent tasks)
+                || ((transit == TRANSIT_TASK_OPEN || transit == TRANSIT_TASK_TO_FRONT)
+                && mWallpaperControllerLocked.isWallpaperVisible()));
         // If wallpaper is animating or wallpaperTarget doesn't have SHOW_WALLPAPER flag set,
         // don't consider upgrading to wallpaper transition.
         final WindowState oldWallpaper =
diff --git a/services/core/java/com/android/server/wm/RecentsAnimationController.java b/services/core/java/com/android/server/wm/RecentsAnimationController.java
index be68c5d..634d7e9 100644
--- a/services/core/java/com/android/server/wm/RecentsAnimationController.java
+++ b/services/core/java/com/android/server/wm/RecentsAnimationController.java
@@ -742,8 +742,10 @@
     }
 
     boolean isWallpaperVisible(WindowState w) {
-        return w != null && w.mAttrs.type == TYPE_BASE_APPLICATION && w.mActivityRecord != null
-                && mTargetActivityRecord == w.mActivityRecord && isTargetOverWallpaper();
+        return w != null && w.mAttrs.type == TYPE_BASE_APPLICATION &&
+                ((w.mActivityRecord != null && mTargetActivityRecord == w.mActivityRecord)
+                        || isAnimatingTask(w.getTask()))
+                && isTargetOverWallpaper();
     }
 
     /**
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 1b0d177..e85261d 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -1336,7 +1336,10 @@
             DisplayCutout.ParcelableWrapper outDisplayCutout, InputChannel outInputChannel,
             InsetsState outInsetsState) {
         int[] appOp = new int[1];
-        int res = mPolicy.checkAddPermission(attrs, appOp);
+        final boolean isRoundedCornerOverlay = (attrs.privateFlags
+                & PRIVATE_FLAG_IS_ROUNDED_CORNERS_OVERLAY) != 0;
+        int res = mPolicy.checkAddPermission(attrs.type, isRoundedCornerOverlay, attrs.packageName,
+                appOp);
         if (res != WindowManagerGlobal.ADD_OKAY) {
             return res;
         }
@@ -1410,8 +1413,6 @@
                     return WindowManagerGlobal.ADD_BAD_APP_TOKEN;
                 }
                 final IBinder binder = attrs.token != null ? attrs.token : client.asBinder();
-                final boolean isRoundedCornerOverlay =
-                        (attrs.privateFlags & PRIVATE_FLAG_IS_ROUNDED_CORNERS_OVERLAY) != 0;
                 token = new WindowToken(this, binder, type, false, displayContent,
                         session.mCanAddInternalSystemWindow, isRoundedCornerOverlay);
             } else if (rootType >= FIRST_APPLICATION_WINDOW
@@ -2560,10 +2561,8 @@
         final boolean callerCanManageAppTokens =
                 checkCallingPermission(MANAGE_APP_TOKENS, "addWindowToken()");
         if (!callerCanManageAppTokens) {
-            // TODO(window-context): refactor checkAddPermission to not take attrs.
-            LayoutParams attrs = new LayoutParams(type);
-            attrs.packageName = packageName;
-            final int res = mPolicy.checkAddPermission(attrs, new int[1]);
+            final int res = mPolicy.checkAddPermission(type, false /* isRoundedCornerOverlay */,
+                    packageName, new int[1]);
             if (res != ADD_OKAY) {
                 return res;
             }
diff --git a/services/tests/wmtests/src/com/android/server/wm/TestWindowManagerPolicy.java b/services/tests/wmtests/src/com/android/server/wm/TestWindowManagerPolicy.java
index 3a68924..7a075a2 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TestWindowManagerPolicy.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TestWindowManagerPolicy.java
@@ -75,7 +75,8 @@
     }
 
     @Override
-    public int checkAddPermission(WindowManager.LayoutParams attrs, int[] outAppOp) {
+    public int checkAddPermission(int type, boolean isRoundedCornerOverlay, String packageName,
+            int[] outAppOp) {
         return 0;
     }