Merge "ContentCaptureManager should be null when the feature is disabled."
diff --git a/Android.bp b/Android.bp
index 995fe3e..072007b 100644
--- a/Android.bp
+++ b/Android.bp
@@ -209,6 +209,7 @@
         "core/java/android/hardware/soundtrigger/IRecognitionStatusCallback.aidl",
         "core/java/android/hardware/usb/IUsbManager.aidl",
         "core/java/android/hardware/usb/IUsbSerialReader.aidl",
+        "core/java/android/net/ICaptivePortal.aidl",
         "core/java/android/net/IConnectivityManager.aidl",
         "core/java/android/hardware/ISensorPrivacyListener.aidl",
         "core/java/android/hardware/ISensorPrivacyManager.aidl",
@@ -893,7 +894,6 @@
     srcs: [
         "core/java/android/net/ApfCapabilitiesParcelable.aidl",
         "core/java/android/net/DhcpResultsParcelable.aidl",
-        "core/java/android/net/ICaptivePortal.aidl",
         "core/java/android/net/INetworkMonitor.aidl",
         "core/java/android/net/INetworkMonitorCallbacks.aidl",
         "core/java/android/net/IIpMemoryStore.aidl",
diff --git a/api/current.txt b/api/current.txt
index 15c4fdf..9cd33e6 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -2222,6 +2222,7 @@
     field public static final int TextAppearance_WindowTitle = 16973907; // 0x1030053
     field public static final int Theme = 16973829; // 0x1030005
     field public static final int ThemeOverlay = 16974407; // 0x1030247
+    field public static final int ThemeOverlay_DeviceDefault_Accent_DayNight = 16974564; // 0x10302e4
     field public static final int ThemeOverlay_Material = 16974408; // 0x1030248
     field public static final int ThemeOverlay_Material_ActionBar = 16974409; // 0x1030249
     field public static final int ThemeOverlay_Material_Dark = 16974411; // 0x103024b
@@ -2233,6 +2234,7 @@
     field public static final int Theme_Black_NoTitleBar = 16973833; // 0x1030009
     field public static final int Theme_Black_NoTitleBar_Fullscreen = 16973834; // 0x103000a
     field public static final int Theme_DeviceDefault = 16974120; // 0x1030128
+    field public static final int Theme_DeviceDefault_DayNight = 16974563; // 0x10302e3
     field public static final int Theme_DeviceDefault_Dialog = 16974126; // 0x103012e
     field public static final int Theme_DeviceDefault_DialogWhenLarge = 16974134; // 0x1030136
     field public static final int Theme_DeviceDefault_DialogWhenLarge_NoActionBar = 16974135; // 0x1030137
@@ -13064,6 +13066,10 @@
     method public String buildUnionSubQuery(String, String[], java.util.Set<java.lang.String>, int, String, String, String, String);
     method @Deprecated public String buildUnionSubQuery(String, String[], java.util.Set<java.lang.String>, int, String, String, String[], String, String);
     method public int delete(@NonNull android.database.sqlite.SQLiteDatabase, @Nullable String, @Nullable String[]);
+    method public android.database.sqlite.SQLiteDatabase.CursorFactory getCursorFactory();
+    method public boolean getDistinct();
+    method public java.util.Map<java.lang.String,java.lang.String> getProjectionMap();
+    method public boolean getStrict();
     method public String getTables();
     method public android.database.Cursor query(android.database.sqlite.SQLiteDatabase, String[], String, String[], String, String, String);
     method public android.database.Cursor query(android.database.sqlite.SQLiteDatabase, String[], String, String[], String, String, String, String);
@@ -23408,7 +23414,7 @@
     method @NonNull public android.media.AudioPresentation.Builder setProgramId(int);
   }
 
-  public class AudioRecord implements android.media.AudioRecordingMonitor android.media.AudioRouting {
+  public class AudioRecord implements android.media.AudioRecordingMonitor android.media.AudioRouting android.media.MicrophoneDirection {
     ctor public AudioRecord(int, int, int, int, int) throws java.lang.IllegalArgumentException;
     method public void addOnRoutingChangedListener(android.media.AudioRouting.OnRoutingChangedListener, android.os.Handler);
     method @Deprecated public void addOnRoutingChangedListener(android.media.AudioRecord.OnRoutingChangedListener, android.os.Handler);
@@ -23443,6 +23449,8 @@
     method public void release();
     method public void removeOnRoutingChangedListener(android.media.AudioRouting.OnRoutingChangedListener);
     method @Deprecated public void removeOnRoutingChangedListener(android.media.AudioRecord.OnRoutingChangedListener);
+    method public boolean setMicrophoneDirection(int);
+    method public boolean setMicrophoneFieldDimension(@FloatRange(from=-1.0, to=1.0) float);
     method public int setNotificationMarkerPosition(int);
     method public int setPositionNotificationPeriod(int);
     method public boolean setPreferredDevice(android.media.AudioDeviceInfo);
@@ -26092,6 +26100,15 @@
     field public static final android.media.MediaTimestamp TIMESTAMP_UNKNOWN;
   }
 
+  public interface MicrophoneDirection {
+    method public boolean setMicrophoneDirection(int);
+    method public boolean setMicrophoneFieldDimension(@FloatRange(from=-1.0, to=1.0) float);
+    field public static final int MIC_DIRECTION_BACK = 2; // 0x2
+    field public static final int MIC_DIRECTION_EXTERNAL = 3; // 0x3
+    field public static final int MIC_DIRECTION_FRONT = 1; // 0x1
+    field public static final int MIC_DIRECTION_UNSPECIFIED = 0; // 0x0
+  }
+
   public final class MicrophoneInfo {
     method @NonNull public String getAddress();
     method public java.util.List<android.util.Pair<java.lang.Integer,java.lang.Integer>> getChannelMapping();
@@ -41588,6 +41605,7 @@
     field public static final android.os.Parcelable.Creator<android.service.notification.Adjustment> CREATOR;
     field public static final String KEY_CONTEXTUAL_ACTIONS = "key_contextual_actions";
     field public static final String KEY_IMPORTANCE = "key_importance";
+    field public static final String KEY_SNOOZE_CRITERIA = "key_snooze_criteria";
     field public static final String KEY_TEXT_REPLIES = "key_text_replies";
     field public static final String KEY_USER_SENTIMENT = "key_user_sentiment";
   }
@@ -41645,12 +41663,14 @@
     method public void onActionInvoked(@NonNull String, @NonNull android.app.Notification.Action, int);
     method public final android.os.IBinder onBind(android.content.Intent);
     method public void onNotificationDirectReplied(@NonNull String);
-    method public android.service.notification.Adjustment onNotificationEnqueued(android.service.notification.StatusBarNotification);
+    method public abstract android.service.notification.Adjustment onNotificationEnqueued(android.service.notification.StatusBarNotification);
     method public android.service.notification.Adjustment onNotificationEnqueued(android.service.notification.StatusBarNotification, android.app.NotificationChannel);
     method public void onNotificationExpansionChanged(@NonNull String, boolean, boolean);
     method public void onNotificationRemoved(android.service.notification.StatusBarNotification, android.service.notification.NotificationListenerService.RankingMap, android.service.notification.NotificationStats, int);
+    method public abstract void onNotificationSnoozedUntilContext(android.service.notification.StatusBarNotification, String);
     method public void onNotificationsSeen(java.util.List<java.lang.String>);
     method public void onSuggestedReplySent(@NonNull String, @NonNull CharSequence, int);
+    method public final void unsnoozeNotification(String);
     field public static final String SERVICE_INTERFACE = "android.service.notification.NotificationAssistantService";
     field public static final int SOURCE_FROM_APP = 0; // 0x0
     field public static final int SOURCE_FROM_ASSISTANT = 1; // 0x1
diff --git a/api/system-current.txt b/api/system-current.txt
index 357f6a4..33b1586 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -3475,7 +3475,7 @@
     field public static final int PLAYER_TYPE_UNKNOWN = -1; // 0xffffffff
   }
 
-  public class AudioRecord implements android.media.AudioRecordingMonitor android.media.AudioRouting {
+  public class AudioRecord implements android.media.AudioRecordingMonitor android.media.AudioRouting android.media.MicrophoneDirection {
     ctor public AudioRecord(android.media.AudioAttributes, android.media.AudioFormat, int, int) throws java.lang.IllegalArgumentException;
   }
 
@@ -3933,7 +3933,7 @@
     method @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public void getLatestTetheringEntitlementValue(int, boolean, @NonNull android.net.ConnectivityManager.TetheringEntitlementValueListener, @Nullable android.os.Handler);
     method @RequiresPermission(anyOf={android.Manifest.permission.TETHER_PRIVILEGED, android.Manifest.permission.WRITE_SETTINGS}) public boolean isTetheringSupported();
     method @RequiresPermission(anyOf={"android.permission.NETWORK_SETTINGS", android.Manifest.permission.NETWORK_SETUP_WIZARD, "android.permission.NETWORK_STACK"}) public void setAirplaneMode(boolean);
-    method @RequiresPermission(android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK) public void startCaptivePortalApp(android.os.Bundle);
+    method @RequiresPermission(android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK) public void startCaptivePortalApp(android.net.Network, android.os.Bundle);
     method @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public void startTethering(int, boolean, android.net.ConnectivityManager.OnStartTetheringCallback);
     method @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public void startTethering(int, boolean, android.net.ConnectivityManager.OnStartTetheringCallback, android.os.Handler);
     method @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public void stopTethering(int);
@@ -6350,7 +6350,8 @@
     method public void onDestroyContentCaptureSession(@NonNull android.view.contentcapture.ContentCaptureSessionId);
     method public void onDisconnected();
     method public void onUserDataRemovalRequest(@NonNull android.view.contentcapture.UserDataRemovalRequest);
-    method public final void setContentCaptureWhitelist(@Nullable java.util.List<java.lang.String>, @Nullable java.util.List<android.content.ComponentName>);
+    method @Deprecated public final void setContentCaptureWhitelist(@Nullable java.util.List<java.lang.String>, @Nullable java.util.List<android.content.ComponentName>);
+    method public final void setContentCaptureWhitelist(@Nullable java.util.Set<java.lang.String>, @Nullable java.util.Set<android.content.ComponentName>);
     field public static final String SERVICE_INTERFACE = "android.service.contentcapture.ContentCaptureService";
   }
 
@@ -6521,6 +6522,15 @@
 
 package android.service.notification {
 
+  public final class Adjustment implements android.os.Parcelable {
+    ctor protected Adjustment(android.os.Parcel);
+    field public static final String KEY_PEOPLE = "key_people";
+  }
+
+  public final class NotificationStats implements android.os.Parcelable {
+    ctor protected NotificationStats(android.os.Parcel);
+  }
+
   public final class SnoozeCriterion implements android.os.Parcelable {
     ctor public SnoozeCriterion(String, CharSequence, CharSequence);
     ctor protected SnoozeCriterion(android.os.Parcel);
@@ -9307,7 +9317,8 @@
 package android.view.autofill {
 
   public final class AutofillManager {
-    method public void setAugmentedAutofillWhitelist(@Nullable java.util.List<java.lang.String>, @Nullable java.util.List<android.content.ComponentName>);
+    method @Deprecated public void setAugmentedAutofillWhitelist(@Nullable java.util.List<java.lang.String>, @Nullable java.util.List<android.content.ComponentName>);
+    method public void setAugmentedAutofillWhitelist(@Nullable java.util.Set<java.lang.String>, @Nullable java.util.Set<android.content.ComponentName>);
   }
 
 }
diff --git a/api/test-current.txt b/api/test-current.txt
index 47d38a7..d91ad44 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -516,13 +516,20 @@
     method @RequiresPermission("android.permission.INTERACT_ACROSS_USERS_FULL") public abstract java.util.List<android.content.pm.PackageInfo> getInstalledPackagesAsUser(int, int);
     method @Nullable public abstract String[] getNamesForUids(int[]);
     method public abstract String getPermissionControllerPackageName();
+    method @RequiresPermission(anyOf={"android.permission.GRANT_RUNTIME_PERMISSIONS", "android.permission.REVOKE_RUNTIME_PERMISSIONS"}) public abstract int getPermissionFlags(String, String, @NonNull android.os.UserHandle);
     method @NonNull public abstract String getServicesSystemSharedLibraryPackageName();
     method @NonNull public abstract String getSharedSystemSharedLibraryPackageName();
     method public String getWellbeingPackageName();
     method @RequiresPermission("android.permission.GRANT_RUNTIME_PERMISSIONS") public abstract void grantRuntimePermission(@NonNull String, @NonNull String, @NonNull android.os.UserHandle);
     method @RequiresPermission("android.permission.REVOKE_RUNTIME_PERMISSIONS") public abstract void revokeRuntimePermission(@NonNull String, @NonNull String, @NonNull android.os.UserHandle);
+    method @RequiresPermission(anyOf={"android.permission.GRANT_RUNTIME_PERMISSIONS", "android.permission.REVOKE_RUNTIME_PERMISSIONS"}) public abstract void updatePermissionFlags(String, String, int, int, @NonNull android.os.UserHandle);
     field public static final String FEATURE_ADOPTABLE_STORAGE = "android.software.adoptable_storage";
     field public static final String FEATURE_FILE_BASED_ENCRYPTION = "android.software.file_based_encryption";
+    field public static final int FLAG_PERMISSION_REVIEW_REQUIRED = 64; // 0x40
+    field public static final int FLAG_PERMISSION_REVOKE_ON_UPGRADE = 8; // 0x8
+    field public static final int FLAG_PERMISSION_REVOKE_WHEN_REQUESTED = 128; // 0x80
+    field public static final int FLAG_PERMISSION_USER_FIXED = 2; // 0x2
+    field public static final int FLAG_PERMISSION_USER_SET = 1; // 0x1
     field public static final int MATCH_FACTORY_ONLY = 2097152; // 0x200000
     field public static final int MATCH_KNOWN_PACKAGES = 4202496; // 0x402000
     field public static final String SYSTEM_SHARED_LIBRARY_SERVICES = "android.ext.services";
@@ -802,6 +809,7 @@
 
   public class LocationManager {
     method public String[] getBackgroundThrottlingWhitelist();
+    method public String[] getIgnoreSettingsWhitelist();
     method @RequiresPermission(anyOf={android.Manifest.permission.ACCESS_COARSE_LOCATION, android.Manifest.permission.ACCESS_FINE_LOCATION}) public void requestLocationUpdates(android.location.LocationRequest, android.location.LocationListener, android.os.Looper);
     method @RequiresPermission(anyOf={android.Manifest.permission.ACCESS_COARSE_LOCATION, android.Manifest.permission.ACCESS_FINE_LOCATION}) public void requestLocationUpdates(android.location.LocationRequest, android.app.PendingIntent);
     method @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public void setLocationEnabledForUser(boolean, android.os.UserHandle);
@@ -936,7 +944,7 @@
   }
 
   public class ConnectivityManager {
-    method @RequiresPermission(android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK) public void startCaptivePortalApp(android.os.Bundle);
+    method @RequiresPermission(android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK) public void startCaptivePortalApp(android.net.Network, android.os.Bundle);
     field public static final String EXTRA_CAPTIVE_PORTAL_PROBE_SPEC = "android.net.extra.CAPTIVE_PORTAL_PROBE_SPEC";
     field public static final String EXTRA_CAPTIVE_PORTAL_USER_AGENT = "android.net.extra.CAPTIVE_PORTAL_USER_AGENT";
   }
@@ -2103,7 +2111,8 @@
     method public void onDestroyContentCaptureSession(@NonNull android.view.contentcapture.ContentCaptureSessionId);
     method public void onDisconnected();
     method public void onUserDataRemovalRequest(@NonNull android.view.contentcapture.UserDataRemovalRequest);
-    method public final void setContentCaptureWhitelist(@Nullable java.util.List<java.lang.String>, @Nullable java.util.List<android.content.ComponentName>);
+    method @Deprecated public final void setContentCaptureWhitelist(@Nullable java.util.List<java.lang.String>, @Nullable java.util.List<android.content.ComponentName>);
+    method public final void setContentCaptureWhitelist(@Nullable java.util.Set<java.lang.String>, @Nullable java.util.Set<android.content.ComponentName>);
     field public static final String SERVICE_INTERFACE = "android.service.contentcapture.ContentCaptureService";
   }
 
@@ -2692,7 +2701,8 @@
   }
 
   public final class AutofillManager {
-    method public void setAugmentedAutofillWhitelist(@Nullable java.util.List<java.lang.String>, @Nullable java.util.List<android.content.ComponentName>);
+    method @Deprecated public void setAugmentedAutofillWhitelist(@Nullable java.util.List<java.lang.String>, @Nullable java.util.List<android.content.ComponentName>);
+    method public void setAugmentedAutofillWhitelist(@Nullable java.util.Set<java.lang.String>, @Nullable java.util.Set<android.content.ComponentName>);
     field public static final String DEVICE_CONFIG_AUTOFILL_SMART_SUGGESTION_SUPPORTED_MODES = "smart_suggestion_supported_modes";
     field public static final int FLAG_SMART_SUGGESTION_OFF = 0; // 0x0
     field public static final int FLAG_SMART_SUGGESTION_SYSTEM = 1; // 0x1
diff --git a/cmds/bootanimation/BootAnimation.cpp b/cmds/bootanimation/BootAnimation.cpp
index 46917e4..a6c7cae 100644
--- a/cmds/bootanimation/BootAnimation.cpp
+++ b/cmds/bootanimation/BootAnimation.cpp
@@ -110,13 +110,30 @@
     } else {
         mShuttingDown = true;
     }
+    ALOGD("%sAnimationStartTiming start time: %" PRId64 "ms", mShuttingDown ? "Shutdown" : "Boot",
+            elapsedRealtime());
+}
+
+BootAnimation::~BootAnimation() {
+    if (mAnimation != nullptr) {
+        releaseAnimation(mAnimation);
+        mAnimation = nullptr;
+    }
+    ALOGD("%sAnimationStopTiming start time: %" PRId64 "ms", mShuttingDown ? "Shutdown" : "Boot",
+            elapsedRealtime());
 }
 
 void BootAnimation::onFirstRef() {
     status_t err = mSession->linkToComposerDeath(this);
     SLOGE_IF(err, "linkToComposerDeath failed (%s) ", strerror(-err));
     if (err == NO_ERROR) {
-        run("BootAnimation", PRIORITY_DISPLAY);
+        // Load the animation content -- this can be slow (eg 200ms)
+        // called before waitForSurfaceFlinger() in main() to avoid wait
+        ALOGD("%sAnimationPreloadTiming start time: %" PRId64 "ms",
+                mShuttingDown ? "Shutdown" : "Boot", elapsedRealtime());
+        preloadAnimation();
+        ALOGD("%sAnimationPreloadStopTiming start time: %" PRId64 "ms",
+                mShuttingDown ? "Shutdown" : "Boot", elapsedRealtime());
     }
 }
 
@@ -306,6 +323,20 @@
     mFlingerSurface = s;
     mTargetInset = -1;
 
+    return NO_ERROR;
+}
+
+bool BootAnimation::preloadAnimation() {
+    findBootAnimationFile();
+    if (!mZipFileName.isEmpty()) {
+        mAnimation = loadAnimation(mZipFileName);
+        return (mAnimation != nullptr);
+    }
+
+    return false;
+}
+
+void BootAnimation::findBootAnimationFile() {
     // If the device has encryption turned on or is in process
     // of being encrypted we show the encrypted boot animation.
     char decrypt[PROPERTY_VALUE_MAX];
@@ -320,7 +351,7 @@
         for (const char* f : encryptedBootFiles) {
             if (access(f, R_OK) == 0) {
                 mZipFileName = f;
-                return NO_ERROR;
+                return;
             }
         }
     }
@@ -332,10 +363,9 @@
     for (const char* f : (!mShuttingDown ? bootFiles : shutdownFiles)) {
         if (access(f, R_OK) == 0) {
             mZipFileName = f;
-            return NO_ERROR;
+            return;
         }
     }
-    return NO_ERROR;
 }
 
 bool BootAnimation::threadLoop()
@@ -790,8 +820,6 @@
         }
     }
 
-    mCallbacks->init(animation.parts);
-
     zip->endIteration(cookie);
 
     return true;
@@ -799,13 +827,25 @@
 
 bool BootAnimation::movie()
 {
-    Animation* animation = loadAnimation(mZipFileName);
-    if (animation == NULL)
+    if (mAnimation == nullptr) {
+        mAnimation = loadAnimation(mZipFileName);
+    }
+
+    if (mAnimation == nullptr)
         return false;
 
+    // mCallbacks->init() may get called recursively,
+    // this loop is needed to get the same results
+    for (const Animation::Part& part : mAnimation->parts) {
+        if (part.animation != nullptr) {
+            mCallbacks->init(part.animation->parts);
+        }
+    }
+    mCallbacks->init(mAnimation->parts);
+
     bool anyPartHasClock = false;
-    for (size_t i=0; i < animation->parts.size(); i++) {
-        if(validClock(animation->parts[i])) {
+    for (size_t i=0; i < mAnimation->parts.size(); i++) {
+        if(validClock(mAnimation->parts[i])) {
             anyPartHasClock = true;
             break;
         }
@@ -846,7 +886,7 @@
     bool clockFontInitialized = false;
     if (mClockEnabled) {
         clockFontInitialized =
-            (initFont(&animation->clockFont, CLOCK_FONT_ASSET) == NO_ERROR);
+            (initFont(&mAnimation->clockFont, CLOCK_FONT_ASSET) == NO_ERROR);
         mClockEnabled = clockFontInitialized;
     }
 
@@ -855,7 +895,7 @@
         mTimeCheckThread->run("BootAnimation::TimeCheckThread", PRIORITY_NORMAL);
     }
 
-    playAnimation(*animation);
+    playAnimation(*mAnimation);
 
     if (mTimeCheckThread != nullptr) {
         mTimeCheckThread->requestExit();
@@ -863,10 +903,11 @@
     }
 
     if (clockFontInitialized) {
-        glDeleteTextures(1, &animation->clockFont.texture.name);
+        glDeleteTextures(1, &mAnimation->clockFont.texture.name);
     }
 
-    releaseAnimation(animation);
+    releaseAnimation(mAnimation);
+    mAnimation = nullptr;
 
     return false;
 }
diff --git a/cmds/bootanimation/BootAnimation.h b/cmds/bootanimation/BootAnimation.h
index 19616cb..dc19fb0 100644
--- a/cmds/bootanimation/BootAnimation.h
+++ b/cmds/bootanimation/BootAnimation.h
@@ -115,6 +115,7 @@
     };
 
     explicit BootAnimation(sp<Callbacks> callbacks);
+    virtual ~BootAnimation();
 
     sp<SurfaceComposerClient> session() const;
 
@@ -155,6 +156,8 @@
     void releaseAnimation(Animation*) const;
     bool parseAnimationDesc(Animation&);
     bool preloadZip(Animation &animation);
+    void findBootAnimationFile();
+    bool preloadAnimation();
 
     void checkExit();
 
@@ -182,6 +185,7 @@
     SortedVector<String8> mLoadedFiles;
     sp<TimeCheckThread> mTimeCheckThread = nullptr;
     sp<Callbacks> mCallbacks;
+    Animation* mAnimation = nullptr;
 };
 
 // ---------------------------------------------------------------------------
diff --git a/cmds/bootanimation/bootanimation_main.cpp b/cmds/bootanimation/bootanimation_main.cpp
index a52a5e9..6c7b3e5 100644
--- a/cmds/bootanimation/bootanimation_main.cpp
+++ b/cmds/bootanimation/bootanimation_main.cpp
@@ -44,14 +44,16 @@
         sp<ProcessState> proc(ProcessState::self());
         ProcessState::self()->startThreadPool();
 
+        // create the boot animation object (may take up to 200ms for 2MB zip)
+        sp<BootAnimation> boot = new BootAnimation(audioplay::createAnimationCallbacks());
+
         waitForSurfaceFlinger();
 
-        // create the boot animation object
-        sp<BootAnimation> boot = new BootAnimation(audioplay::createAnimationCallbacks());
+        boot->run("BootAnimation", PRIORITY_DISPLAY);
+
         ALOGV("Boot animation set up. Joining pool.");
 
         IPCThreadState::self()->joinThreadPool();
     }
-    ALOGV("Boot animation exit");
     return 0;
 }
diff --git a/cmds/statsd/src/FieldValue.cpp b/cmds/statsd/src/FieldValue.cpp
index 80ed807..13f5c8a 100644
--- a/cmds/statsd/src/FieldValue.cpp
+++ b/cmds/statsd/src/FieldValue.cpp
@@ -19,6 +19,7 @@
 #include "FieldValue.h"
 #include "HashableDimensionKey.h"
 #include "math.h"
+#include "statslog.h"
 
 namespace android {
 namespace os {
@@ -122,6 +123,24 @@
     return false;
 }
 
+int32_t getUidIfExists(const FieldValue& value) {
+    bool isUid = false;
+    // the field is uid field if the field is the uid field in attribution node or marked as
+    // is_uid in atoms.proto
+    if (isAttributionUidField(value)) {
+        isUid = true;
+    } else {
+        auto it = android::util::AtomsInfo::kAtomsWithUidField.find(value.mField.getTag());
+        if (it != android::util::AtomsInfo::kAtomsWithUidField.end()) {
+            int uidField = it->second;  // uidField is the field number in proto
+            isUid = value.mField.getDepth() == 0 && value.mField.getPosAtDepth(0) == uidField &&
+                    value.mValue.getType() == INT;
+        }
+    }
+
+    return isUid ? value.mValue.int_value : -1;
+}
+
 bool isAttributionUidField(const Field& field, const Value& value) {
     int f = field.getField() & 0xff007f;
     if (f == 0x10001 && value.getType() == INT) {
diff --git a/cmds/statsd/src/FieldValue.h b/cmds/statsd/src/FieldValue.h
index a5d00ac..6729e05 100644
--- a/cmds/statsd/src/FieldValue.h
+++ b/cmds/statsd/src/FieldValue.h
@@ -386,6 +386,9 @@
 
 bool isAttributionUidField(const FieldValue& value);
 
+/* returns uid if the field is uid field, or -1 if the field is not a uid field */
+int getUidIfExists(const FieldValue& value);
+
 void translateFieldMatcher(const FieldMatcher& matcher, std::vector<Matcher>* output);
 
 bool isAttributionUidField(const Field& field, const Value& value);
diff --git a/cmds/statsd/src/StatsService.cpp b/cmds/statsd/src/StatsService.cpp
index fb603b9..c542b62 100644
--- a/cmds/statsd/src/StatsService.cpp
+++ b/cmds/statsd/src/StatsService.cpp
@@ -159,7 +159,7 @@
            }
 
       }))  {
-    mUidMap = new UidMap();
+    mUidMap = UidMap::getInstance();
     mPullerManager = new StatsPullerManager();
     StatsPuller::SetUidMap(mUidMap);
     mConfigManager = new ConfigManager();
diff --git a/cmds/statsd/src/anomaly/AlarmTracker.cpp b/cmds/statsd/src/anomaly/AlarmTracker.cpp
index 8d73699..019a9f7 100644
--- a/cmds/statsd/src/anomaly/AlarmTracker.cpp
+++ b/cmds/statsd/src/anomaly/AlarmTracker.cpp
@@ -78,8 +78,8 @@
     }
     if (!mSubscriptions.empty()) {
         VLOG("AlarmTracker triggers the subscribers.");
-        triggerSubscribers(mAlarmConfig.id(), DEFAULT_METRIC_DIMENSION_KEY, mConfigKey,
-                           mSubscriptions);
+        triggerSubscribers(mAlarmConfig.id(), 0 /*metricId N/A*/, DEFAULT_METRIC_DIMENSION_KEY,
+                           0 /* metricValue N/A */, mConfigKey, mSubscriptions);
     }
     firedAlarms.erase(mInternalAlarm);
     mAlarmSec = findNextAlarmSec((timestampNs-1) / NS_PER_SEC + 1); // round up
diff --git a/cmds/statsd/src/anomaly/AnomalyTracker.cpp b/cmds/statsd/src/anomaly/AnomalyTracker.cpp
index ee111cd..d1dcb5df 100644
--- a/cmds/statsd/src/anomaly/AnomalyTracker.cpp
+++ b/cmds/statsd/src/anomaly/AnomalyTracker.cpp
@@ -207,7 +207,8 @@
            getSumOverPastBuckets(key) + currentBucketValue > mAlert.trigger_if_sum_gt();
 }
 
-void AnomalyTracker::declareAnomaly(const int64_t& timestampNs, const MetricDimensionKey& key) {
+void AnomalyTracker::declareAnomaly(const int64_t& timestampNs, int64_t metricId,
+                                    const MetricDimensionKey& key, int64_t metricValue) {
     // TODO(b/110563466): Why receive timestamp? RefractoryPeriod should always be based on
     // real time right now.
     if (isInRefractoryPeriod(timestampNs, key)) {
@@ -225,7 +226,7 @@
     if (!mSubscriptions.empty()) {
         ALOGI("An anomaly (%lld) %s has occurred! Informing subscribers.",
                 mAlert.id(), key.toString().c_str());
-        informSubscribers(key);
+        informSubscribers(key, metricId, metricValue);
     } else {
         ALOGI("An anomaly has occurred! (But no subscriber for that alert.)");
     }
@@ -238,11 +239,11 @@
 }
 
 void AnomalyTracker::detectAndDeclareAnomaly(const int64_t& timestampNs,
-                                             const int64_t& currBucketNum,
+                                             const int64_t& currBucketNum, int64_t metricId,
                                              const MetricDimensionKey& key,
                                              const int64_t& currentBucketValue) {
     if (detectAnomaly(currBucketNum, key, currentBucketValue)) {
-        declareAnomaly(timestampNs, key);
+        declareAnomaly(timestampNs, metricId, key, currentBucketValue);
     }
 }
 
@@ -255,8 +256,9 @@
     return false;
 }
 
-void AnomalyTracker::informSubscribers(const MetricDimensionKey& key) {
-    triggerSubscribers(mAlert.id(), key, mConfigKey, mSubscriptions);
+void AnomalyTracker::informSubscribers(const MetricDimensionKey& key, int64_t metric_id,
+                                       int64_t metricValue) {
+    triggerSubscribers(mAlert.id(), metric_id, key, metricValue, mConfigKey, mSubscriptions);
 }
 
 }  // namespace statsd
diff --git a/cmds/statsd/src/anomaly/AnomalyTracker.h b/cmds/statsd/src/anomaly/AnomalyTracker.h
index 927e2df..e941473 100644
--- a/cmds/statsd/src/anomaly/AnomalyTracker.h
+++ b/cmds/statsd/src/anomaly/AnomalyTracker.h
@@ -67,14 +67,16 @@
                        const int64_t& currentBucketValue);
 
     // Informs incidentd about the detected alert.
-    void declareAnomaly(const int64_t& timestampNs, const MetricDimensionKey& key);
+    void declareAnomaly(const int64_t& timestampNs, int64_t metricId, const MetricDimensionKey& key,
+                        int64_t metricValue);
 
     // Detects if, based on past buckets plus the new currentBucketValue (which generally
     // represents the partially-filled current bucket), an anomaly has happened, and if so,
     // declares an anomaly and informs relevant subscribers.
     // Also advances to currBucketNum-1.
     void detectAndDeclareAnomaly(const int64_t& timestampNs, const int64_t& currBucketNum,
-                                 const MetricDimensionKey& key, const int64_t& currentBucketValue);
+                                 int64_t metricId, const MetricDimensionKey& key,
+                                 const int64_t& currentBucketValue);
 
     // Init the AlarmMonitor which is shared across anomaly trackers.
     virtual void setAlarmMonitor(const sp<AlarmMonitor>& alarmMonitor) {
@@ -176,7 +178,7 @@
     virtual void resetStorage();
 
     // Informs the subscribers (incidentd, perfetto, broadcasts, etc) that an anomaly has occurred.
-    void informSubscribers(const MetricDimensionKey& key);
+    void informSubscribers(const MetricDimensionKey& key, int64_t metricId, int64_t metricValue);
 
     FRIEND_TEST(AnomalyTrackerTest, TestConsecutiveBuckets);
     FRIEND_TEST(AnomalyTrackerTest, TestSparseBuckets);
diff --git a/cmds/statsd/src/anomaly/DurationAnomalyTracker.cpp b/cmds/statsd/src/anomaly/DurationAnomalyTracker.cpp
index 3acfd17..2b56810 100644
--- a/cmds/statsd/src/anomaly/DurationAnomalyTracker.cpp
+++ b/cmds/statsd/src/anomaly/DurationAnomalyTracker.cpp
@@ -65,7 +65,9 @@
 
     // If the alarm is set in the past but hasn't fired yet (due to lag), catch it now.
     if (itr->second != nullptr && timestampNs >= (int64_t)NS_PER_SEC * itr->second->timestampSec) {
-        declareAnomaly(timestampNs, dimensionKey);
+        declareAnomaly(timestampNs, mAlert.metric_id(), dimensionKey,
+                       mAlert.trigger_if_sum_gt() + (timestampNs / NS_PER_SEC) -
+                               itr->second->timestampSec);
     }
     if (mAlarmMonitor != nullptr) {
         mAlarmMonitor->remove(itr->second);
@@ -100,7 +102,9 @@
 
     // Now declare each of these alarms to have fired.
     for (const auto& kv : matchedAlarms) {
-        declareAnomaly(timestampNs, kv.first);
+        declareAnomaly(
+                timestampNs, mAlert.metric_id(), kv.first,
+                mAlert.trigger_if_sum_gt() + (timestampNs / NS_PER_SEC) - kv.second->timestampSec);
         mAlarms.erase(kv.first);
         firedAlarms.erase(kv.second);  // No one else can also own it, so we're done with it.
     }
diff --git a/cmds/statsd/src/anomaly/subscriber_util.cpp b/cmds/statsd/src/anomaly/subscriber_util.cpp
index 6b46b8b..548a686 100644
--- a/cmds/statsd/src/anomaly/subscriber_util.cpp
+++ b/cmds/statsd/src/anomaly/subscriber_util.cpp
@@ -30,9 +30,8 @@
 namespace os {
 namespace statsd {
 
-void triggerSubscribers(const int64_t rule_id,
-                        const MetricDimensionKey& dimensionKey,
-                        const ConfigKey& configKey,
+void triggerSubscribers(int64_t ruleId, int64_t metricId, const MetricDimensionKey& dimensionKey,
+                        int64_t metricValue, const ConfigKey& configKey,
                         const std::vector<Subscription>& subscriptions) {
     VLOG("informSubscribers called.");
     if (subscriptions.empty()) {
@@ -50,13 +49,14 @@
         }
         switch (subscription.subscriber_information_case()) {
             case Subscription::SubscriberInformationCase::kIncidentdDetails:
-                if (!GenerateIncidentReport(subscription.incidentd_details(), rule_id, configKey)) {
+                if (!GenerateIncidentReport(subscription.incidentd_details(), ruleId, metricId,
+                                            dimensionKey, metricValue, configKey)) {
                     ALOGW("Failed to generate incident report.");
                 }
                 break;
             case Subscription::SubscriberInformationCase::kPerfettoDetails:
                 if (!CollectPerfettoTraceAndUploadToDropbox(subscription.perfetto_details(),
-                                                            subscription.id(), rule_id, configKey)) {
+                                                            subscription.id(), ruleId, configKey)) {
                     ALOGW("Failed to generate perfetto traces.");
                 }
                 break;
@@ -66,7 +66,7 @@
                 break;
             case Subscription::SubscriberInformationCase::kPerfprofdDetails:
                 if (!CollectPerfprofdTraceAndUploadToDropbox(subscription.perfprofd_details(),
-                                                             rule_id, configKey)) {
+                                                             ruleId, configKey)) {
                     ALOGW("Failed to generate perfprofd traces.");
                 }
                 break;
@@ -76,7 +76,6 @@
     }
 }
 
-
 }  // namespace statsd
 }  // namespace os
 }  // namespace android
diff --git a/cmds/statsd/src/anomaly/subscriber_util.h b/cmds/statsd/src/anomaly/subscriber_util.h
index dba8981..1df3c89 100644
--- a/cmds/statsd/src/anomaly/subscriber_util.h
+++ b/cmds/statsd/src/anomaly/subscriber_util.h
@@ -24,10 +24,9 @@
 namespace os {
 namespace statsd {
 
-void triggerSubscribers(const int64_t rule_id,
-                        const MetricDimensionKey& dimensionKey,
-                        const ConfigKey& configKey,
-                        const std::vector<Subscription>& subscriptions);
+void triggerSubscribers(const int64_t ruleId, const int64_t metricId,
+                        const MetricDimensionKey& dimensionKey, int64_t metricValue,
+                        const ConfigKey& configKey, const std::vector<Subscription>& subscriptions);
 
 }  // namespace statsd
 }  // namespace os
diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto
index 873b772..2632294d 100644
--- a/cmds/statsd/src/atoms.proto
+++ b/cmds/statsd/src/atoms.proto
@@ -302,6 +302,8 @@
         RoleHolder role_holder = 10049;
         DangerousPermissionState dangerous_permission_state = 10050;
         TrainInfo train_info = 10051;
+        TimeZoneDataInfo time_zone_data_info = 10052;
+        SDCardInfo sdcard_info = 10053;
     }
 
     // DO NOT USE field numbers above 100,000 in AOSP.
@@ -3230,6 +3232,28 @@
     optional int32 cycle_count = 1;
 }
 
+/**
+ * Logs that an SD card is mounted and information about it, its type (public or private) and the
+ * size in bytes.
+ * Pulled from:
+ *   StatsCompanionService
+ */
+
+message SDCardInfo {
+
+    enum Type {
+        UNKNOWN = 0;
+        TYPE_PUBLIC = 1;
+        TYPE_PRIVATE = 2;
+        OTHERS = 3;
+    }
+
+    // Type of the SD card: TYPE_PUBLIC if portable and TYPE_PRIVATE if internal.
+    optional Type type = 1;
+    // Total size of the sd card in bytes.
+    optional int64 size_bytes = 2;
+}
+
 /*
  * Logs when a connection becomes available and lost.
  * Logged in StatsCompanionService.java
@@ -5564,3 +5588,11 @@
   // [0,100] progress for the assist gesture.
   optional int32 progress = 1;
 }
+
+/*
+ * Information about the time zone data on a device.
+ */
+message TimeZoneDataInfo {
+    // A version identifier for the data set on device. e.g. "2018i"
+    optional string tzdb_version = 1;
+}
diff --git a/cmds/statsd/src/external/StatsPullerManager.cpp b/cmds/statsd/src/external/StatsPullerManager.cpp
index 98f810f..1513834 100644
--- a/cmds/statsd/src/external/StatsPullerManager.cpp
+++ b/cmds/statsd/src/external/StatsPullerManager.cpp
@@ -234,6 +234,12 @@
          {.puller = new StatsCompanionServicePuller(android::util::DANGEROUS_PERMISSION_STATE)}},
         // TrainInfo.
         {android::util::TRAIN_INFO, {.puller = new TrainInfoPuller()}},
+        // TimeZoneDataInfo.
+        {android::util::TIME_ZONE_DATA_INFO,
+         {.puller = new StatsCompanionServicePuller(android::util::TIME_ZONE_DATA_INFO)}},
+        // SDCardInfo
+        {android::util::SDCARD_INFO,
+         {.puller = new StatsCompanionServicePuller(android::util::SDCARD_INFO)}},
 };
 
 StatsPullerManager::StatsPullerManager() : mNextPullTimeNs(NO_ALARM_UPDATE) {
diff --git a/cmds/statsd/src/metrics/CountMetricProducer.cpp b/cmds/statsd/src/metrics/CountMetricProducer.cpp
index 5707de5..e84f88d 100644
--- a/cmds/statsd/src/metrics/CountMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/CountMetricProducer.cpp
@@ -303,7 +303,7 @@
         if (prev != mCurrentFullCounters->end()) {
             countWholeBucket += prev->second;
         }
-        tracker->detectAndDeclareAnomaly(eventTimeNs, mCurrentBucketNum, eventKey,
+        tracker->detectAndDeclareAnomaly(eventTimeNs, mCurrentBucketNum, mMetricId, eventKey,
                                          countWholeBucket);
     }
 
diff --git a/cmds/statsd/src/metrics/GaugeMetricProducer.cpp b/cmds/statsd/src/metrics/GaugeMetricProducer.cpp
index 837d532..6301793 100644
--- a/cmds/statsd/src/metrics/GaugeMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/GaugeMetricProducer.cpp
@@ -485,8 +485,8 @@
                 gaugeVal = value.long_value;
             }
             for (auto& tracker : mAnomalyTrackers) {
-                tracker->detectAndDeclareAnomaly(eventTimeNs, mCurrentBucketNum, eventKey,
-                                                 gaugeVal);
+                tracker->detectAndDeclareAnomaly(eventTimeNs, mCurrentBucketNum, mMetricId,
+                                                 eventKey, gaugeVal);
             }
         }
     }
diff --git a/cmds/statsd/src/metrics/ValueMetricProducer.cpp b/cmds/statsd/src/metrics/ValueMetricProducer.cpp
index bc7c872..3cf378d 100644
--- a/cmds/statsd/src/metrics/ValueMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/ValueMetricProducer.cpp
@@ -697,8 +697,8 @@
             wholeBucketVal += prev->second;
         }
         for (auto& tracker : mAnomalyTrackers) {
-            tracker->detectAndDeclareAnomaly(
-                eventTimeNs, mCurrentBucketNum, eventKey, wholeBucketVal);
+            tracker->detectAndDeclareAnomaly(eventTimeNs, mCurrentBucketNum, mMetricId, eventKey,
+                                             wholeBucketVal);
         }
     }
 }
diff --git a/cmds/statsd/src/metrics/duration_helper/DurationTracker.h b/cmds/statsd/src/metrics/duration_helper/DurationTracker.h
index ccb1d43..081e61e 100644
--- a/cmds/statsd/src/metrics/duration_helper/DurationTracker.h
+++ b/cmds/statsd/src/metrics/duration_helper/DurationTracker.h
@@ -155,8 +155,8 @@
                                  const int64_t& currentBucketValue) {
         for (auto& anomalyTracker : mAnomalyTrackers) {
             if (anomalyTracker != nullptr) {
-                anomalyTracker->detectAndDeclareAnomaly(timestamp, currBucketNum, mEventKey,
-                                                        currentBucketValue);
+                anomalyTracker->detectAndDeclareAnomaly(timestamp, currBucketNum, mTrackerId,
+                                                        mEventKey, currentBucketValue);
             }
         }
     }
diff --git a/cmds/statsd/src/packages/UidMap.cpp b/cmds/statsd/src/packages/UidMap.cpp
index 5cf012638..d4b57dd 100644
--- a/cmds/statsd/src/packages/UidMap.cpp
+++ b/cmds/statsd/src/packages/UidMap.cpp
@@ -73,6 +73,11 @@
 
 UidMap::~UidMap() {}
 
+sp<UidMap> UidMap::getInstance() {
+    static sp<UidMap> sInstance = new UidMap();
+    return sInstance;
+}
+
 bool UidMap::hasApp(int uid, const string& packageName) const {
     lock_guard<mutex> lock(mMutex);
 
@@ -336,6 +341,61 @@
     return mBytesUsed;
 }
 
+void UidMap::writeUidMapSnapshot(int64_t timestamp, bool includeVersionStrings,
+                                 bool includeInstaller, const std::set<int32_t>& interestingUids,
+                                 std::set<string>* str_set, ProtoOutputStream* proto) {
+    lock_guard<mutex> lock(mMutex);
+
+    writeUidMapSnapshotLocked(timestamp, includeVersionStrings, includeInstaller, interestingUids,
+                              str_set, proto);
+}
+
+void UidMap::writeUidMapSnapshotLocked(int64_t timestamp, bool includeVersionStrings,
+                                       bool includeInstaller,
+                                       const std::set<int32_t>& interestingUids,
+                                       std::set<string>* str_set, ProtoOutputStream* proto) {
+    proto->write(FIELD_TYPE_INT64 | FIELD_ID_SNAPSHOT_TIMESTAMP, (long long)timestamp);
+    for (const auto& kv : mMap) {
+        if (!interestingUids.empty() &&
+            interestingUids.find(kv.first.first) == interestingUids.end()) {
+            continue;
+        }
+        uint64_t token = proto->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED |
+                                      FIELD_ID_SNAPSHOT_PACKAGE_INFO);
+        if (str_set != nullptr) {
+            str_set->insert(kv.first.second);
+            proto->write(FIELD_TYPE_UINT64 | FIELD_ID_SNAPSHOT_PACKAGE_NAME_HASH,
+                         (long long)Hash64(kv.first.second));
+            if (includeVersionStrings) {
+                str_set->insert(kv.second.versionString);
+                proto->write(FIELD_TYPE_UINT64 | FIELD_ID_SNAPSHOT_PACKAGE_VERSION_STRING_HASH,
+                             (long long)Hash64(kv.second.versionString));
+            }
+            if (includeInstaller) {
+                str_set->insert(kv.second.installer);
+                proto->write(FIELD_TYPE_UINT64 | FIELD_ID_SNAPSHOT_PACKAGE_INSTALLER_HASH,
+                             (long long)Hash64(kv.second.installer));
+            }
+        } else {
+            proto->write(FIELD_TYPE_STRING | FIELD_ID_SNAPSHOT_PACKAGE_NAME, kv.first.second);
+            if (includeVersionStrings) {
+                proto->write(FIELD_TYPE_STRING | FIELD_ID_SNAPSHOT_PACKAGE_VERSION_STRING,
+                             kv.second.versionString);
+            }
+            if (includeInstaller) {
+                proto->write(FIELD_TYPE_STRING | FIELD_ID_SNAPSHOT_PACKAGE_INSTALLER,
+                             kv.second.installer);
+            }
+        }
+
+        proto->write(FIELD_TYPE_INT64 | FIELD_ID_SNAPSHOT_PACKAGE_VERSION,
+                     (long long)kv.second.versionCode);
+        proto->write(FIELD_TYPE_INT32 | FIELD_ID_SNAPSHOT_PACKAGE_UID, kv.first.first);
+        proto->write(FIELD_TYPE_BOOL | FIELD_ID_SNAPSHOT_PACKAGE_DELETED, kv.second.deleted);
+        proto->end(token);
+    }
+}
+
 void UidMap::appendUidMap(const int64_t& timestamp, const ConfigKey& key, std::set<string>* str_set,
                           bool includeVersionStrings, bool includeInstaller,
                           ProtoOutputStream* proto) {
@@ -381,43 +441,9 @@
     // Write snapshot from current uid map state.
     uint64_t snapshotsToken =
             proto->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_SNAPSHOTS);
-    proto->write(FIELD_TYPE_INT64 | FIELD_ID_SNAPSHOT_TIMESTAMP, (long long)timestamp);
-    for (const auto& kv : mMap) {
-        uint64_t token = proto->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED |
-                                      FIELD_ID_SNAPSHOT_PACKAGE_INFO);
-
-        if (str_set != nullptr) {
-            str_set->insert(kv.first.second);
-            proto->write(FIELD_TYPE_UINT64 | FIELD_ID_SNAPSHOT_PACKAGE_NAME_HASH,
-                         (long long)Hash64(kv.first.second));
-            if (includeVersionStrings) {
-                str_set->insert(kv.second.versionString);
-                proto->write(FIELD_TYPE_UINT64 | FIELD_ID_SNAPSHOT_PACKAGE_VERSION_STRING_HASH,
-                             (long long)Hash64(kv.second.versionString));
-            }
-            if (includeInstaller) {
-                str_set->insert(kv.second.installer);
-                proto->write(FIELD_TYPE_UINT64 | FIELD_ID_SNAPSHOT_PACKAGE_INSTALLER_HASH,
-                             (long long)Hash64(kv.second.installer));
-            }
-        } else {
-            proto->write(FIELD_TYPE_STRING | FIELD_ID_SNAPSHOT_PACKAGE_NAME, kv.first.second);
-            if (includeVersionStrings) {
-                proto->write(FIELD_TYPE_STRING | FIELD_ID_SNAPSHOT_PACKAGE_VERSION_STRING,
-                             kv.second.versionString);
-            }
-            if (includeInstaller) {
-                proto->write(FIELD_TYPE_STRING | FIELD_ID_SNAPSHOT_PACKAGE_INSTALLER,
-                             kv.second.installer);
-            }
-        }
-
-        proto->write(FIELD_TYPE_INT64 | FIELD_ID_SNAPSHOT_PACKAGE_VERSION,
-                     (long long)kv.second.versionCode);
-        proto->write(FIELD_TYPE_INT32 | FIELD_ID_SNAPSHOT_PACKAGE_UID, kv.first.first);
-        proto->write(FIELD_TYPE_BOOL | FIELD_ID_SNAPSHOT_PACKAGE_DELETED, kv.second.deleted);
-        proto->end(token);
-    }
+    writeUidMapSnapshotLocked(timestamp, includeVersionStrings, includeInstaller,
+                              std::set<int32_t>() /*empty uid set means including every uid*/,
+                              str_set, proto);
     proto->end(snapshotsToken);
 
     int64_t prevMin = getMinimumTimestampNs();
diff --git a/cmds/statsd/src/packages/UidMap.h b/cmds/statsd/src/packages/UidMap.h
index 75ff507..a7c5fb2 100644
--- a/cmds/statsd/src/packages/UidMap.h
+++ b/cmds/statsd/src/packages/UidMap.h
@@ -91,6 +91,8 @@
     UidMap();
     ~UidMap();
     static const std::map<std::string, uint32_t> sAidToUidMapping;
+
+    static sp<UidMap> getInstance();
     /*
      * All three inputs must be the same size, and the jth element in each array refers to the same
      * tuple, ie. uid[j] corresponds to packageName[j] with versionCode[j].
@@ -152,12 +154,25 @@
 
     std::set<int32_t> getAppUid(const string& package) const;
 
+    // Write current PackageInfoSnapshot to ProtoOutputStream.
+    // interestingUids: If not empty, only write the package info for these uids. If empty, write
+    //                  package info for all uids.
+    // str_set: if not null, add new string to the set and write str_hash to proto
+    //          if null, write string to proto.
+    void writeUidMapSnapshot(int64_t timestamp, bool includeVersionStrings, bool includeInstaller,
+                             const std::set<int32_t>& interestingUids, std::set<string>* str_set,
+                             ProtoOutputStream* proto);
+
 private:
     std::set<string> getAppNamesFromUidLocked(const int32_t& uid, bool returnNormalized) const;
     string normalizeAppName(const string& appName) const;
 
     void getListenerListCopyLocked(std::vector<wp<PackageInfoListener>>* output);
 
+    void writeUidMapSnapshotLocked(int64_t timestamp, bool includeVersionStrings,
+                                   bool includeInstaller, const std::set<int32_t>& interestingUids,
+                                   std::set<string>* str_set, ProtoOutputStream* proto);
+
     mutable mutex mMutex;
     mutable mutex mIsolatedMutex;
 
diff --git a/cmds/statsd/src/stats_log.proto b/cmds/statsd/src/stats_log.proto
index 09b8fed..623d8bc 100644
--- a/cmds/statsd/src/stats_log.proto
+++ b/cmds/statsd/src/stats_log.proto
@@ -457,3 +457,17 @@
     }
     repeated LogLossStats detected_log_loss = 16;
 }
+
+message AlertTriggerDetails {
+    message MetricValue {
+        optional int64 metric_id = 1;
+        optional DimensionsValue dimension_in_what = 2;
+        optional DimensionsValue dimension_in_condition = 3;
+        optional int64 value = 4;
+    }
+    oneof value {
+        MetricValue trigger_metric = 1;
+        EventMetricData trigger_event = 2;
+    }
+    optional UidMapping.PackageInfoSnapshot package_info = 3;
+}
diff --git a/cmds/statsd/src/subscriber/IncidentdReporter.cpp b/cmds/statsd/src/subscriber/IncidentdReporter.cpp
index 42cac0c..0ed2d75 100644
--- a/cmds/statsd/src/subscriber/IncidentdReporter.cpp
+++ b/cmds/statsd/src/subscriber/IncidentdReporter.cpp
@@ -16,7 +16,10 @@
 #define DEBUG false
 #include "Log.h"
 
+#include "FieldValue.h"
 #include "IncidentdReporter.h"
+#include "packages/UidMap.h"
+#include "stats_log_util.h"
 
 #include <android/os/IIncidentManager.h>
 #include <android/os/IncidentReportArgs.h>
@@ -43,8 +46,18 @@
 const int FIELD_ID_CONFIG_KEY_UID = 1;
 const int FIELD_ID_CONFIG_KEY_ID = 2;
 
+const int FIELD_ID_TRIGGER_DETAILS = 4;
+const int FIELD_ID_TRIGGER_DETAILS_TRIGGER_METRIC = 1;
+const int FIELD_ID_METRIC_VALUE_METRIC_ID = 1;
+const int FIELD_ID_METRIC_VALUE_DIMENSION_IN_WHAT = 2;
+const int FIELD_ID_METRIC_VALUE_DIMENSION_IN_CONDITION = 3;
+const int FIELD_ID_METRIC_VALUE_VALUE = 4;
+
+const int FIELD_ID_PACKAGE_INFO = 3;
+
 namespace {
-void getProtoData(const int64_t& rule_id, const ConfigKey& configKey, vector<uint8_t>* protoData) {
+void getProtoData(const int64_t& rule_id, int64_t metricId, const MetricDimensionKey& dimensionKey,
+                  int64_t metricValue, const ConfigKey& configKey, vector<uint8_t>* protoData) {
     ProtoOutputStream headerProto;
     headerProto.write(FIELD_TYPE_INT64 | FIELD_ID_ALERT_ID, (long long)rule_id);
     uint64_t token =
@@ -53,6 +66,58 @@
     headerProto.write(FIELD_TYPE_INT64 | FIELD_ID_CONFIG_KEY_ID, (long long)configKey.GetId());
     headerProto.end(token);
 
+    token = headerProto.start(FIELD_TYPE_MESSAGE | FIELD_ID_TRIGGER_DETAILS);
+
+    // MetricValue trigger_metric = 1;
+    uint64_t metricToken =
+            headerProto.start(FIELD_TYPE_MESSAGE | FIELD_ID_TRIGGER_DETAILS_TRIGGER_METRIC);
+    // message MetricValue {
+    // optional int64 metric_id = 1;
+    headerProto.write(FIELD_TYPE_INT64 | FIELD_ID_METRIC_VALUE_METRIC_ID, (long long)metricId);
+    // optional DimensionsValue dimension_in_what = 2;
+    uint64_t dimToken =
+            headerProto.start(FIELD_TYPE_MESSAGE | FIELD_ID_METRIC_VALUE_DIMENSION_IN_WHAT);
+    writeDimensionToProto(dimensionKey.getDimensionKeyInWhat(), nullptr, &headerProto);
+    headerProto.end(dimToken);
+
+    // optional DimensionsValue dimension_in_condition = 3;
+    dimToken = headerProto.start(FIELD_TYPE_MESSAGE | FIELD_ID_METRIC_VALUE_DIMENSION_IN_CONDITION);
+    writeDimensionToProto(dimensionKey.getDimensionKeyInCondition(), nullptr, &headerProto);
+    headerProto.end(dimToken);
+
+    // optional int64 value = 4;
+    headerProto.write(FIELD_TYPE_INT64 | FIELD_ID_METRIC_VALUE_VALUE, (long long)metricValue);
+
+    // }
+    headerProto.end(metricToken);
+
+    // write relevant uid package info
+    std::set<int32_t> uids;
+
+    for (const auto& dim : dimensionKey.getDimensionKeyInWhat().getValues()) {
+        int uid = getUidIfExists(dim);
+        // any uid <= 2000 are predefined AID_*
+        if (uid > 2000) {
+            uids.insert(uid);
+        }
+    }
+
+    for (const auto& dim : dimensionKey.getDimensionKeyInCondition().getValues()) {
+        int uid = getUidIfExists(dim);
+        if (uid > 2000) {
+            uids.insert(uid);
+        }
+    }
+
+    if (!uids.empty()) {
+        uint64_t token = headerProto.start(FIELD_TYPE_MESSAGE | FIELD_ID_PACKAGE_INFO);
+        UidMap::getInstance()->writeUidMapSnapshot(getElapsedRealtimeNs(), true, true, uids,
+                                                   nullptr /*string set*/, &headerProto);
+        headerProto.end(token);
+    }
+
+    headerProto.end(token);
+
     protoData->resize(headerProto.size());
     size_t pos = 0;
     auto iter = headerProto.data();
@@ -65,7 +130,8 @@
 }
 }  // namespace
 
-bool GenerateIncidentReport(const IncidentdDetails& config, const int64_t& rule_id,
+bool GenerateIncidentReport(const IncidentdDetails& config, int64_t rule_id, int64_t metricId,
+                            const MetricDimensionKey& dimensionKey, int64_t metricValue,
                             const ConfigKey& configKey) {
     if (config.section_size() == 0) {
         VLOG("The alert %lld contains zero section in config(%d,%lld)", (unsigned long long)rule_id,
@@ -76,7 +142,7 @@
     IncidentReportArgs incidentReport;
 
     vector<uint8_t> protoData;
-    getProtoData(rule_id, configKey, &protoData);
+    getProtoData(rule_id, metricId, dimensionKey, metricValue, configKey, &protoData);
     incidentReport.addHeader(protoData);
 
     for (int i = 0; i < config.section_size(); i++) {
diff --git a/cmds/statsd/src/subscriber/IncidentdReporter.h b/cmds/statsd/src/subscriber/IncidentdReporter.h
index 1b83fe2..e78a4d9 100644
--- a/cmds/statsd/src/subscriber/IncidentdReporter.h
+++ b/cmds/statsd/src/subscriber/IncidentdReporter.h
@@ -16,6 +16,7 @@
 
 #pragma once
 
+#include "HashableDimensionKey.h"
 #include "config/ConfigKey.h"
 #include "frameworks/base/cmds/statsd/src/statsd_config.pb.h"  // Alert, IncidentdDetails
 
@@ -26,7 +27,8 @@
 /**
  * Calls incidentd to trigger an incident report and put in dropbox for uploading.
  */
-bool GenerateIncidentReport(const IncidentdDetails& config, const int64_t& rule_id,
+bool GenerateIncidentReport(const IncidentdDetails& config, int64_t rule_id, int64_t metricId,
+                            const MetricDimensionKey& dimensionKey, int64_t metricValue,
                             const ConfigKey& configKey);
 
 }  // namespace statsd
diff --git a/cmds/statsd/tests/anomaly/AnomalyTracker_test.cpp b/cmds/statsd/tests/anomaly/AnomalyTracker_test.cpp
index 960fbda..c10703c 100644
--- a/cmds/statsd/tests/anomaly/AnomalyTracker_test.cpp
+++ b/cmds/statsd/tests/anomaly/AnomalyTracker_test.cpp
@@ -90,7 +90,8 @@
                                const std::shared_ptr<DimToValMap>& bucket,
                                const int64_t& eventTimestamp) {
     for (const auto& kv : *bucket) {
-        tracker.detectAndDeclareAnomaly(eventTimestamp, bucketNum, kv.first, kv.second);
+        tracker.detectAndDeclareAnomaly(eventTimestamp, bucketNum, 0 /*metric_id*/, kv.first,
+                                        kv.second);
     }
 }
 
diff --git a/core/java/android/app/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java
index 688af39..c12a92f 100644
--- a/core/java/android/app/SystemServiceRegistry.java
+++ b/core/java/android/app/SystemServiceRegistry.java
@@ -106,7 +106,6 @@
 import android.net.IpSecManager;
 import android.net.NetworkPolicyManager;
 import android.net.NetworkScoreManager;
-import android.net.NetworkStack;
 import android.net.NetworkWatchlistManager;
 import android.net.lowpan.ILowpanManager;
 import android.net.lowpan.LowpanManager;
@@ -336,13 +335,6 @@
             }
         });
 
-        registerService(Context.NETWORK_STACK_SERVICE, NetworkStack.class,
-                new StaticServiceFetcher<NetworkStack>() {
-                    @Override
-                    public NetworkStack createService() {
-                        return new NetworkStack();
-                    }});
-
         registerService(Context.IP_MEMORY_STORE_SERVICE, IpMemoryStore.class,
                 new CachedServiceFetcher<IpMemoryStore>() {
                     @Override
diff --git a/core/java/android/app/UiModeManager.java b/core/java/android/app/UiModeManager.java
index a554882..46316e1 100644
--- a/core/java/android/app/UiModeManager.java
+++ b/core/java/android/app/UiModeManager.java
@@ -229,7 +229,11 @@
      * <strong>Note:</strong> On API 22 and below, changes to the night mode
      * are only effective when the {@link Configuration#UI_MODE_TYPE_CAR car}
      * or {@link Configuration#UI_MODE_TYPE_DESK desk} mode is enabled on a
-     * device. Starting in API 23, changes to night mode are always effective.
+     * device. On API 23 through API 28, changes to night mode are always effective.
+     * <p>
+     * Starting in API 29, when the device is in car mode and this method is called, night mode
+     * will change, but the new setting is not persisted and the previously persisted setting
+     * will be restored when the device exits car mode.
      * <p>
      * Changes to night mode take effect globally and will result in a configuration change
      * (and potentially an Activity lifecycle event) being applied to all running apps.
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index 957a484..25bfba2 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -49,7 +49,6 @@
 import android.database.sqlite.SQLiteDatabase.CursorFactory;
 import android.graphics.Bitmap;
 import android.graphics.drawable.Drawable;
-import android.net.NetworkStack;
 import android.net.Uri;
 import android.os.Build;
 import android.os.Bundle;
@@ -3647,11 +3646,10 @@
     public static final String NETD_SERVICE = "netd";
 
     /**
-     * Use with {@link #getSystemService(String)} to retrieve a
-     * {@link NetworkStack} for communicating with the network stack
+     * Use with {@link android.os.ServiceManager.getService()} to retrieve a
+     * {@link NetworkStackClient} IBinder for communicating with the network stack
      * @hide
-     * @see #getSystemService(String)
-     * @see NetworkStack
+     * @see NetworkStackClient
      */
     public static final String NETWORK_STACK_SERVICE = "network_stack";
 
diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl
index 36ffb0e..14e7725 100644
--- a/core/java/android/content/pm/IPackageManager.aidl
+++ b/core/java/android/content/pm/IPackageManager.aidl
@@ -295,8 +295,6 @@
     void restoreDefaultApps(in byte[] backup, int userId);
     byte[] getIntentFilterVerificationBackup(int userId);
     void restoreIntentFilterVerification(in byte[] backup, int userId);
-    byte[] getPermissionGrantBackup(int userId);
-    void restorePermissionGrants(in byte[] backup, int userId);
 
     /**
      * Report the set of 'Home' activity candidates, plus (if any) which of them
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index 3ea78df..0041921 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -2943,6 +2943,7 @@
     * @hide
     */
     @SystemApi
+    @TestApi
     public static final int FLAG_PERMISSION_USER_SET = 1 << 0;
 
     /**
@@ -2953,6 +2954,7 @@
      * @hide
      */
     @SystemApi
+    @TestApi
     public static final int FLAG_PERMISSION_USER_FIXED =  1 << 1;
 
     /**
@@ -2976,6 +2978,7 @@
      * @hide
      */
     @SystemApi
+    @TestApi
     public static final int FLAG_PERMISSION_REVOKE_ON_UPGRADE =  1 << 3;
 
     /**
@@ -3005,6 +3008,7 @@
      * @hide
      */
     @SystemApi
+    @TestApi
     public static final int FLAG_PERMISSION_REVIEW_REQUIRED =  1 << 6;
 
     /**
@@ -3014,6 +3018,7 @@
      *
      * @hide
      */
+    @TestApi
     public static final int FLAG_PERMISSION_REVOKE_WHEN_REQUESTED =  1 << 7;
 
     /**
@@ -3795,6 +3800,7 @@
      * @hide
      */
     @SystemApi
+    @TestApi
     @RequiresPermission(anyOf = {
             android.Manifest.permission.GRANT_RUNTIME_PERMISSIONS,
             android.Manifest.permission.REVOKE_RUNTIME_PERMISSIONS
@@ -3815,6 +3821,7 @@
      * @hide
      */
     @SystemApi
+    @TestApi
     @RequiresPermission(anyOf = {
             android.Manifest.permission.GRANT_RUNTIME_PERMISSIONS,
             android.Manifest.permission.REVOKE_RUNTIME_PERMISSIONS
diff --git a/core/java/android/database/sqlite/SQLiteQueryBuilder.java b/core/java/android/database/sqlite/SQLiteQueryBuilder.java
index 5722e7b..ea489c4 100644
--- a/core/java/android/database/sqlite/SQLiteQueryBuilder.java
+++ b/core/java/android/database/sqlite/SQLiteQueryBuilder.java
@@ -77,6 +77,14 @@
     }
 
     /**
+     * Get if the query is marked as DISTINCT, as last configured by
+     * {@link #setDistinct(boolean)}.
+     */
+    public boolean getDistinct() {
+        return mDistinct;
+    }
+
+    /**
      * Returns the list of tables being queried
      *
      * @return the list of tables being queried
@@ -167,6 +175,14 @@
     }
 
     /**
+     * Gets the projection map for the query, as last configured by
+     * {@link #setProjectionMap(Map)}.
+     */
+    public Map<String, String> getProjectionMap() {
+        return mProjectionMap;
+    }
+
+    /**
      * Sets a projection greylist of columns that will be allowed through, even
      * when {@link #setStrict(boolean)} is enabled. This provides a way for
      * abusive custom columns like {@code COUNT(*)} to continue working.
@@ -178,6 +194,16 @@
     }
 
     /**
+     * Gets the projection greylist for the query, as last configured by
+     * {@link #setProjectionGreylist(List)}.
+     *
+     * @hide
+     */
+    public List<Pattern> getProjectionGreylist() {
+        return mProjectionGreylist;
+    }
+
+    /**
      * Sets the cursor factory to be used for the query.  You can use
      * one factory for all queries on a database but it is normally
      * easier to specify the factory when doing this query.
@@ -189,6 +215,14 @@
     }
 
     /**
+     * Sets the cursor factory to be used for the query, as last configured by
+     * {@link #setCursorFactory(android.database.sqlite.SQLiteDatabase.CursorFactory)}.
+     */
+    public SQLiteDatabase.CursorFactory getCursorFactory() {
+        return mFactory;
+    }
+
+    /**
      * When set, the selection is verified against malicious arguments.
      * When using this class to create a statement using
      * {@link #buildQueryString(boolean, String, String[], String, String, String, String, String)},
@@ -214,6 +248,14 @@
     }
 
     /**
+     * Get if the query is marked as strict, as last configured by
+     * {@link #setStrict(boolean)}.
+     */
+    public boolean getStrict() {
+        return mStrict;
+    }
+
+    /**
      * Build an SQL query string from the given clauses.
      *
      * @param distinct true if you want each row to be unique, false otherwise.
diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java
index 68d36de..6d195ae 100644
--- a/core/java/android/net/ConnectivityManager.java
+++ b/core/java/android/net/ConnectivityManager.java
@@ -3936,15 +3936,16 @@
      *
      * <p>This endpoint is exclusively for use by the NetworkStack and is protected by the
      * corresponding permission.
+     * @param network Network on which the captive portal was detected.
      * @param appExtras Extras to include in the app start intent.
      * @hide
      */
     @SystemApi
     @TestApi
     @RequiresPermission(NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK)
-    public void startCaptivePortalApp(Bundle appExtras) {
+    public void startCaptivePortalApp(Network network, Bundle appExtras) {
         try {
-            mService.startCaptivePortalAppInternal(appExtras);
+            mService.startCaptivePortalAppInternal(network, appExtras);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
diff --git a/core/java/android/net/IConnectivityManager.aidl b/core/java/android/net/IConnectivityManager.aidl
index 872671f..87c62d2 100644
--- a/core/java/android/net/IConnectivityManager.aidl
+++ b/core/java/android/net/IConnectivityManager.aidl
@@ -168,7 +168,7 @@
     void setAcceptUnvalidated(in Network network, boolean accept, boolean always);
     void setAvoidUnvalidated(in Network network);
     void startCaptivePortalApp(in Network network);
-    void startCaptivePortalAppInternal(in Bundle appExtras);
+    void startCaptivePortalAppInternal(in Network network, in Bundle appExtras);
 
     boolean getAvoidBadWifi();
     int getMultipathPreference(in Network Network);
diff --git a/core/java/android/net/INetworkMonitor.aidl b/core/java/android/net/INetworkMonitor.aidl
index 41f969a..c94cdde 100644
--- a/core/java/android/net/INetworkMonitor.aidl
+++ b/core/java/android/net/INetworkMonitor.aidl
@@ -34,6 +34,7 @@
 
     void start();
     void launchCaptivePortalApp();
+    void notifyCaptivePortalAppFinished(int response);
     void forceReevaluation(int uid);
     void notifyPrivateDnsChanged(in PrivateDnsConfigParcel config);
     void notifyDnsResponse(int returnCode);
diff --git a/core/java/android/net/INetworkMonitorCallbacks.aidl b/core/java/android/net/INetworkMonitorCallbacks.aidl
index 5146585..2c61511 100644
--- a/core/java/android/net/INetworkMonitorCallbacks.aidl
+++ b/core/java/android/net/INetworkMonitorCallbacks.aidl
@@ -26,5 +26,4 @@
     void notifyPrivateDnsConfigResolved(in PrivateDnsConfigParcel config);
     void showProvisioningNotification(String action, String packageName);
     void hideProvisioningNotification();
-    void logCaptivePortalLoginEvent(int eventId, String packageName);
 }
\ No newline at end of file
diff --git a/core/java/android/net/NetworkStack.java b/core/java/android/net/NetworkStack.java
index ca49438..dbb894f 100644
--- a/core/java/android/net/NetworkStack.java
+++ b/core/java/android/net/NetworkStack.java
@@ -15,46 +15,17 @@
  */
 package android.net;
 
-import static android.content.pm.PackageManager.PERMISSION_GRANTED;
-import static android.os.IServiceManager.DUMP_FLAG_PRIORITY_HIGH;
-import static android.os.IServiceManager.DUMP_FLAG_PRIORITY_NORMAL;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
 import android.annotation.SystemApi;
-import android.annotation.SystemService;
 import android.annotation.TestApi;
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.Intent;
-import android.content.ServiceConnection;
-import android.content.pm.PackageManager;
-import android.net.dhcp.DhcpServingParamsParcel;
-import android.net.dhcp.IDhcpServerCallbacks;
-import android.net.ip.IIpClientCallbacks;
-import android.os.Binder;
-import android.os.IBinder;
-import android.os.Process;
-import android.os.RemoteException;
-import android.os.ServiceManager;
-import android.os.UserHandle;
-import android.util.Slog;
-
-import com.android.internal.annotations.GuardedBy;
-
-import java.lang.reflect.InvocationTargetException;
-import java.util.ArrayList;
 
 /**
- * Service used to communicate with the network stack, which is running in a separate module.
+ *
+ * Constants for client code communicating with the network stack service.
  * @hide
  */
-@SystemService(Context.NETWORK_STACK_SERVICE)
 @SystemApi
 @TestApi
 public class NetworkStack {
-    private static final String TAG = NetworkStack.class.getSimpleName();
-
     /**
      * Permission granted only to the NetworkStack APK, defined in NetworkStackStub with signature
      * protection level.
@@ -65,235 +36,5 @@
     public static final String PERMISSION_MAINLINE_NETWORK_STACK =
             "android.permission.MAINLINE_NETWORK_STACK";
 
-    private static final int NETWORKSTACK_TIMEOUT_MS = 10_000;
-
-    @NonNull
-    @GuardedBy("mPendingNetStackRequests")
-    private final ArrayList<NetworkStackCallback> mPendingNetStackRequests = new ArrayList<>();
-    @Nullable
-    @GuardedBy("mPendingNetStackRequests")
-    private INetworkStackConnector mConnector;
-
-    private volatile boolean mNetworkStackStartRequested = false;
-
-    private interface NetworkStackCallback {
-        void onNetworkStackConnected(INetworkStackConnector connector);
-    }
-
-    /** @hide */
-    public NetworkStack() { }
-
-    /**
-     * Create a DHCP server according to the specified parameters.
-     *
-     * <p>The server will be returned asynchronously through the provided callbacks.
-     * @hide
-     */
-    public void makeDhcpServer(final String ifName, final DhcpServingParamsParcel params,
-            final IDhcpServerCallbacks cb) {
-        requestConnector(connector -> {
-            try {
-                connector.makeDhcpServer(ifName, params, cb);
-            } catch (RemoteException e) {
-                e.rethrowFromSystemServer();
-            }
-        });
-    }
-
-    /**
-     * Create an IpClient on the specified interface.
-     *
-     * <p>The IpClient will be returned asynchronously through the provided callbacks.
-     * @hide
-     */
-    public void makeIpClient(String ifName, IIpClientCallbacks cb) {
-        requestConnector(connector -> {
-            try {
-                connector.makeIpClient(ifName, cb);
-            } catch (RemoteException e) {
-                e.rethrowFromSystemServer();
-            }
-        });
-    }
-
-    /**
-     * Create a NetworkMonitor.
-     *
-     * <p>The INetworkMonitor will be returned asynchronously through the provided callbacks.
-     * @hide
-     */
-    public void makeNetworkMonitor(
-            NetworkParcelable network, String name, INetworkMonitorCallbacks cb) {
-        requestConnector(connector -> {
-            try {
-                connector.makeNetworkMonitor(network, name, cb);
-            } catch (RemoteException e) {
-                e.rethrowFromSystemServer();
-            }
-        });
-    }
-
-    private class NetworkStackConnection implements ServiceConnection {
-        @Override
-        public void onServiceConnected(ComponentName name, IBinder service) {
-            registerNetworkStackService(service);
-        }
-
-        @Override
-        public void onServiceDisconnected(ComponentName name) {
-            // TODO: crash/reboot the system ?
-            Slog.wtf(TAG, "Lost network stack connector");
-        }
-    };
-
-    private void registerNetworkStackService(@NonNull IBinder service) {
-        final INetworkStackConnector connector = INetworkStackConnector.Stub.asInterface(service);
-
-        ServiceManager.addService(Context.NETWORK_STACK_SERVICE, service, false /* allowIsolated */,
-                DUMP_FLAG_PRIORITY_HIGH | DUMP_FLAG_PRIORITY_NORMAL);
-
-        final ArrayList<NetworkStackCallback> requests;
-        synchronized (mPendingNetStackRequests) {
-            requests = new ArrayList<>(mPendingNetStackRequests);
-            mPendingNetStackRequests.clear();
-            mConnector = connector;
-        }
-
-        for (NetworkStackCallback r : requests) {
-            r.onNetworkStackConnected(connector);
-        }
-    }
-
-    /**
-     * Start the network stack. Should be called only once on device startup.
-     *
-     * <p>This method will start the network stack either in the network stack process, or inside
-     * the system server on devices that do not support the network stack module. The network stack
-     * connector will then be delivered asynchronously to clients that requested it before it was
-     * started.
-     * @hide
-     */
-    public void start(Context context) {
-        mNetworkStackStartRequested = true;
-        // Try to bind in-process if the library is available
-        IBinder connector = null;
-        try {
-            final Class service = Class.forName(
-                    "com.android.server.NetworkStackService",
-                    true /* initialize */,
-                    context.getClassLoader());
-            connector = (IBinder) service.getMethod("makeConnector", Context.class)
-                    .invoke(null, context);
-        } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
-            Slog.wtf(TAG, "Could not create network stack connector from NetworkStackService");
-            // TODO: crash/reboot system here ?
-            return;
-        } catch (ClassNotFoundException e) {
-            // Normal behavior if stack is provided by the app: fall through
-        }
-
-        // In-process network stack. Add the service to the service manager here.
-        if (connector != null) {
-            registerNetworkStackService(connector);
-            return;
-        }
-        // Start the network stack process. The service will be added to the service manager in
-        // NetworkStackConnection.onServiceConnected().
-        final Intent intent = new Intent(INetworkStackConnector.class.getName());
-        final ComponentName comp = intent.resolveSystemService(context.getPackageManager(), 0);
-        intent.setComponent(comp);
-
-        if (comp == null) {
-            Slog.wtf(TAG, "Could not resolve the network stack with " + intent);
-            // TODO: crash/reboot system server ?
-            return;
-        }
-
-        final PackageManager pm = context.getPackageManager();
-        int uid = -1;
-        try {
-            uid = pm.getPackageUid(comp.getPackageName(), UserHandle.USER_SYSTEM);
-        } catch (PackageManager.NameNotFoundException e) {
-            Slog.wtf("Network stack package not found", e);
-            // Fall through
-        }
-
-        if (uid != Process.NETWORK_STACK_UID) {
-            throw new SecurityException("Invalid network stack UID: " + uid);
-        }
-
-        final int hasPermission =
-                pm.checkPermission(PERMISSION_MAINLINE_NETWORK_STACK, comp.getPackageName());
-        if (hasPermission != PERMISSION_GRANTED) {
-            throw new SecurityException(
-                    "Network stack does not have permission " + PERMISSION_MAINLINE_NETWORK_STACK);
-        }
-
-        if (!context.bindServiceAsUser(intent, new NetworkStackConnection(),
-                Context.BIND_AUTO_CREATE | Context.BIND_IMPORTANT, UserHandle.SYSTEM)) {
-            Slog.wtf(TAG,
-                    "Could not bind to network stack in-process, or in app with " + intent);
-            // TODO: crash/reboot system server if no network stack after a timeout ?
-        }
-    }
-
-    /**
-     * For non-system server clients, get the connector registered by the system server.
-     */
-    private INetworkStackConnector getRemoteConnector() {
-        // Block until the NetworkStack connector is registered in ServiceManager.
-        // <p>This is only useful for non-system processes that do not have a way to be notified of
-        // registration completion. Adding a callback system would be too heavy weight considering
-        // that the connector is registered on boot, so it is unlikely that a client would request
-        // it before it is registered.
-        // TODO: consider blocking boot on registration and simplify much of the logic in this class
-        IBinder connector;
-        try {
-            final long before = System.currentTimeMillis();
-            while ((connector = ServiceManager.getService(Context.NETWORK_STACK_SERVICE)) == null) {
-                Thread.sleep(20);
-                if (System.currentTimeMillis() - before > NETWORKSTACK_TIMEOUT_MS) {
-                    Slog.e(TAG, "Timeout waiting for NetworkStack connector");
-                    return null;
-                }
-            }
-        } catch (InterruptedException e) {
-            Slog.e(TAG, "Error waiting for NetworkStack connector", e);
-            return null;
-        }
-
-        return INetworkStackConnector.Stub.asInterface(connector);
-    }
-
-    private void requestConnector(@NonNull NetworkStackCallback request) {
-        // TODO: PID check.
-        final int caller = Binder.getCallingUid();
-        if (caller != Process.SYSTEM_UID && !UserHandle.isSameApp(caller, Process.BLUETOOTH_UID)) {
-            // Don't even attempt to obtain the connector and give a nice error message
-            throw new SecurityException(
-                    "Only the system server should try to bind to the network stack.");
-        }
-
-        if (!mNetworkStackStartRequested) {
-            // The network stack is not being started in this process, e.g. this process is not
-            // the system server. Get a remote connector registered by the system server.
-            final INetworkStackConnector connector = getRemoteConnector();
-            synchronized (mPendingNetStackRequests) {
-                mConnector = connector;
-            }
-            request.onNetworkStackConnected(connector);
-            return;
-        }
-
-        final INetworkStackConnector connector;
-        synchronized (mPendingNetStackRequests) {
-            connector = mConnector;
-            if (connector == null) {
-                mPendingNetStackRequests.add(request);
-                return;
-            }
-        }
-
-        request.onNetworkStackConnected(connector);
-    }
+    private NetworkStack() {}
 }
diff --git a/core/java/android/permission/PermissionControllerManager.java b/core/java/android/permission/PermissionControllerManager.java
index 89967c3..addfe3d 100644
--- a/core/java/android/permission/PermissionControllerManager.java
+++ b/core/java/android/permission/PermissionControllerManager.java
@@ -624,7 +624,7 @@
          *
          * <p>Needs to be called when canceling this task as it might be hung.
          */
-        void interruptRead() {
+        void interruptWrite() {
             IoUtils.closeQuietly(mLocalPipe);
         }
 
@@ -806,18 +806,19 @@
 
         @Override
         public void run(@NonNull IPermissionController service) {
+            mBackupSender.execute(mBackup);
+
             ParcelFileDescriptor remotePipe = mBackupSender.getRemotePipe();
             try {
                 service.restoreRuntimePermissionBackup(mUser, remotePipe);
             } catch (RemoteException e) {
                 Log.e(TAG, "Error sending runtime permission backup", e);
                 mBackupSender.cancel(false);
+                mBackupSender.interruptWrite();
             } finally {
                 // Remote pipe end is duped by binder call. Local copy is not needed anymore
                 IoUtils.closeQuietly(remotePipe);
             }
-
-            mBackupSender.execute(mBackup);
         }
     }
 
diff --git a/core/java/android/permission/PermissionManagerInternal.java b/core/java/android/permission/PermissionManagerInternal.java
new file mode 100644
index 0000000..92dbab3
--- /dev/null
+++ b/core/java/android/permission/PermissionManagerInternal.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2019 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.permission;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.UserHandle;
+
+/**
+ * Internal interfaces to be used by other components within the system server.
+ *
+ * <p>Only for use within the system server.
+ *
+ * @hide
+ */
+public abstract class PermissionManagerInternal {
+    /**
+     * Get the state of the runtime permissions as xml file.
+     *
+     * @param user The user the data should be extracted for
+     *
+     * @return The state as a xml file
+     */
+    public abstract @Nullable byte[] backupRuntimePermissions(@NonNull UserHandle user);
+
+    /**
+     * Restore a permission state previously backed up via {@link #backupRuntimePermissions}.
+     *
+     * <p>If not all state can be restored, the un-restoreable state will be delayed and can be
+     * re-tried via {@link #restoreDelayedRuntimePermissions}.
+     *
+     * @param backup The state as an xml file
+     * @param user The user the data should be restored for
+     */
+    public abstract void restoreRuntimePermissions(@NonNull byte[] backup,
+            @NonNull UserHandle user);
+
+    /**
+     * Try to apply permission backup of a package that was previously not applied.
+     *
+     * @param packageName The package that is newly installed
+     * @param user The user the package is installed for
+     *
+     * @see #restoreRuntimePermissions
+     */
+    public abstract void restoreDelayedRuntimePermissions(@NonNull String packageName,
+            @NonNull UserHandle user);
+}
diff --git a/core/java/android/provider/BaseColumns.java b/core/java/android/provider/BaseColumns.java
index f594c19..00c9e72 100644
--- a/core/java/android/provider/BaseColumns.java
+++ b/core/java/android/provider/BaseColumns.java
@@ -16,17 +16,18 @@
 
 package android.provider;
 
-public interface BaseColumns
-{
+import android.database.Cursor;
+
+public interface BaseColumns {
     /**
      * The unique ID for a row.
-     * <P>Type: INTEGER (long)</P>
      */
+    @Column(Cursor.FIELD_TYPE_INTEGER)
     public static final String _ID = "_id";
 
     /**
      * The count of rows in a directory.
-     * <P>Type: INTEGER</P>
      */
+    // @Column(Cursor.FIELD_TYPE_INTEGER)
     public static final String _COUNT = "_count";
 }
diff --git a/core/java/android/provider/Column.java b/core/java/android/provider/Column.java
new file mode 100644
index 0000000..1364fb8
--- /dev/null
+++ b/core/java/android/provider/Column.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2019 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.provider;
+
+import static java.lang.annotation.ElementType.FIELD;
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+import android.content.ContentProvider;
+import android.content.ContentValues;
+import android.database.Cursor;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+/**
+ * Denotes that a field is a {@link ContentProvider} column. It can be used as a
+ * key for {@link ContentValues} when inserting or updating data, or as a
+ * projection when querying.
+ *
+ * @hide
+ */
+@Documented
+@Retention(RUNTIME)
+@Target({FIELD})
+public @interface Column {
+    /**
+     * The {@link Cursor#getType(int)} of the data stored in this column.
+     */
+    int value();
+
+    /**
+     * This column is read-only and cannot be defined during insert or updates.
+     */
+    boolean readOnly() default false;
+}
diff --git a/core/java/android/provider/MediaStore.java b/core/java/android/provider/MediaStore.java
index 0b38420..1b10bef 100644
--- a/core/java/android/provider/MediaStore.java
+++ b/core/java/android/provider/MediaStore.java
@@ -857,8 +857,6 @@
          * this path. Instead of trying to open this path directly, apps should
          * use {@link ContentResolver#openFileDescriptor(Uri, String)} to gain
          * access.
-         * <p>
-         * Type: TEXT
          *
          * @deprecated Apps may not have filesystem permissions to directly
          *             access this path. Instead of trying to open this path
@@ -869,6 +867,7 @@
          *             {@link android.os.Build.VERSION_CODES#Q} or higher.
          */
         @Deprecated
+        @Column(Cursor.FIELD_TYPE_STRING)
         public static final String DATA = "_data";
 
         /**
@@ -883,44 +882,44 @@
          * If you require the hash of a specific item, you can call
          * {@link ContentResolver#canonicalize(Uri)}, which will block until the
          * hash is calculated.
-         * <p>
-         * Type: BLOB
+         *
          * @removed
          */
         @Deprecated
+        @Column(value = Cursor.FIELD_TYPE_BLOB, readOnly = true)
         public static final String HASH = "_hash";
 
         /**
          * The size of the file in bytes
-         * <P>Type: INTEGER (long)</P>
          */
+        @Column(value = Cursor.FIELD_TYPE_INTEGER, readOnly = true)
         public static final String SIZE = "_size";
 
         /**
          * The display name of the file
-         * <P>Type: TEXT</P>
          */
+        @Column(Cursor.FIELD_TYPE_STRING)
         public static final String DISPLAY_NAME = "_display_name";
 
         /**
          * The title of the content
-         * <P>Type: TEXT</P>
          */
+        @Column(value = Cursor.FIELD_TYPE_STRING, readOnly = true)
         public static final String TITLE = "title";
 
         /**
          * The time the file was added to the media provider
          * Units are seconds since 1970.
-         * <P>Type: INTEGER (long)</P>
          */
+        @Column(value = Cursor.FIELD_TYPE_INTEGER, readOnly = true)
         public static final String DATE_ADDED = "date_added";
 
         /**
          * The time the file was last modified
          * Units are seconds since 1970.
          * NOTE: This is for internal use by the media scanner.  Do not modify this field.
-         * <P>Type: INTEGER (long)</P>
          */
+        @Column(value = Cursor.FIELD_TYPE_INTEGER, readOnly = true)
         public static final String DATE_MODIFIED = "date_modified";
 
         /**
@@ -938,9 +937,8 @@
          * {@code format} of {@code audio/ogg} would be ignored.
          * <p>
          * This is a read-only column that is automatically computed.
-         * <p>
-         * Type: TEXT
          */
+        @Column(Cursor.FIELD_TYPE_STRING)
         public static final String MIME_TYPE = "mime_type";
 
         /**
@@ -948,35 +946,34 @@
          * Used to pass the new file's object handle through the media scanner
          * from MTP to the media provider
          * For internal use only by MTP, media scanner and media provider.
-         * <P>Type: INTEGER</P>
          * @hide
          */
+        @Deprecated
+        // @Column(Cursor.FIELD_TYPE_INTEGER)
         public static final String MEDIA_SCANNER_NEW_OBJECT_ID = "media_scanner_new_object_id";
 
         /**
          * Non-zero if the media file is drm-protected
-         * <P>Type: INTEGER (boolean)</P>
          * @hide
          */
         @UnsupportedAppUsage
+        @Deprecated
+        @Column(Cursor.FIELD_TYPE_INTEGER)
         public static final String IS_DRM = "is_drm";
 
         /**
          * Flag indicating if a media item is pending, and still being inserted
          * by its owner.
-         * <p>
-         * Type: BOOLEAN
          *
          * @see MediaColumns#IS_PENDING
          * @see MediaStore#setIncludePending(Uri)
          * @see MediaStore#createPending(Context, PendingParams)
          */
+        @Column(Cursor.FIELD_TYPE_INTEGER)
         public static final String IS_PENDING = "is_pending";
 
         /**
          * Flag indicating if a media item is trashed.
-         * <p>
-         * Type: BOOLEAN
          *
          * @see MediaColumns#IS_TRASHED
          * @see MediaStore#setIncludeTrashed(Uri)
@@ -985,57 +982,54 @@
          * @removed
          */
         @Deprecated
+        @Column(Cursor.FIELD_TYPE_INTEGER)
         public static final String IS_TRASHED = "is_trashed";
 
         /**
          * The time the file should be considered expired. Units are seconds
          * since 1970. Typically only meaningful in the context of
          * {@link #IS_PENDING} or {@link #IS_TRASHED}.
-         * <p>
-         * Type: INTEGER
          * @removed
          */
         @Deprecated
+        @Column(Cursor.FIELD_TYPE_INTEGER)
         public static final String DATE_EXPIRES = "date_expires";
 
         /**
          * The width of the image/video in pixels.
          */
+        @Column(value = Cursor.FIELD_TYPE_INTEGER, readOnly = true)
         public static final String WIDTH = "width";
 
         /**
          * The height of the image/video in pixels.
          */
+        @Column(value = Cursor.FIELD_TYPE_INTEGER, readOnly = true)
         public static final String HEIGHT = "height";
 
         /**
          * Package name that contributed this media. The value may be
          * {@code NULL} if ownership cannot be reliably determined.
-         * <p>
-         * This is a read-only column that is automatically computed.
-         * <p>
-         * Type: TEXT
          */
+        @Column(value = Cursor.FIELD_TYPE_STRING, readOnly = true)
         public static final String OWNER_PACKAGE_NAME = "owner_package_name";
 
         /**
          * The primary directory name this media exists under. The value may be
          * {@code NULL} if the media doesn't have a primary directory name.
-         * <p>
-         * Type: TEXT
          *
          * @see PendingParams#setPrimaryDirectory(String)
          */
+        @Column(Cursor.FIELD_TYPE_STRING)
         public static final String PRIMARY_DIRECTORY = "primary_directory";
 
         /**
          * The secondary directory name this media exists under. The value may
          * be {@code NULL} if the media doesn't have a secondary directory name.
-         * <p>
-         * Type: TEXT
          *
          * @see PendingParams#setSecondaryDirectory(String)
          */
+        @Column(Cursor.FIELD_TYPE_STRING)
         public static final String SECONDARY_DIRECTORY = "secondary_directory";
 
         /**
@@ -1046,11 +1040,8 @@
          * <p>
          * Each "document ID" is created once for each new resource. Different
          * renditions of that resource are expected to have different IDs.
-         * <p>
-         * This is a read-only column that is automatically computed.
-         * <p>
-         * Type: TEXT
          */
+        @Column(value = Cursor.FIELD_TYPE_STRING, readOnly = true)
         public static final String DOCUMENT_ID = "document_id";
 
         /**
@@ -1061,11 +1052,8 @@
          * <p>
          * This "instance ID" changes with each save operation of a specific
          * "document ID".
-         * <p>
-         * This is a read-only column that is automatically computed.
-         * <p>
-         * Type: TEXT
          */
+        @Column(value = Cursor.FIELD_TYPE_STRING, readOnly = true)
         public static final String INSTANCE_ID = "instance_id";
 
         /**
@@ -1077,11 +1065,8 @@
          * For example, when you save a PSD document as a JPEG, then convert the
          * JPEG to GIF format, the "original document ID" of both the JPEG and
          * GIF files is the "document ID" of the original PSD file.
-         * <p>
-         * This is a read-only column that is automatically computed.
-         * <p>
-         * Type: TEXT
          */
+        @Column(value = Cursor.FIELD_TYPE_STRING, readOnly = true)
         public static final String ORIGINAL_DOCUMENT_ID = "original_document_id";
     }
 
@@ -1172,43 +1157,46 @@
         public interface FileColumns extends MediaColumns {
             /**
              * The MTP storage ID of the file
-             * <P>Type: INTEGER</P>
              * @hide
              */
             @UnsupportedAppUsage
+            @Deprecated
+            // @Column(Cursor.FIELD_TYPE_INTEGER)
             public static final String STORAGE_ID = "storage_id";
 
             /**
              * The MTP format code of the file
-             * <P>Type: INTEGER</P>
              * @hide
              */
             @UnsupportedAppUsage
+            @Column(value = Cursor.FIELD_TYPE_INTEGER, readOnly = true)
             public static final String FORMAT = "format";
 
             /**
              * The index of the parent directory of the file
-             * <P>Type: INTEGER</P>
              */
+            @Column(value = Cursor.FIELD_TYPE_INTEGER, readOnly = true)
             public static final String PARENT = "parent";
 
             /**
              * The MIME type of the file
              * <P>Type: TEXT</P>
              */
+            @Column(Cursor.FIELD_TYPE_STRING)
             public static final String MIME_TYPE = "mime_type";
 
             /**
              * The title of the content
              * <P>Type: TEXT</P>
              */
+            @Column(value = Cursor.FIELD_TYPE_STRING, readOnly = true)
             public static final String TITLE = "title";
 
             /**
              * The media type (audio, video, image or playlist)
              * of the file, or 0 for not a media file
-             * <P>Type: TEXT</P>
              */
+            @Column(Cursor.FIELD_TYPE_INTEGER)
             public static final String MEDIA_TYPE = "media_type";
 
             /**
@@ -1241,6 +1229,7 @@
              * Column indicating if the file is part of Downloads collection.
              * @hide
              */
+            @Column(value = Cursor.FIELD_TYPE_INTEGER, readOnly = true)
             public static final String IS_DOWNLOAD = "is_download";
         }
     }
@@ -1260,23 +1249,20 @@
     public interface DownloadColumns extends MediaColumns {
         /**
          * Uri indicating where the file has been downloaded from.
-         * <p>
-         * Type: TEXT
          */
+        @Column(Cursor.FIELD_TYPE_STRING)
         String DOWNLOAD_URI = "download_uri";
 
         /**
          * Uri indicating HTTP referer of {@link #DOWNLOAD_URI}.
-         * <p>
-         * Type: TEXT
          */
+        @Column(Cursor.FIELD_TYPE_STRING)
         String REFERER_URI = "referer_uri";
 
         /**
          * The description of the download.
-         * <p>
-         * Type: Text
          */
+        @Column(Cursor.FIELD_TYPE_STRING)
         String DESCRIPTION = "description";
     }
 
@@ -1442,29 +1428,28 @@
         public interface ImageColumns extends MediaColumns {
             /**
              * The description of the image
-             * <P>Type: TEXT</P>
              */
+            @Column(value = Cursor.FIELD_TYPE_STRING, readOnly = true)
             public static final String DESCRIPTION = "description";
 
             /**
              * The picasa id of the image
-             * <P>Type: TEXT</P>
              *
              * @deprecated this value was only relevant for images hosted on
              *             Picasa, which are no longer supported.
              */
             @Deprecated
+            @Column(Cursor.FIELD_TYPE_STRING)
             public static final String PICASA_ID = "picasa_id";
 
             /**
              * Whether the video should be published as public or private
-             * <P>Type: INTEGER</P>
              */
+            @Column(Cursor.FIELD_TYPE_INTEGER)
             public static final String IS_PRIVATE = "isprivate";
 
             /**
              * The latitude where the image was captured.
-             * <P>Type: DOUBLE</P>
              *
              * @deprecated location details are no longer indexed for privacy
              *             reasons, and this value is now always {@code null}.
@@ -1472,11 +1457,11 @@
              *             {@link ExifInterface#getLatLong(float[])}.
              */
             @Deprecated
+            @Column(value = Cursor.FIELD_TYPE_FLOAT, readOnly = true)
             public static final String LATITUDE = "latitude";
 
             /**
              * The longitude where the image was captured.
-             * <P>Type: DOUBLE</P>
              *
              * @deprecated location details are no longer indexed for privacy
              *             reasons, and this value is now always {@code null}.
@@ -1484,40 +1469,40 @@
              *             {@link ExifInterface#getLatLong(float[])}.
              */
             @Deprecated
+            @Column(value = Cursor.FIELD_TYPE_FLOAT, readOnly = true)
             public static final String LONGITUDE = "longitude";
 
             /**
              * The date & time that the image was taken in units
              * of milliseconds since jan 1, 1970.
-             * <P>Type: INTEGER</P>
              */
+            @Column(value = Cursor.FIELD_TYPE_INTEGER, readOnly = true)
             public static final String DATE_TAKEN = "datetaken";
 
             /**
              * The orientation for the image expressed as degrees.
              * Only degrees 0, 90, 180, 270 will work.
-             * <P>Type: INTEGER</P>
              */
+            @Column(value = Cursor.FIELD_TYPE_INTEGER, readOnly = true)
             public static final String ORIENTATION = "orientation";
 
             /**
              * The mini thumb id.
-             * <P>Type: INTEGER</P>
              *
              * @deprecated all thumbnails should be obtained via
              *             {@link MediaStore.Images.Thumbnails#getThumbnail}, as this
              *             value is no longer supported.
              */
             @Deprecated
+            @Column(Cursor.FIELD_TYPE_INTEGER)
             public static final String MINI_THUMB_MAGIC = "mini_thumb_magic";
 
             /**
              * The primary bucket ID of this media item. This can be useful to
              * present the user a first-level clustering of related media items.
              * This is a read-only column that is automatically computed.
-             * <p>
-             * Type: INTEGER
              */
+            @Column(value = Cursor.FIELD_TYPE_INTEGER, readOnly = true)
             public static final String BUCKET_ID = "bucket_id";
 
             /**
@@ -1525,9 +1510,8 @@
              * useful to present the user a first-level clustering of related
              * media items. This is a read-only column that is automatically
              * computed.
-             * <p>
-             * Type: TEXT
              */
+            @Column(value = Cursor.FIELD_TYPE_STRING, readOnly = true)
             public static final String BUCKET_DISPLAY_NAME = "bucket_display_name";
 
             /**
@@ -1541,9 +1525,8 @@
              * {@code IMG1024.BURST001.JPG} and {@code IMG1024.BURST002.JPG}
              * will have the same {@link #GROUP_ID} because the first portion of
              * their filenames is identical.
-             * <p>
-             * Type: INTEGER
              */
+            @Column(value = Cursor.FIELD_TYPE_INTEGER, readOnly = true)
             public static final String GROUP_ID = "group_id";
         }
 
@@ -1848,8 +1831,6 @@
              * apps should use
              * {@link ContentResolver#openFileDescriptor(Uri, String)} to gain
              * access.
-             * <p>
-             * Type: TEXT
              *
              * @deprecated Apps may not have filesystem permissions to directly
              *             access this path. Instead of trying to open this path
@@ -1860,39 +1841,45 @@
              *             {@link android.os.Build.VERSION_CODES#Q} or higher.
              */
             @Deprecated
+            @Column(Cursor.FIELD_TYPE_STRING)
             public static final String DATA = "_data";
 
             /**
              * The original image for the thumbnal
-             * <P>Type: INTEGER (ID from Images table)</P>
              */
+            @Column(Cursor.FIELD_TYPE_INTEGER)
             public static final String IMAGE_ID = "image_id";
 
             /**
              * The kind of the thumbnail
-             * <P>Type: INTEGER (One of the values below)</P>
              */
+            @Column(Cursor.FIELD_TYPE_INTEGER)
             public static final String KIND = "kind";
 
             public static final int MINI_KIND = ThumbnailConstants.MINI_KIND;
             public static final int FULL_SCREEN_KIND = ThumbnailConstants.FULL_SCREEN_KIND;
             public static final int MICRO_KIND = ThumbnailConstants.MICRO_KIND;
+
             /**
              * The blob raw data of thumbnail
-             * <P>Type: DATA STREAM</P>
+             *
+             * @deprecated this column never existed internally, and could never
+             *             have returned valid data.
              */
+            @Deprecated
+            @Column(Cursor.FIELD_TYPE_BLOB)
             public static final String THUMB_DATA = "thumb_data";
 
             /**
              * The width of the thumbnal
-             * <P>Type: INTEGER (long)</P>
              */
+            @Column(value = Cursor.FIELD_TYPE_INTEGER, readOnly = true)
             public static final String WIDTH = "width";
 
             /**
              * The height of the thumbnail
-             * <P>Type: INTEGER (long)</P>
              */
+            @Column(value = Cursor.FIELD_TYPE_INTEGER, readOnly = true)
             public static final String HEIGHT = "height";
         }
     }
@@ -1909,79 +1896,80 @@
             /**
              * A non human readable key calculated from the TITLE, used for
              * searching, sorting and grouping
-             * <P>Type: TEXT</P>
              */
+            @Column(value = Cursor.FIELD_TYPE_STRING, readOnly = true)
             public static final String TITLE_KEY = "title_key";
 
             /**
              * The duration of the audio file, in ms
-             * <P>Type: INTEGER (long)</P>
              */
+            @Column(value = Cursor.FIELD_TYPE_INTEGER, readOnly = true)
             public static final String DURATION = "duration";
 
             /**
              * The position, in ms, playback was at when playback for this file
              * was last stopped.
-             * <P>Type: INTEGER (long)</P>
              */
+            @Column(Cursor.FIELD_TYPE_INTEGER)
             public static final String BOOKMARK = "bookmark";
 
             /**
              * The id of the artist who created the audio file, if any
-             * <P>Type: INTEGER (long)</P>
              */
+            @Column(value = Cursor.FIELD_TYPE_INTEGER, readOnly = true)
             public static final String ARTIST_ID = "artist_id";
 
             /**
              * The artist who created the audio file, if any
-             * <P>Type: TEXT</P>
              */
+            @Column(value = Cursor.FIELD_TYPE_STRING, readOnly = true)
             public static final String ARTIST = "artist";
 
             /**
              * The artist credited for the album that contains the audio file
-             * <P>Type: TEXT</P>
              * @hide
              */
+            @Column(value = Cursor.FIELD_TYPE_STRING, readOnly = true)
             public static final String ALBUM_ARTIST = "album_artist";
 
             /**
              * Whether the song is part of a compilation
-             * <P>Type: TEXT</P>
              * @hide
              */
+            @Deprecated
+            // @Column(Cursor.FIELD_TYPE_STRING)
             public static final String COMPILATION = "compilation";
 
             /**
              * A non human readable key calculated from the ARTIST, used for
              * searching, sorting and grouping
-             * <P>Type: TEXT</P>
              */
+            @Column(value = Cursor.FIELD_TYPE_STRING, readOnly = true)
             public static final String ARTIST_KEY = "artist_key";
 
             /**
              * The composer of the audio file, if any
-             * <P>Type: TEXT</P>
              */
+            @Column(value = Cursor.FIELD_TYPE_STRING, readOnly = true)
             public static final String COMPOSER = "composer";
 
             /**
              * The id of the album the audio file is from, if any
-             * <P>Type: INTEGER (long)</P>
              */
+            @Column(value = Cursor.FIELD_TYPE_INTEGER, readOnly = true)
             public static final String ALBUM_ID = "album_id";
 
             /**
              * The album the audio file is from, if any
-             * <P>Type: TEXT</P>
              */
+            @Column(value = Cursor.FIELD_TYPE_STRING, readOnly = true)
             public static final String ALBUM = "album";
 
             /**
              * A non human readable key calculated from the ALBUM, used for
              * searching, sorting and grouping
-             * <P>Type: TEXT</P>
              */
+            @Column(value = Cursor.FIELD_TYPE_STRING, readOnly = true)
             public static final String ALBUM_KEY = "album_key";
 
             /**
@@ -1990,63 +1978,63 @@
              * disc number. For multi-disc sets, this number will
              * be 1xxx for tracks on the first disc, 2xxx for tracks
              * on the second disc, etc.
-             * <P>Type: INTEGER</P>
              */
+            @Column(value = Cursor.FIELD_TYPE_INTEGER, readOnly = true)
             public static final String TRACK = "track";
 
             /**
              * The year the audio file was recorded, if any
-             * <P>Type: INTEGER</P>
              */
+            @Column(value = Cursor.FIELD_TYPE_INTEGER, readOnly = true)
             public static final String YEAR = "year";
 
             /**
              * Non-zero if the audio file is music
-             * <P>Type: INTEGER (boolean)</P>
              */
+            @Column(value = Cursor.FIELD_TYPE_INTEGER, readOnly = true)
             public static final String IS_MUSIC = "is_music";
 
             /**
              * Non-zero if the audio file is a podcast
-             * <P>Type: INTEGER (boolean)</P>
              */
+            @Column(value = Cursor.FIELD_TYPE_INTEGER, readOnly = true)
             public static final String IS_PODCAST = "is_podcast";
 
             /**
              * Non-zero if the audio file may be a ringtone
-             * <P>Type: INTEGER (boolean)</P>
              */
+            @Column(value = Cursor.FIELD_TYPE_INTEGER, readOnly = true)
             public static final String IS_RINGTONE = "is_ringtone";
 
             /**
              * Non-zero if the audio file may be an alarm
-             * <P>Type: INTEGER (boolean)</P>
              */
+            @Column(value = Cursor.FIELD_TYPE_INTEGER, readOnly = true)
             public static final String IS_ALARM = "is_alarm";
 
             /**
              * Non-zero if the audio file may be a notification sound
-             * <P>Type: INTEGER (boolean)</P>
              */
+            @Column(value = Cursor.FIELD_TYPE_INTEGER, readOnly = true)
             public static final String IS_NOTIFICATION = "is_notification";
 
             /**
              * Non-zero if the audio file is an audiobook
-             * <P>Type: INTEGER (boolean)</P>
              */
+            @Column(value = Cursor.FIELD_TYPE_INTEGER, readOnly = true)
             public static final String IS_AUDIOBOOK = "is_audiobook";
 
             /**
              * The genre of the audio file, if any
-             * <P>Type: TEXT</P>
              * Does not exist in the database - only used by the media scanner for inserts.
              * @hide
              */
+            @Deprecated
+            // @Column(Cursor.FIELD_TYPE_STRING)
             public static final String GENRE = "genre";
 
             /**
              * The resource URI of a localized title, if any
-             * <P>Type: TEXT</P>
              * Conforms to this pattern:
              *   Scheme: {@link ContentResolver.SCHEME_ANDROID_RESOURCE}
              *   Authority: Package Name of ringtone title provider
@@ -2054,6 +2042,7 @@
              *   Second Path Segment: Resource ID of title
              * @hide
              */
+            @Column(value = Cursor.FIELD_TYPE_STRING, readOnly = true)
             public static final String TITLE_RESOURCE_URI = "title_resource_uri";
         }
 
@@ -2142,6 +2131,7 @@
              * @deprecated Apps may not have filesystem permissions to directly
              *             access this path.
              */
+            @Deprecated
             public static @Nullable Uri getContentUriForPath(@NonNull String path) {
                 return getContentUri(getVolumeName(new File(path)));
             }
@@ -2202,8 +2192,8 @@
         public interface GenresColumns {
             /**
              * The name of the genre
-             * <P>Type: TEXT</P>
              */
+            @Column(Cursor.FIELD_TYPE_STRING)
             public static final String NAME = "name";
         }
 
@@ -2287,14 +2277,14 @@
 
                 /**
                  * The ID of the audio file
-                 * <P>Type: INTEGER (long)</P>
                  */
+                @Column(Cursor.FIELD_TYPE_INTEGER)
                 public static final String AUDIO_ID = "audio_id";
 
                 /**
                  * The ID of the genre
-                 * <P>Type: INTEGER (long)</P>
                  */
+                @Column(Cursor.FIELD_TYPE_INTEGER)
                 public static final String GENRE_ID = "genre_id";
             }
         }
@@ -2305,8 +2295,8 @@
         public interface PlaylistsColumns {
             /**
              * The name of the playlist
-             * <P>Type: TEXT</P>
              */
+            @Column(Cursor.FIELD_TYPE_STRING)
             public static final String NAME = "name";
 
             /**
@@ -2317,8 +2307,6 @@
              * apps should use
              * {@link ContentResolver#openFileDescriptor(Uri, String)} to gain
              * access.
-             * <p>
-             * Type: TEXT
              *
              * @deprecated Apps may not have filesystem permissions to directly
              *             access this path. Instead of trying to open this path
@@ -2329,21 +2317,22 @@
              *             {@link android.os.Build.VERSION_CODES#Q} or higher.
              */
             @Deprecated
+            @Column(Cursor.FIELD_TYPE_STRING)
             public static final String DATA = "_data";
 
             /**
              * The time the file was added to the media provider
              * Units are seconds since 1970.
-             * <P>Type: INTEGER (long)</P>
              */
+            @Column(value = Cursor.FIELD_TYPE_INTEGER, readOnly = true)
             public static final String DATE_ADDED = "date_added";
 
             /**
              * The time the file was last modified
              * Units are seconds since 1970.
              * NOTE: This is for internal use by the media scanner.  Do not modify this field.
-             * <P>Type: INTEGER (long)</P>
              */
+            @Column(value = Cursor.FIELD_TYPE_INTEGER, readOnly = true)
             public static final String DATE_MODIFIED = "date_modified";
         }
 
@@ -2426,6 +2415,7 @@
                 /**
                  * The ID within the playlist.
                  */
+                @Column(Cursor.FIELD_TYPE_INTEGER)
                 public static final String _ID = "_id";
 
                 /**
@@ -2436,20 +2426,20 @@
 
                 /**
                  * The ID of the audio file
-                 * <P>Type: INTEGER (long)</P>
                  */
+                @Column(Cursor.FIELD_TYPE_INTEGER)
                 public static final String AUDIO_ID = "audio_id";
 
                 /**
                  * The ID of the playlist
-                 * <P>Type: INTEGER (long)</P>
                  */
+                @Column(Cursor.FIELD_TYPE_INTEGER)
                 public static final String PLAYLIST_ID = "playlist_id";
 
                 /**
                  * The order of the songs in the playlist
-                 * <P>Type: INTEGER (long)></P>
                  */
+                @Column(Cursor.FIELD_TYPE_INTEGER)
                 public static final String PLAY_ORDER = "play_order";
 
                 /**
@@ -2465,25 +2455,27 @@
         public interface ArtistColumns {
             /**
              * The artist who created the audio file, if any
-             * <P>Type: TEXT</P>
              */
+            @Column(value = Cursor.FIELD_TYPE_STRING, readOnly = true)
             public static final String ARTIST = "artist";
 
             /**
              * A non human readable key calculated from the ARTIST, used for
              * searching, sorting and grouping
-             * <P>Type: TEXT</P>
              */
+            @Column(value = Cursor.FIELD_TYPE_STRING, readOnly = true)
             public static final String ARTIST_KEY = "artist_key";
 
             /**
              * The number of albums in the database for this artist
              */
+            @Column(value = Cursor.FIELD_TYPE_INTEGER, readOnly = true)
             public static final String NUMBER_OF_ALBUMS = "number_of_albums";
 
             /**
              * The number of albums in the database for this artist
              */
+            @Column(value = Cursor.FIELD_TYPE_INTEGER, readOnly = true)
             public static final String NUMBER_OF_TRACKS = "number_of_tracks";
         }
 
@@ -2551,34 +2543,34 @@
 
             /**
              * The id for the album
-             * <P>Type: INTEGER</P>
              */
+            @Column(value = Cursor.FIELD_TYPE_INTEGER, readOnly = true)
             public static final String ALBUM_ID = "album_id";
 
             /**
              * The album on which the audio file appears, if any
-             * <P>Type: TEXT</P>
              */
+            @Column(value = Cursor.FIELD_TYPE_STRING, readOnly = true)
             public static final String ALBUM = "album";
 
             /**
              * The artist whose songs appear on this album
-             * <P>Type: TEXT</P>
              */
+            @Column(value = Cursor.FIELD_TYPE_STRING, readOnly = true)
             public static final String ARTIST = "artist";
 
             /**
              * The number of songs on this album
-             * <P>Type: INTEGER</P>
              */
+            @Column(value = Cursor.FIELD_TYPE_INTEGER, readOnly = true)
             public static final String NUMBER_OF_SONGS = "numsongs";
 
             /**
              * This column is available when getting album info via artist,
              * and indicates the number of songs on the album by the given
              * artist.
-             * <P>Type: INTEGER</P>
              */
+            @Column(value = Cursor.FIELD_TYPE_INTEGER, readOnly = true)
             public static final String NUMBER_OF_SONGS_FOR_ARTIST = "numsongs_by_artist";
 
             /**
@@ -2586,8 +2578,8 @@
              * on this album were released. This will often
              * be the same as {@link #LAST_YEAR}, but for compilation albums
              * they might differ.
-             * <P>Type: INTEGER</P>
              */
+            @Column(value = Cursor.FIELD_TYPE_INTEGER, readOnly = true)
             public static final String FIRST_YEAR = "minyear";
 
             /**
@@ -2595,20 +2587,19 @@
              * on this album were released. This will often
              * be the same as {@link #FIRST_YEAR}, but for compilation albums
              * they might differ.
-             * <P>Type: INTEGER</P>
              */
+            @Column(value = Cursor.FIELD_TYPE_INTEGER, readOnly = true)
             public static final String LAST_YEAR = "maxyear";
 
             /**
              * A non human readable key calculated from the ALBUM, used for
              * searching, sorting and grouping
-             * <P>Type: TEXT</P>
              */
+            @Column(value = Cursor.FIELD_TYPE_STRING, readOnly = true)
             public static final String ALBUM_KEY = "album_key";
 
             /**
              * Cached album art.
-             * <P>Type: TEXT</P>
              *
              * @deprecated Apps may not have filesystem permissions to directly
              *             access this path. Instead of trying to open this path
@@ -2619,6 +2610,7 @@
              *             {@link android.os.Build.VERSION_CODES#Q} or higher.
              */
             @Deprecated
+            @Column(Cursor.FIELD_TYPE_STRING)
             public static final String ALBUM_ART = "album_art";
         }
 
@@ -2676,6 +2668,43 @@
             // Not instantiable.
             private Radio() { }
         }
+
+        /**
+         * This class provides utility methods to obtain thumbnails for various
+         * {@link Audio} items.
+         *
+         * @deprecated Callers should migrate to using
+         *             {@link ContentResolver#loadThumbnail}, since it offers
+         *             richer control over requested thumbnail sizes and
+         *             cancellation behavior.
+         * @hide
+         */
+        @Deprecated
+        public static class Thumbnails implements BaseColumns {
+            /**
+             * Path to the thumbnail file on disk.
+             * <p>
+             * Note that apps may not have filesystem permissions to directly
+             * access this path. Instead of trying to open this path directly,
+             * apps should use
+             * {@link ContentResolver#openFileDescriptor(Uri, String)} to gain
+             * access.
+             *
+             * @deprecated Apps may not have filesystem permissions to directly
+             *             access this path. Instead of trying to open this path
+             *             directly, apps should use
+             *             {@link ContentResolver#loadThumbnail}
+             *             to gain access. This value will always be
+             *             {@code NULL} for apps targeting
+             *             {@link android.os.Build.VERSION_CODES#Q} or higher.
+             */
+            @Deprecated
+            @Column(Cursor.FIELD_TYPE_STRING)
+            public static final String DATA = "_data";
+
+            @Column(Cursor.FIELD_TYPE_INTEGER)
+            public static final String ALBUM_ID = "album_id";
+        }
     }
 
     public static final class Video {
@@ -2693,61 +2722,60 @@
 
             /**
              * The duration of the video file, in ms
-             * <P>Type: INTEGER (long)</P>
              */
+            @Column(value = Cursor.FIELD_TYPE_INTEGER, readOnly = true)
             public static final String DURATION = "duration";
 
             /**
              * The artist who created the video file, if any
-             * <P>Type: TEXT</P>
              */
+            @Column(value = Cursor.FIELD_TYPE_STRING, readOnly = true)
             public static final String ARTIST = "artist";
 
             /**
              * The album the video file is from, if any
-             * <P>Type: TEXT</P>
              */
+            @Column(value = Cursor.FIELD_TYPE_STRING, readOnly = true)
             public static final String ALBUM = "album";
 
             /**
              * The resolution of the video file, formatted as "XxY"
-             * <P>Type: TEXT</P>
              */
+            @Column(value = Cursor.FIELD_TYPE_STRING, readOnly = true)
             public static final String RESOLUTION = "resolution";
 
             /**
              * The description of the video recording
-             * <P>Type: TEXT</P>
              */
+            @Column(value = Cursor.FIELD_TYPE_STRING, readOnly = true)
             public static final String DESCRIPTION = "description";
 
             /**
              * Whether the video should be published as public or private
-             * <P>Type: INTEGER</P>
              */
+            @Column(Cursor.FIELD_TYPE_INTEGER)
             public static final String IS_PRIVATE = "isprivate";
 
             /**
              * The user-added tags associated with a video
-             * <P>Type: TEXT</P>
              */
+            @Column(Cursor.FIELD_TYPE_STRING)
             public static final String TAGS = "tags";
 
             /**
              * The YouTube category of the video
-             * <P>Type: TEXT</P>
              */
+            @Column(Cursor.FIELD_TYPE_STRING)
             public static final String CATEGORY = "category";
 
             /**
              * The language of the video
-             * <P>Type: TEXT</P>
              */
+            @Column(Cursor.FIELD_TYPE_STRING)
             public static final String LANGUAGE = "language";
 
             /**
              * The latitude where the video was captured.
-             * <P>Type: DOUBLE</P>
              *
              * @deprecated location details are no longer indexed for privacy
              *             reasons, and this value is now always {@code null}.
@@ -2755,11 +2783,11 @@
              *             {@link ExifInterface#getLatLong(float[])}.
              */
             @Deprecated
+            @Column(value = Cursor.FIELD_TYPE_FLOAT, readOnly = true)
             public static final String LATITUDE = "latitude";
 
             /**
              * The longitude where the video was captured.
-             * <P>Type: DOUBLE</P>
              *
              * @deprecated location details are no longer indexed for privacy
              *             reasons, and this value is now always {@code null}.
@@ -2767,33 +2795,33 @@
              *             {@link ExifInterface#getLatLong(float[])}.
              */
             @Deprecated
+            @Column(value = Cursor.FIELD_TYPE_FLOAT, readOnly = true)
             public static final String LONGITUDE = "longitude";
 
             /**
              * The date & time that the video was taken in units
              * of milliseconds since jan 1, 1970.
-             * <P>Type: INTEGER</P>
              */
+            @Column(value = Cursor.FIELD_TYPE_INTEGER, readOnly = true)
             public static final String DATE_TAKEN = "datetaken";
 
             /**
              * The mini thumb id.
-             * <P>Type: INTEGER</P>
              *
              * @deprecated all thumbnails should be obtained via
              *             {@link MediaStore.Images.Thumbnails#getThumbnail}, as this
              *             value is no longer supported.
              */
             @Deprecated
+            @Column(Cursor.FIELD_TYPE_INTEGER)
             public static final String MINI_THUMB_MAGIC = "mini_thumb_magic";
 
             /**
              * The primary bucket ID of this media item. This can be useful to
              * present the user a first-level clustering of related media items.
              * This is a read-only column that is automatically computed.
-             * <p>
-             * Type: INTEGER
              */
+            @Column(value = Cursor.FIELD_TYPE_INTEGER, readOnly = true)
             public static final String BUCKET_ID = "bucket_id";
 
             /**
@@ -2801,9 +2829,8 @@
              * useful to present the user a first-level clustering of related
              * media items. This is a read-only column that is automatically
              * computed.
-             * <p>
-             * Type: TEXT
              */
+            @Column(value = Cursor.FIELD_TYPE_STRING, readOnly = true)
             public static final String BUCKET_DISPLAY_NAME = "bucket_display_name";
 
             /**
@@ -2817,9 +2844,8 @@
              * {@code IMG1024.BURST001.JPG} and {@code IMG1024.BURST002.JPG}
              * will have the same {@link #GROUP_ID} because the first portion of
              * their filenames is identical.
-             * <p>
-             * Type: INTEGER
              */
+            @Column(value = Cursor.FIELD_TYPE_INTEGER, readOnly = true)
             public static final String GROUP_ID = "group_id";
 
             /**
@@ -2827,29 +2853,29 @@
              * video should start playing at the next time it is opened. If the value is null or
              * out of the range 0..DURATION-1 then the video should start playing from the
              * beginning.
-             * <P>Type: INTEGER</P>
              */
+            @Column(Cursor.FIELD_TYPE_INTEGER)
             public static final String BOOKMARK = "bookmark";
 
             /**
              * The standard of color aspects
-             * <P>Type: INTEGER</P>
              * @hide
              */
+            @Column(value = Cursor.FIELD_TYPE_INTEGER, readOnly = true)
             public static final String COLOR_STANDARD = "color_standard";
 
             /**
              * The transfer of color aspects
-             * <P>Type: INTEGER</P>
              * @hide
              */
+            @Column(value = Cursor.FIELD_TYPE_INTEGER, readOnly = true)
             public static final String COLOR_TRANSFER = "color_transfer";
 
             /**
              * The range of color aspects
-             * <P>Type: INTEGER</P>
              * @hide
              */
+            @Column(value = Cursor.FIELD_TYPE_INTEGER, readOnly = true)
             public static final String COLOR_RANGE = "color_range";
         }
 
@@ -3016,8 +3042,6 @@
 
             /**
              * Path to the thumbnail file on disk.
-             * <p>
-             * Type: TEXT
              *
              * @deprecated Apps may not have filesystem permissions to directly
              *             access this path. Instead of trying to open this path
@@ -3028,18 +3052,19 @@
              *             {@link android.os.Build.VERSION_CODES#Q} or higher.
              */
             @Deprecated
+            @Column(Cursor.FIELD_TYPE_STRING)
             public static final String DATA = "_data";
 
             /**
              * The original image for the thumbnal
-             * <P>Type: INTEGER (ID from Video table)</P>
              */
+            @Column(Cursor.FIELD_TYPE_INTEGER)
             public static final String VIDEO_ID = "video_id";
 
             /**
              * The kind of the thumbnail
-             * <P>Type: INTEGER (One of the values below)</P>
              */
+            @Column(Cursor.FIELD_TYPE_INTEGER)
             public static final String KIND = "kind";
 
             public static final int MINI_KIND = ThumbnailConstants.MINI_KIND;
@@ -3048,14 +3073,14 @@
 
             /**
              * The width of the thumbnal
-             * <P>Type: INTEGER (long)</P>
              */
+            @Column(value = Cursor.FIELD_TYPE_INTEGER, readOnly = true)
             public static final String WIDTH = "width";
 
             /**
              * The height of the thumbnail
-             * <P>Type: INTEGER (long)</P>
              */
+            @Column(value = Cursor.FIELD_TYPE_INTEGER, readOnly = true)
             public static final String HEIGHT = "height";
         }
     }
diff --git a/core/java/android/service/contentcapture/ContentCaptureService.java b/core/java/android/service/contentcapture/ContentCaptureService.java
index d2f9859..ae70775 100644
--- a/core/java/android/service/contentcapture/ContentCaptureService.java
+++ b/core/java/android/service/contentcapture/ContentCaptureService.java
@@ -49,7 +49,9 @@
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
+import java.util.ArrayList;
 import java.util.List;
+import java.util.Set;
 
 /**
  * A service used to capture the content of the screen to provide contextual data in other areas of
@@ -164,17 +166,9 @@
     }
 
     /**
-     * Explicitly limits content capture to the given packages and activities.
-     *
-     * <p>To reset the whitelist, call it passing {@code null} to both arguments.
-     *
-     * <p>Useful when the service wants to restrict content capture to a category of apps, like
-     * chat apps. For example, if the service wants to support view captures on all activities of
-     * app {@code ChatApp1} and just activities {@code act1} and {@code act2} of {@code ChatApp2},
-     * it would call: {@code setContentCaptureWhitelist(Arrays.asList("ChatApp1"),
-     * Arrays.asList(new ComponentName("ChatApp2", "act1"),
-     * new ComponentName("ChatApp2", "act2")));}
+     * @deprecated use {@link #setContentCaptureWhitelist(Set, Set)} instead
      */
+    @Deprecated
     public final void setContentCaptureWhitelist(@Nullable List<String> packages,
             @Nullable List<ComponentName> activities) {
         final IContentCaptureServiceCallback callback = mCallback;
@@ -190,6 +184,27 @@
     }
 
     /**
+     * Explicitly limits content capture to the given packages and activities.
+     *
+     * <p>To reset the whitelist, call it passing {@code null} to both arguments.
+     *
+     * <p>Useful when the service wants to restrict content capture to a category of apps, like
+     * chat apps. For example, if the service wants to support view captures on all activities of
+     * app {@code ChatApp1} and just activities {@code act1} and {@code act2} of {@code ChatApp2},
+     * it would call: {@code setContentCaptureWhitelist(Sets.newArraySet("ChatApp1"),
+     * Sets.newArraySet(new ComponentName("ChatApp2", "act1"),
+     * new ComponentName("ChatApp2", "act2")));}
+     */
+    public final void setContentCaptureWhitelist(@Nullable Set<String> packages,
+            @Nullable Set<ComponentName> activities) {
+        setContentCaptureWhitelist(toList(packages), toList(activities));
+    }
+
+    private <T> ArrayList<T> toList(@Nullable Set<T> set) {
+        return set == null ? null : new ArrayList<T>(set);
+    }
+
+    /**
      * Called when the Android system connects to service.
      *
      * <p>You should generally do initialization here rather than in {@link #onCreate}.
diff --git a/core/java/android/service/notification/Adjustment.java b/core/java/android/service/notification/Adjustment.java
index 93189b3..2961426 100644
--- a/core/java/android/service/notification/Adjustment.java
+++ b/core/java/android/service/notification/Adjustment.java
@@ -15,6 +15,7 @@
  */
 package android.service.notification;
 
+import android.annotation.SystemApi;
 import android.app.Notification;
 import android.os.Bundle;
 import android.os.Parcel;
@@ -36,13 +37,13 @@
      * See {@link android.app.Notification.Builder#addPerson(String)}.
      * @hide
      */
+    @SystemApi
     public static final String KEY_PEOPLE = "key_people";
     /**
      * Parcelable {@code ArrayList} of {@link SnoozeCriterion}. These criteria may be visible to
      * users. If a user chooses to snooze a notification until one of these criterion, the
      * assistant will be notified via
      * {@link NotificationAssistantService#onNotificationSnoozedUntilContext}.
-     * @hide
      */
     public static final String KEY_SNOOZE_CRITERIA = "key_snooze_criteria";
     /**
@@ -111,7 +112,11 @@
         mUser = user;
     }
 
-    private Adjustment(Parcel in) {
+    /**
+     * @hide
+     */
+    @SystemApi
+    protected Adjustment(Parcel in) {
         if (in.readInt() == 1) {
             mPackage = in.readString();
         } else {
diff --git a/core/java/android/service/notification/NotificationAssistantService.java b/core/java/android/service/notification/NotificationAssistantService.java
index e93b158..a697248 100644
--- a/core/java/android/service/notification/NotificationAssistantService.java
+++ b/core/java/android/service/notification/NotificationAssistantService.java
@@ -21,6 +21,7 @@
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.SdkConstant;
+import android.annotation.SystemApi;
 import android.app.Notification;
 import android.app.NotificationChannel;
 import android.app.admin.DevicePolicyManager;
@@ -103,7 +104,6 @@
      *
      * @param sbn the notification to snooze
      * @param snoozeCriterionId the {@link SnoozeCriterion#getId()} representing a device context.
-     * @hide
      */
     abstract public void onNotificationSnoozedUntilContext(StatusBarNotification sbn,
             String snoozeCriterionId);
@@ -111,12 +111,13 @@
     /**
      * A notification was posted by an app. Called before post.
      *
+     * <p>Note: this method is only called if you don't override
+     * {@link #onNotificationEnqueued(StatusBarNotification, NotificationChannel)}.</p>
+     *
      * @param sbn the new notification
      * @return an adjustment or null to take no action, within 100ms.
      */
-    public Adjustment onNotificationEnqueued(StatusBarNotification sbn) {
-        return null;
-    }
+    abstract public Adjustment onNotificationEnqueued(StatusBarNotification sbn);
 
     /**
      * A notification was posted by an app. Called before post.
@@ -240,12 +241,11 @@
     /**
      * Inform the notification manager about un-snoozing a specific notification.
      * <p>
-     * This should only be used for notifications snoozed by this listener using
-     * {@link #snoozeNotification(String, String)}. Once un-snoozed, you will get a
+     * This should only be used for notifications snoozed because of a contextual snooze suggestion
+     * you provided via {@link Adjustment#KEY_SNOOZE_CRITERIA}. Once un-snoozed, you will get a
      * {@link #onNotificationPosted(StatusBarNotification, RankingMap)} callback for the
      * notification.
      * @param key The key of the notification to snooze
-     * @hide
      */
     public final void unsnoozeNotification(String key) {
         if (!isBound()) return;
diff --git a/core/java/android/service/notification/NotificationStats.java b/core/java/android/service/notification/NotificationStats.java
index 814b477..ebfabbf 100644
--- a/core/java/android/service/notification/NotificationStats.java
+++ b/core/java/android/service/notification/NotificationStats.java
@@ -16,6 +16,7 @@
 package android.service.notification;
 
 import android.annotation.IntDef;
+import android.annotation.SystemApi;
 import android.app.RemoteInput;
 import android.os.Parcel;
 import android.os.Parcelable;
@@ -98,7 +99,11 @@
     public NotificationStats() {
     }
 
-    private NotificationStats(Parcel in) {
+    /**
+     * @hide
+     */
+    @SystemApi
+    protected NotificationStats(Parcel in) {
         mSeen = in.readByte() != 0;
         mExpanded = in.readByte() != 0;
         mDirectReplied = in.readByte() != 0;
diff --git a/core/java/android/view/autofill/AutofillManager.java b/core/java/android/view/autofill/AutofillManager.java
index f93ac4c..a6b40ed 100644
--- a/core/java/android/view/autofill/AutofillManager.java
+++ b/core/java/android/view/autofill/AutofillManager.java
@@ -78,6 +78,7 @@
 import java.util.Collections;
 import java.util.List;
 import java.util.Objects;
+import java.util.Set;
 
 //TODO: use java.lang.ref.Cleaner once Android supports Java 9
 import sun.misc.Cleaner;
@@ -1780,6 +1781,18 @@
     }
 
     /**
+     * @deprecated use {@link #setAugmentedAutofillWhitelist(Set, Set)} instead.
+     * @hide
+     */
+    @SystemApi
+    @TestApi
+    @Deprecated
+    public void setAugmentedAutofillWhitelist(@Nullable List<String> packages,
+            @Nullable List<ComponentName> activities) {
+        // TODO(b/123100824): implement
+    }
+
+    /**
      * Explicitly limits augmented autofill to the given packages and activities.
      *
      * <p>To reset the whitelist, call it passing {@code null} to both arguments.
@@ -1799,8 +1812,8 @@
      */
     @SystemApi
     @TestApi
-    public void setAugmentedAutofillWhitelist(@Nullable List<String> packages,
-            @Nullable List<ComponentName> activities) {
+    public void setAugmentedAutofillWhitelist(@Nullable Set<String> packages,
+            @Nullable Set<ComponentName> activities) {
         // TODO(b/123100824): implement
     }
 
diff --git a/core/java/android/widget/ProgressBar.java b/core/java/android/widget/ProgressBar.java
index 29f070e..8113b40 100644
--- a/core/java/android/widget/ProgressBar.java
+++ b/core/java/android/widget/ProgressBar.java
@@ -199,7 +199,9 @@
     private boolean mMaxInitialized;
 
     private int mBehavior;
-    @UnsupportedAppUsage
+    // Better to define a Drawable that implements Animatable if you want to modify animation
+    // characteristics programatically.
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 124052713)
     private int mDuration;
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
     private boolean mIndeterminate;
@@ -284,7 +286,6 @@
             }
         }
 
-
         mDuration = a.getInt(R.styleable.ProgressBar_indeterminateDuration, mDuration);
 
         mMinWidth = a.getDimensionPixelSize(R.styleable.ProgressBar_minWidth, mMinWidth);
@@ -709,7 +710,17 @@
     /**
      * Define the drawable used to draw the progress bar in indeterminate mode.
      *
+     * <p>For the Drawable to animate, it must implement {@link Animatable}, or override
+     * {@link Drawable#onLevelChange(int)}.  A Drawable that implements Animatable will be animated
+     * via that interface and therefore provides the greatest amount of customization. A Drawable
+     * that only overrides onLevelChange(int) is animated directly by ProgressBar and only the
+     * animation {@link android.R.styleable#ProgressBar_indeterminateDuration duration},
+         * {@link android.R.styleable#ProgressBar_indeterminateBehavior repeating behavior}, and
+     * {@link #setInterpolator(Interpolator) interpolator} can be modified, and only before the
+     * indeterminate animation begins.
+     *
      * @param d the new drawable
+     * @attr ref android.R.styleable#ProgressBar_indeterminateDrawable
      * @see #getIndeterminateDrawable()
      * @see #setIndeterminate(boolean)
      */
@@ -1774,10 +1785,21 @@
 
     /**
      * Sets the acceleration curve for the indeterminate animation.
-     * The interpolator is loaded as a resource from the specified context.
+     *
+     * <p>The interpolator is loaded as a resource from the specified context. Defaults to a linear
+     * interpolation.
+     *
+     * <p>The interpolator only affects the indeterminate animation if the
+     * {@link #setIndeterminateDrawable(Drawable) supplied indeterminate drawable} does not
+     * implement {@link Animatable}.
+     *
+     * <p>This call must be made before the indeterminate animation starts for it to have an affect.
      *
      * @param context The application environment
      * @param resID The resource identifier of the interpolator to load
+     * @attr ref android.R.styleable#ProgressBar_interpolator
+     * @see #setInterpolator(Interpolator)
+     * @see #getInterpolator()
      */
     public void setInterpolator(Context context, @InterpolatorRes int resID) {
         setInterpolator(AnimationUtils.loadInterpolator(context, resID));
@@ -1787,7 +1809,17 @@
      * Sets the acceleration curve for the indeterminate animation.
      * Defaults to a linear interpolation.
      *
+     * <p>The interpolator only affects the indeterminate animation if the
+     * {@link #setIndeterminateDrawable(Drawable) supplied indeterminate drawable} does not
+     * implement {@link Animatable}.
+     *
+     * <p>This call must be made before the indeterminate animation starts for it to have
+     * an affect.
+     *
      * @param interpolator The interpolator which defines the acceleration curve
+     * @attr ref android.R.styleable#ProgressBar_interpolator
+     * @see #setInterpolator(Context, int)
+     * @see #getInterpolator()
      */
     public void setInterpolator(Interpolator interpolator) {
         mInterpolator = interpolator;
@@ -1797,6 +1829,9 @@
      * Gets the acceleration curve type for the indeterminate animation.
      *
      * @return the {@link Interpolator} associated to this animation
+     * @attr ref android.R.styleable#ProgressBar_interpolator
+     * @see #setInterpolator(Context, int)
+     * @see #setInterpolator(Interpolator)
      */
     @InspectableProperty
     public Interpolator getInterpolator() {
diff --git a/core/java/android/widget/Toast.java b/core/java/android/widget/Toast.java
index eef40e1..d037337 100644
--- a/core/java/android/widget/Toast.java
+++ b/core/java/android/widget/Toast.java
@@ -95,7 +95,7 @@
     public static final int LENGTH_LONG = 1;
 
     final Context mContext;
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
     final TN mTN;
     @UnsupportedAppUsage
     int mDuration;
@@ -354,7 +354,7 @@
     }
 
     private static class TN extends ITransientNotification.Stub {
-        @UnsupportedAppUsage
+        @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
         private final WindowManager.LayoutParams mParams = new WindowManager.LayoutParams();
 
         private static final int SHOW = 0;
@@ -362,18 +362,18 @@
         private static final int CANCEL = 2;
         final Handler mHandler;
 
-        @UnsupportedAppUsage
+        @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
         int mGravity;
         int mX;
-        @UnsupportedAppUsage
+        @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
         int mY;
         float mHorizontalMargin;
         float mVerticalMargin;
 
 
-        @UnsupportedAppUsage
+        @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
         View mView;
-        @UnsupportedAppUsage
+        @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
         View mNextView;
         int mDuration;
 
diff --git a/core/java/com/android/internal/app/ChooserActivity.java b/core/java/com/android/internal/app/ChooserActivity.java
index fbf8091..2009fd50 100644
--- a/core/java/com/android/internal/app/ChooserActivity.java
+++ b/core/java/com/android/internal/app/ChooserActivity.java
@@ -623,7 +623,14 @@
     private FileInfo extractFileInfo(Uri uri, ContentResolver resolver) {
         String fileName = null;
         boolean hasThumbnail = false;
-        Cursor cursor = resolver.query(uri, null, null, null, null);
+        Cursor cursor = null;
+
+        try {
+            cursor = resolver.query(uri, null, null, null, null);
+        } catch (SecurityException e) {
+            Log.w(TAG, "Error loading file preview", e);
+        }
+
         if (cursor != null && cursor.getCount() > 0) {
             int nameIndex = cursor.getColumnIndex(OpenableColumns.DISPLAY_NAME);
             int flagsIndex = cursor.getColumnIndex(DocumentsContract.Document.COLUMN_FLAGS);
@@ -655,46 +662,50 @@
         // due to permissions issues
         findViewById(R.id.file_copy_button).setVisibility(View.GONE);
 
-        try {
-            ContentResolver resolver = getContentResolver();
-            TextView fileNameView = findViewById(R.id.content_preview_filename);
-            String action = targetIntent.getAction();
-            if (Intent.ACTION_SEND.equals(action)) {
-                Uri uri = targetIntent.getParcelableExtra(Intent.EXTRA_STREAM);
+        String action = targetIntent.getAction();
+        if (Intent.ACTION_SEND.equals(action)) {
+            Uri uri = targetIntent.getParcelableExtra(Intent.EXTRA_STREAM);
+            loadFileUriIntoView(uri);
+        } else {
+            List<Uri> uris = targetIntent.getParcelableArrayListExtra(Intent.EXTRA_STREAM);
+            int uriCount = uris.size();
 
-                FileInfo fileInfo = extractFileInfo(uri, resolver);
-                fileNameView.setText(fileInfo.name);
-
-                if (fileInfo.hasThumbnail) {
-                    loadUriIntoView(R.id.content_preview_file_thumbnail, uri);
-                } else {
-                    ImageView fileIconView = findViewById(R.id.content_preview_file_icon);
-                    fileIconView.setVisibility(View.VISIBLE);
-                    fileIconView.setImageResource(R.drawable.ic_doc_generic);
-                }
+            if (uriCount == 0) {
+                contentPreviewLayout.setVisibility(View.GONE);
+                Log.i(TAG,
+                        "Appears to be no uris available in EXTRA_STREAM, removing "
+                                + "preview area");
+                return;
+            } else if (uriCount == 1) {
+                loadFileUriIntoView(uris.get(0));
             } else {
-                List<Uri> uris = targetIntent.getParcelableArrayListExtra(Intent.EXTRA_STREAM);
-                if (uris.size() == 0) {
-                    contentPreviewLayout.setVisibility(View.GONE);
-                    Log.i(TAG,
-                            "Appears to be no uris available in EXTRA_STREAM, removing preview "
-                                    + "area");
-                    return;
-                }
-
-                FileInfo fileInfo = extractFileInfo(uris.get(0), resolver);
-                int remFileCount = uris.size() - 1;
+                FileInfo fileInfo = extractFileInfo(uris.get(0), getContentResolver());
+                int remUriCount = uriCount - 1;
                 String fileName = getResources().getQuantityString(R.plurals.file_count,
-                        remFileCount, fileInfo.name, remFileCount);
+                        remUriCount, fileInfo.name, remUriCount);
 
+                TextView fileNameView = findViewById(R.id.content_preview_filename);
                 fileNameView.setText(fileName);
+
                 ImageView fileIconView = findViewById(R.id.content_preview_file_icon);
                 fileIconView.setVisibility(View.VISIBLE);
                 fileIconView.setImageResource(R.drawable.ic_file_copy);
             }
-        } catch (SecurityException e) {
-            Log.w(TAG, "Error loading file preview", e);
-            contentPreviewLayout.setVisibility(View.GONE);
+        }
+    }
+
+    private void loadFileUriIntoView(Uri uri) {
+        FileInfo fileInfo = extractFileInfo(uri, getContentResolver());
+
+        TextView fileNameView = findViewById(R.id.content_preview_filename);
+        fileNameView.setText(fileInfo.name);
+
+        if (fileInfo.hasThumbnail) {
+            loadUriIntoView(R.id.content_preview_file_thumbnail, uri);
+        } else {
+            ImageView fileIconView = findViewById(R.id.content_preview_file_icon);
+            fileIconView.setVisibility(View.VISIBLE);
+            fileIconView.setImageResource(R.drawable.ic_doc_generic);
         }
     }
 
diff --git a/core/java/com/android/internal/os/BackgroundThread.java b/core/java/com/android/internal/os/BackgroundThread.java
index eada142..22c832e 100644
--- a/core/java/com/android/internal/os/BackgroundThread.java
+++ b/core/java/com/android/internal/os/BackgroundThread.java
@@ -17,10 +17,13 @@
 package com.android.internal.os;
 
 import android.os.Handler;
+import android.os.HandlerExecutor;
 import android.os.HandlerThread;
 import android.os.Looper;
 import android.os.Trace;
 
+import java.util.concurrent.Executor;
+
 /**
  * Shared singleton background thread for each process.
  */
@@ -29,6 +32,7 @@
     private static final long SLOW_DELIVERY_THRESHOLD_MS = 30_000;
     private static BackgroundThread sInstance;
     private static Handler sHandler;
+    private static HandlerExecutor sHandlerExecutor;
 
     private BackgroundThread() {
         super("android.bg", android.os.Process.THREAD_PRIORITY_BACKGROUND);
@@ -43,6 +47,7 @@
             looper.setSlowLogThresholdMs(
                     SLOW_DISPATCH_THRESHOLD_MS, SLOW_DELIVERY_THRESHOLD_MS);
             sHandler = new Handler(sInstance.getLooper());
+            sHandlerExecutor = new HandlerExecutor(sHandler);
         }
     }
 
@@ -59,4 +64,11 @@
             return sHandler;
         }
     }
+
+    public static Executor getExecutor() {
+        synchronized (BackgroundThread.class) {
+            ensureThreadLocked();
+            return sHandlerExecutor;
+        }
+    }
 }
diff --git a/core/java/com/android/server/backup/PermissionBackupHelper.java b/core/java/com/android/server/backup/PermissionBackupHelper.java
index c0ba181..c7c423b 100644
--- a/core/java/com/android/server/backup/PermissionBackupHelper.java
+++ b/core/java/com/android/server/backup/PermissionBackupHelper.java
@@ -16,11 +16,14 @@
 
 package com.android.server.backup;
 
-import android.app.AppGlobals;
+import android.annotation.NonNull;
 import android.app.backup.BlobBackupHelper;
-import android.content.pm.IPackageManager;
+import android.os.UserHandle;
+import android.permission.PermissionManagerInternal;
 import android.util.Slog;
 
+import com.android.server.LocalServices;
+
 public class PermissionBackupHelper extends BlobBackupHelper {
     private static final String TAG = "PermissionBackup";
     private static final boolean DEBUG = false;
@@ -31,24 +34,26 @@
     // key under which the permission-grant state blob is committed to backup
     private static final String KEY_PERMISSIONS = "permissions";
 
-    private final int mUserId;
+    private final @NonNull UserHandle mUser;
+
+    private final @NonNull PermissionManagerInternal mPermissionManager;
 
     public PermissionBackupHelper(int userId) {
         super(STATE_VERSION, KEY_PERMISSIONS);
 
-        mUserId = userId;
+        mUser = UserHandle.of(userId);
+        mPermissionManager = LocalServices.getService(PermissionManagerInternal.class);
     }
 
     @Override
     protected byte[] getBackupPayload(String key) {
-        IPackageManager pm = AppGlobals.getPackageManager();
         if (DEBUG) {
             Slog.d(TAG, "Handling backup of " + key);
         }
         try {
             switch (key) {
                 case KEY_PERMISSIONS:
-                    return pm.getPermissionGrantBackup(mUserId);
+                    return mPermissionManager.backupRuntimePermissions(mUser);
 
                 default:
                     Slog.w(TAG, "Unexpected backup key " + key);
@@ -61,14 +66,13 @@
 
     @Override
     protected void applyRestoredPayload(String key, byte[] payload) {
-        IPackageManager pm = AppGlobals.getPackageManager();
         if (DEBUG) {
             Slog.d(TAG, "Handling restore of " + key);
         }
         try {
             switch (key) {
                 case KEY_PERMISSIONS:
-                    pm.restorePermissionGrants(payload, mUserId);
+                    mPermissionManager.restoreRuntimePermissions(payload, mUser);
                     break;
 
                 default:
diff --git a/core/jni/android_media_AudioSystem.cpp b/core/jni/android_media_AudioSystem.cpp
index a2ed7d3..bd998999 100644
--- a/core/jni/android_media_AudioSystem.cpp
+++ b/core/jni/android_media_AudioSystem.cpp
@@ -1918,7 +1918,6 @@
     for (jint i = 0; i < nb; i++) {
         deviceTypesVector.push_back((audio_devices_t) typesPtr[i]);
     }
-    env->ReleaseIntArrayElements(deviceTypes, typesPtr, 0);
 
     // check each address is a string and add device type/address to list for device affinity
     Vector<AudioDeviceTypeAddr> deviceVector;
@@ -1932,6 +1931,7 @@
         AudioDeviceTypeAddr dev = AudioDeviceTypeAddr(typesPtr[i], address);
         deviceVector.add(dev);
     }
+    env->ReleaseIntArrayElements(deviceTypes, typesPtr, 0);
 
     status_t status = AudioSystem::setUidDeviceAffinities((uid_t) uid, deviceVector);
     return (jint) nativeToJavaStatus(status);
diff --git a/core/jni/com_android_internal_os_Zygote.cpp b/core/jni/com_android_internal_os_Zygote.cpp
index 5cecf66a..fe81347 100644
--- a/core/jni/com_android_internal_os_Zygote.cpp
+++ b/core/jni/com_android_internal_os_Zygote.cpp
@@ -612,7 +612,7 @@
     }
 }
 
-static void CreatePkgSandbox(uid_t uid, const std::string& package_name, fail_fn_t fail_fn) {
+static void CreatePkgSandboxTarget(uid_t uid, const std::string& package_name, fail_fn_t fail_fn) {
     // Create /mnt/user/0/package/<package-name>
     userid_t user_id = multiuser_get_user_id(uid);
     std::string pkg_sandbox_dir = StringPrintf("/mnt/user/%d", user_id);
@@ -622,7 +622,7 @@
     CreateDir(pkg_sandbox_dir, 0700, AID_ROOT, AID_ROOT, fail_fn);
 
     StringAppendF(&pkg_sandbox_dir, "/%s", package_name.c_str());
-    CreateDir(pkg_sandbox_dir, 0700, AID_ROOT, AID_ROOT, fail_fn);
+    CreateDir(pkg_sandbox_dir, 0755, uid, uid, fail_fn);
 }
 
 static void BindMount(const std::string& sourceDir, const std::string& targetDir,
@@ -642,29 +642,98 @@
                                 fail_fn_t fail_fn) {
     std::string mntSourceDir = StringPrintf("%s/Android/%s/%s",
             mntSourceRoot.c_str(), dirName, packageName.c_str());
-    CreateDir(mntSourceDir, 0755, uid, uid, fail_fn);
 
     std::string mntTargetDir = StringPrintf("%s/Android/%s/%s",
             mntTargetRoot.c_str(), dirName, packageName.c_str());
-    CreateDir(mntTargetDir, 0755, uid, uid, fail_fn);
 
     BindMount(mntSourceDir, mntTargetDir, fail_fn);
 }
 
-
-static void createPkgSpecificDirRoots(const std::string& parentDir,
-                                      bool createSandbox,
-                                      mode_t mode, uid_t uid, gid_t gid,
-                                      fail_fn_t fail_fn) {
-    std::string androidDir = StringPrintf("%s/Android", parentDir.c_str());
-    CreateDir(androidDir, mode, uid, gid, fail_fn);
-    std::vector<std::string> dirs = {"data", "media", "obb"};
-    if (createSandbox) {
-        dirs.push_back("sandbox");
+static void CreateSubDirs(int dirfd, const std::string& parentDirPath,
+                          const std::vector<std::string>& subDirs,
+                          fail_fn_t fail_fn) {
+    for (auto& dirName : subDirs) {
+        struct stat sb;
+        if (TEMP_FAILURE_RETRY(fstatat(dirfd, dirName.c_str(), &sb, 0)) == 0) {
+            if (S_ISDIR(sb.st_mode)) {
+                continue;
+            } else if (TEMP_FAILURE_RETRY(unlinkat(dirfd, dirName.c_str(), 0)) == -1) {
+                fail_fn(CREATE_ERROR("Failed to unlinkat on %s/%s: %s",
+                        parentDirPath.c_str(), dirName.c_str(), strerror(errno)));
+            }
+        } else if (errno != ENOENT) {
+            fail_fn(CREATE_ERROR("Failed to fstatat on %s/%s: %s",
+                    parentDirPath.c_str(), dirName.c_str(), strerror(errno)));
+        }
+        if (TEMP_FAILURE_RETRY(mkdirat(dirfd, dirName.c_str(), 0700)) == -1) {
+            fail_fn(CREATE_ERROR("Failed to mkdirat on %s/%s: %s",
+                    parentDirPath.c_str(), dirName.c_str(), strerror(errno)));
+        }
     }
-    for (auto& dir : dirs) {
-        std::string path = StringPrintf("%s/%s", androidDir.c_str(), dir.c_str());
-        CreateDir(path, mode, uid, gid, fail_fn);
+}
+
+static void EnsurePkgSpecificDirs(const std::string& path,
+                                  const std::vector<std::string>& packageNames,
+                                  bool createSandboxDir,
+                                  fail_fn_t fail_fn) {
+    std::string androidDir = StringPrintf("%s/Android", path.c_str());
+    android::base::unique_fd androidFd(
+            open(androidDir.c_str(), O_RDONLY | O_DIRECTORY | O_CLOEXEC));
+    if (androidFd.get() < 0) {
+        if (errno == ENOENT || errno == ENOTDIR) {
+            if (errno == ENOTDIR && TEMP_FAILURE_RETRY(unlink(androidDir.c_str())) == -1) {
+                fail_fn(CREATE_ERROR("Failed to unlink %s: %s",
+                        androidDir.c_str(), strerror(errno)));
+            }
+            if (TEMP_FAILURE_RETRY(mkdir(androidDir.c_str(), 0700)) == -1) {
+                fail_fn(CREATE_ERROR("Failed to mkdir %s: %s",
+                        androidDir.c_str(), strerror(errno)));
+            }
+            androidFd.reset(open(androidDir.c_str(), O_RDONLY | O_DIRECTORY | O_CLOEXEC));
+        }
+
+        if (androidFd.get() < 0) {
+            fail_fn(CREATE_ERROR("Failed to open %s: %s", androidDir.c_str(), strerror(errno)));
+        }
+    }
+
+    std::vector<std::string> dataMediaObbDirs = {"data", "media", "obb"};
+    if (createSandboxDir) {
+        dataMediaObbDirs.push_back("sandbox");
+    }
+    CreateSubDirs(androidFd.get(), androidDir, dataMediaObbDirs, fail_fn);
+    if (createSandboxDir) {
+        dataMediaObbDirs.pop_back();
+    }
+    for (auto& dirName : dataMediaObbDirs) {
+        std::string dataDir = StringPrintf("%s/%s", androidDir.c_str(), dirName.c_str());
+        android::base::unique_fd dataFd(
+                openat(androidFd, dirName.c_str(), O_RDONLY | O_DIRECTORY | O_CLOEXEC));
+        if (dataFd.get() < 0) {
+            fail_fn(CREATE_ERROR("Failed to openat %s/%s: %s",
+                    androidDir.c_str(), dirName.c_str(), strerror(errno)));
+        }
+        CreateSubDirs(dataFd.get(), dataDir, packageNames, fail_fn);
+    }
+}
+
+static void CreatePkgSandboxSource(const std::string& sandboxSource, fail_fn_t fail_fn) {
+
+    struct stat sb;
+    if (TEMP_FAILURE_RETRY(stat(sandboxSource.c_str(), &sb)) == 0) {
+        if (S_ISDIR(sb.st_mode)) {
+            return;
+        } else if (TEMP_FAILURE_RETRY(unlink(sandboxSource.c_str())) == -1) {
+            fail_fn(CREATE_ERROR("Failed to unlink %s: %s",
+                    sandboxSource.c_str(), strerror(errno)));
+        }
+    } else if (errno != ENOENT) {
+        fail_fn(CREATE_ERROR("Failed to stat %s: %s",
+                sandboxSource.c_str(), strerror(errno)));
+    }
+    if (TEMP_FAILURE_RETRY(mkdir(sandboxSource.c_str(), 0700)) == -1) {
+        fail_fn(CREATE_ERROR("Failed to mkdir %s: %s",
+                sandboxSource.c_str(), strerror(errno)));
     }
 }
 
@@ -680,21 +749,21 @@
             StringAppendF(&mntTarget, "/%d", userId);
         }
 
-        if (TEMP_FAILURE_RETRY(access(mntSource.c_str(), F_OK)) < 0) {
+        if (TEMP_FAILURE_RETRY(access(mntSource.c_str(), F_OK)) == -1) {
             ALOGE("Can't access %s: %s", mntSource.c_str(), strerror(errno));
             continue;
         }
 
-        // Create /mnt/runtime/write/emulated/0/Android/{data,media,obb,sandbox}
-        createPkgSpecificDirRoots(mntSource, true, 0700, AID_ROOT, AID_ROOT, fail_fn);
+        // Ensure /mnt/runtime/write/emulated/0/Android/{data,media,obb}
+        EnsurePkgSpecificDirs(mntSource, packageNames, true, fail_fn);
 
         std::string sandboxSource = StringPrintf("%s/Android/sandbox/%s",
             mntSource.c_str(), sandboxId.c_str());
-        CreateDir(sandboxSource, 0755, uid, uid, fail_fn);
+        CreatePkgSandboxSource(sandboxSource, fail_fn);
         BindMount(sandboxSource, mntTarget, fail_fn);
 
-        // Create /storage/emulated/0/Android/{data,media,obb}
-        createPkgSpecificDirRoots(mntTarget, false, 0755, uid, uid, fail_fn);
+        // Ensure /storage/emulated/0/Android/{data,media,obb}
+        EnsurePkgSpecificDirs(mntTarget, packageNames, false, fail_fn);
         for (auto& package : packageNames) {
             MountPkgSpecificDir(mntSource, mntTarget, package, uid, "data", fail_fn);
             MountPkgSpecificDir(mntSource, mntTarget, package, uid, "media", fail_fn);
@@ -775,15 +844,14 @@
             userid_t user_id = multiuser_get_user_id(uid);
             std::string pkgSandboxDir =
                 StringPrintf("/mnt/user/%d/package/%s", user_id, package_name.c_str());
-            struct stat sb;
             bool sandboxAlreadyCreated = true;
-            if (TEMP_FAILURE_RETRY(lstat(pkgSandboxDir.c_str(), &sb)) == -1) {
+            if (TEMP_FAILURE_RETRY(access(pkgSandboxDir.c_str(), F_OK)) == -1) {
                 if (errno == ENOENT) {
                     ALOGD("Sandbox not yet created for %s", pkgSandboxDir.c_str());
                     sandboxAlreadyCreated = false;
-                    CreatePkgSandbox(uid, package_name, fail_fn);
+                    CreatePkgSandboxTarget(uid, package_name, fail_fn);
                 } else {
-                    fail_fn(CREATE_ERROR("Failed to lstat %s: %s",
+                    fail_fn(CREATE_ERROR("Failed to access %s: %s",
                                          pkgSandboxDir.c_str(), strerror(errno)));
                 }
             }
@@ -794,7 +862,7 @@
                                      pkgSandboxDir.c_str(), strerror(errno)));
             }
 
-            if (access("/storage/obb_mount", F_OK) == 0) {
+            if (TEMP_FAILURE_RETRY(access("/storage/obb_mount", F_OK)) == 0) {
                 if (mount_mode != MOUNT_EXTERNAL_INSTALLER) {
                     remove("/storage/obb_mount");
                 }
@@ -1384,8 +1452,8 @@
       RuntimeAbort(env, __LINE__, "Bad gids array");
     }
 
-    for (int gid_index = gids_num; --gids_num >= 0;) {
-      if (native_gid_proxy[gid_index] == AID_WAKELOCK) {
+    for (int gids_index = 0; gids_index < gids_num; ++gids_index) {
+      if (native_gid_proxy[gids_index] == AID_WAKELOCK) {
         gid_wakelock_found = true;
         break;
       }
diff --git a/core/res/res/values-night/themes_device_defaults.xml b/core/res/res/values-night/themes_device_defaults.xml
index 949c12e..0721f6f 100644
--- a/core/res/res/values-night/themes_device_defaults.xml
+++ b/core/res/res/values-night/themes_device_defaults.xml
@@ -65,4 +65,7 @@
     <style name="Theme.DeviceDefault.Dialog.AppError" parent="Theme.DeviceDefault.Dialog.Alert" />
 
     <style name="Theme.DeviceDefault.DayNight" parent="Theme.DeviceDefault" />
+
+    <style name="ThemeOverlay.DeviceDefault.Accent.DayNight"
+           parent="@style/ThemeOverlay.DeviceDefault.Accent" />
 </resources>
\ No newline at end of file
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index 46e14b4..224f54c 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -4349,14 +4349,18 @@
         <attr name="indeterminate" format="boolean" />
         <!-- Restricts to ONLY indeterminate mode (state-keeping progress mode will not work). -->
         <attr name="indeterminateOnly" format="boolean" />
-        <!-- Drawable used for the indeterminate mode. -->
+        <!-- Drawable used for the indeterminate mode. One that implements Animatable offers more
+             control over the animation.-->
         <attr name="indeterminateDrawable" format="reference" />
         <!-- Drawable used for the progress mode. -->
         <attr name="progressDrawable" format="reference" />
-        <!-- Duration of the indeterminate animation. -->
+        <!-- Duration of the indeterminate animation. Only affects the indeterminate animation
+             if the indeterminate Drawable does not implement
+             android.graphics.drawable.Animatable. -->
         <attr name="indeterminateDuration" format="integer" min="1" />
-        <!-- Defines how the indeterminate mode should behave when the progress
-        reaches max. -->
+        <!-- Defines how the indeterminate mode should behave when the progress reaches max. Only
+             affects the indeterminate animation if the indeterminate Drawable does not implement
+             android.graphics.drawable.Animatable. -->
         <attr name="indeterminateBehavior">
             <!-- Progress starts over from 0. -->
             <enum name="repeat" value="1" />
@@ -4367,6 +4371,9 @@
         <attr name="maxWidth" />
         <attr name="minHeight" format="dimension" />
         <attr name="maxHeight" />
+        <!-- Sets the acceleration curve for the indeterminate animation. Defaults to a linear
+             interpolation. Only affects the indeterminate animation if the indeterminate Drawable
+             does not implement android.graphics.drawable.Animatable.-->
         <attr name="interpolator" format="reference" />
         <!-- Timeout between frames of animation in milliseconds.
              {@deprecated Not used by the framework}. -->
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index dae2692..8dfd5e84 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -46,6 +46,8 @@
         <item><xliff:g id="id">@string/status_bar_cast</xliff:g></item>
         <item><xliff:g id="id">@string/status_bar_vpn</xliff:g></item>
         <item><xliff:g id="id">@string/status_bar_bluetooth</xliff:g></item>
+        <item><xliff:g id="id">@string/status_bar_camera</xliff:g></item>
+        <item><xliff:g id="id">@string/status_bar_microphone</xliff:g></item>
         <item><xliff:g id="id">@string/status_bar_location</xliff:g></item>
         <item><xliff:g id="id">@string/status_bar_mute</xliff:g></item>
         <item><xliff:g id="id">@string/status_bar_volume</xliff:g></item>
@@ -55,8 +57,6 @@
         <item><xliff:g id="id">@string/status_bar_hotspot</xliff:g></item>
         <item><xliff:g id="id">@string/status_bar_mobile</xliff:g></item>
         <item><xliff:g id="id">@string/status_bar_airplane</xliff:g></item>
-        <item><xliff:g id="id">@string/status_bar_microphone</xliff:g></item>
-        <item><xliff:g id="id">@string/status_bar_camera</xliff:g></item>
         <item><xliff:g id="id">@string/status_bar_battery</xliff:g></item>
     </string-array>
 
@@ -3911,19 +3911,19 @@
 
     <!-- See DisplayWhiteBalanceController.
          A float array containing a list of ambient color temperatures, in Kelvin. This array,
-         together with config_displayWhiteBalanceDisplayTemperatureValues, is used to generate a
+         together with config_displayWhiteBalanceDisplayColorTemperatures, is used to generate a
          lookup table used in DisplayWhiteBalanceController. This lookup table is used to map
          ambient color temperature readings to a target color temperature for the display.
          This table is optional. If used, this array must,
          1) Contain at least two entries
-         2) Be the same length as config_displayWhiteBalanceDisplayTemperatureValues. -->
-    <array name="config_displayWhiteBalanceAmbientTemperatureValues">
+         2) Be the same length as config_displayWhiteBalanceDisplayColorTemperatures. -->
+    <array name="config_displayWhiteBalanceAmbientColorTemperatures">
     </array>
 
     <!-- See DisplayWhiteBalanceController.
          An array containing a list of display color temperatures, in Kelvin. See
-         config_displayWhiteBalanceAmbientTemperatureValues for additional details.
+         config_displayWhiteBalanceAmbientColorTemperatures for additional details.
          The same restrictions apply to this array. -->
-    <array name="config_displayWhiteBalanceDisplayTemperatureValues">
+    <array name="config_displayWhiteBalanceDisplayColorTemperatures">
     </array>
 </resources>
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index 5e65605..3580dd4 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -2951,6 +2951,8 @@
     <public-group type="style" first-id="0x010302e2">
         <!-- @hide @SystemApi -->
         <public name="Theme.DeviceDefault.DocumentsUI" />
+        <public name="Theme.DeviceDefault.DayNight" />
+        <public name="ThemeOverlay.DeviceDefault.Accent.DayNight" />
     </public-group>
 
     <public-group type="id" first-id="0x01020046">
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 0cdf388..5948f29 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -3712,7 +3712,7 @@
     <string name="ext_media_browse_action">Explore</string>
 
     <!-- Notification action to transfer media [CHAR LIMIT=40] -->
-    <string name="ext_media_seamless_action">Seamless transfer</string>
+    <string name="ext_media_seamless_action">Switch output</string>
 
     <!-- Notification title when external media is missing [CHAR LIMIT=30] -->
     <string name="ext_media_missing_title"><xliff:g id="name" example="SD card">%s</xliff:g> missing</string>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 08fc36d..16aed90 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -3653,7 +3653,7 @@
   <java-symbol type="array" name="config_displayWhiteBalanceDecreaseThresholds" />
   <java-symbol type="dimen" name="config_displayWhiteBalanceLowLightAmbientBrightnessThreshold" />
   <java-symbol type="dimen" name="config_displayWhiteBalanceLowLightAmbientColorTemperature" />
-  <java-symbol type="array" name="config_displayWhiteBalanceAmbientTemperatureValues" />
-  <java-symbol type="array" name="config_displayWhiteBalanceDisplayTemperatureValues" />
+  <java-symbol type="array" name="config_displayWhiteBalanceAmbientColorTemperatures" />
+  <java-symbol type="array" name="config_displayWhiteBalanceDisplayColorTemperatures" />
   <java-symbol type="drawable" name="ic_action_open" />
 </resources>
diff --git a/core/res/res/values/themes_device_defaults.xml b/core/res/res/values/themes_device_defaults.xml
index 1603508..194c86c 100644
--- a/core/res/res/values/themes_device_defaults.xml
+++ b/core/res/res/values/themes_device_defaults.xml
@@ -1652,6 +1652,7 @@
 
     <style name="Theme.DeviceDefault.Settings.Dialog.NoActionBar" parent="Theme.DeviceDefault.Light.Dialog.NoActionBar" />
 
+    <!-- DeviceDefault theme for day/night activities. -->
     <style name="Theme.DeviceDefault.DayNight" parent="Theme.DeviceDefault.Light" />
 
     <!-- Theme used for the intent picker activity. -->
@@ -1697,6 +1698,10 @@
         <item name="colorAccent">@color/accent_device_default_light</item>
     </style>
 
+    <!-- Theme overlay that replaces colorAccent with the colorAccent from {@link #Theme_DeviceDefault_DayNight}. -->
+    <style name="ThemeOverlay.DeviceDefault.Accent.DayNight"
+           parent="@style/ThemeOverlay.DeviceDefault.Accent.Light" />
+
     <style name="ThemeOverlay.DeviceDefault.Dark.ActionBar.Accent" parent="ThemeOverlay.Material.Dark.ActionBar">
         <item name="colorAccent">@color/accent_device_default_dark</item>
     </style>
diff --git a/graphics/java/android/graphics/Bitmap.java b/graphics/java/android/graphics/Bitmap.java
index bdb6364..8135671 100644
--- a/graphics/java/android/graphics/Bitmap.java
+++ b/graphics/java/android/graphics/Bitmap.java
@@ -1509,6 +1509,9 @@
      * Convenience method that returns the width of this bitmap divided
      * by the density scale factor.
      *
+     * Returns the bitmap's width multiplied by the ratio of the target density to the bitmap's
+     * source density
+     *
      * @param targetDensity The density of the target canvas of the bitmap.
      * @return The scaled width of this bitmap, according to the density scale factor.
      */
@@ -1520,6 +1523,9 @@
      * Convenience method that returns the height of this bitmap divided
      * by the density scale factor.
      *
+     * Returns the bitmap's height multiplied by the ratio of the target density to the bitmap's
+     * source density
+     *
      * @param targetDensity The density of the target canvas of the bitmap.
      * @return The scaled height of this bitmap, according to the density scale factor.
      */
diff --git a/keystore/java/android/security/keystore/AndroidKeyStoreKeyGeneratorSpi.java b/keystore/java/android/security/keystore/AndroidKeyStoreKeyGeneratorSpi.java
index aa29174..3dc884e 100644
--- a/keystore/java/android/security/keystore/AndroidKeyStoreKeyGeneratorSpi.java
+++ b/keystore/java/android/security/keystore/AndroidKeyStoreKeyGeneratorSpi.java
@@ -17,7 +17,6 @@
 package android.security.keystore;
 
 import android.security.Credentials;
-import android.security.GateKeeper;
 import android.security.KeyStore;
 import android.security.keymaster.KeyCharacteristics;
 import android.security.keymaster.KeymasterArguments;
@@ -204,7 +203,12 @@
                         }
                     }
                 }
-
+                if (mKeymasterAlgorithm == KeymasterDefs.KM_ALGORITHM_3DES) {
+                    if (mKeySizeBits != 168) {
+                        throw new InvalidAlgorithmParameterException(
+                            "3DES key size must be 168 bits.");
+                    }
+                }
                 if (mKeymasterAlgorithm == KeymasterDefs.KM_ALGORITHM_HMAC) {
                     if (mKeySizeBits < 64) {
                         throw new InvalidAlgorithmParameterException(
diff --git a/libs/androidfw/include/androidfw/ResourceTypes.h b/libs/androidfw/include/androidfw/ResourceTypes.h
index 742813e..875b90b 100644
--- a/libs/androidfw/include/androidfw/ResourceTypes.h
+++ b/libs/androidfw/include/androidfw/ResourceTypes.h
@@ -1647,6 +1647,10 @@
     // The overlay must reside of the product partition or must have existed on the product
     // partition before an upgrade to overlay these resources.
     POLICY_PRODUCT_PARTITION = 0x00000008,
+
+    // The overlay must be signed with the same signature as the actor of the target resource,
+    // which can be separate or the same as the target package with the resource.
+    POLICY_SIGNATURE = 0x00000010,
   };
   uint32_t policy_flags;
 
diff --git a/libs/hwui/thread/ThreadBase.h b/libs/hwui/thread/ThreadBase.h
index 8cdcc46..0289d3f 100644
--- a/libs/hwui/thread/ThreadBase.h
+++ b/libs/hwui/thread/ThreadBase.h
@@ -68,10 +68,12 @@
     void processQueue() { mQueue.process(); }
 
     virtual bool threadLoop() override {
+        Looper::setForThread(mLooper);
         while (!exitPending()) {
             waitForWork();
             processQueue();
         }
+        Looper::setForThread(nullptr);
         return false;
     }
 
diff --git a/libs/incident/proto/android/os/header.proto b/libs/incident/proto/android/os/header.proto
index a84dc48..d463f87 100644
--- a/libs/incident/proto/android/os/header.proto
+++ b/libs/incident/proto/android/os/header.proto
@@ -37,4 +37,9 @@
       optional int64 id = 2; // The unique id of the statsd config.
     }
     optional StatsdConfigKey config_key = 3;
+
+    // Details about the trigger. com.android.os.AlertTriggerDetails
+    // Only use bytes type here to avoid indirect dependency on atoms.proto
+    // And this header passes through incidentd without incidentd parsing it.
+    optional bytes trigger_details = 4;
 }
diff --git a/location/java/android/location/ILocationManager.aidl b/location/java/android/location/ILocationManager.aidl
index 1aeefb8..57a0a72 100644
--- a/location/java/android/location/ILocationManager.aidl
+++ b/location/java/android/location/ILocationManager.aidl
@@ -114,6 +114,7 @@
     // for reporting callback completion
     void locationCallbackFinished(ILocationListener listener);
 
-    // used by gts tests to verify throttling whitelist
+    // used by gts tests to verify whitelists
     String[] getBackgroundThrottlingWhitelist();
+    String[] getIgnoreSettingsWhitelist();
 }
diff --git a/location/java/android/location/LocationManager.java b/location/java/android/location/LocationManager.java
index c027fd4..586ee2a 100644
--- a/location/java/android/location/LocationManager.java
+++ b/location/java/android/location/LocationManager.java
@@ -414,6 +414,18 @@
     }
 
     /**
+     * @hide
+     */
+    @TestApi
+    public String[] getIgnoreSettingsWhitelist() {
+        try {
+            return mService.getIgnoreSettingsWhitelist();
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
      * @hide - hide this constructor because it has a parameter
      * of type ILocationManager, which is a system private class. The
      * right way to create an instance of this class is using the
diff --git a/media/java/android/media/AudioRecord.java b/media/java/android/media/AudioRecord.java
index 92afe7e..24a3a9b 100644
--- a/media/java/android/media/AudioRecord.java
+++ b/media/java/android/media/AudioRecord.java
@@ -17,6 +17,7 @@
 package android.media;
 
 import android.annotation.CallbackExecutor;
+import android.annotation.FloatRange;
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -1713,12 +1714,11 @@
     /**
      * Specifies the logical microphone (for processing).
      *
-     * @param direction Direction constant (MicrophoneDirection.MIC_DIRECTION_*)
-     * @return retval OK if the call is successful, an error code otherwise.
-     * @hide
+     * @param direction Direction constant.
+     * @return true if sucessful.
      */
-    public int setMicrophoneDirection(int direction) {
-        return native_set_microphone_direction(direction);
+    public boolean setMicrophoneDirection(int direction) {
+        return native_set_microphone_direction(direction) == 0;
     }
 
     /**
@@ -1727,11 +1727,10 @@
      *
      * @param zoom the desired field dimension of microphone capture. Range is from -1 (wide angle),
      * though 0 (no zoom) to 1 (maximum zoom).
-     * @return retval OK if the call is successful, an error code otherwise.
-     * @hide
+     * @return true if sucessful.
      */
-    public int setMicrophoneFieldDimension(float zoom) {
-        return native_set_microphone_field_dimension(zoom);
+    public boolean setMicrophoneFieldDimension(@FloatRange(from = -1.0, to = 1.0) float zoom) {
+        return native_set_microphone_field_dimension(zoom) == 0;
     }
 
     //---------------------------------------------------------
diff --git a/media/java/android/media/MediaHTTPConnection.java b/media/java/android/media/MediaHTTPConnection.java
index ad25a06..5f324f7 100644
--- a/media/java/android/media/MediaHTTPConnection.java
+++ b/media/java/android/media/MediaHTTPConnection.java
@@ -16,6 +16,8 @@
 
 package android.media;
 
+import static android.media.MediaPlayer.MEDIA_ERROR_UNSUPPORTED;
+
 import android.annotation.UnsupportedAppUsage;
 import android.net.NetworkUtils;
 import android.os.IBinder;
@@ -23,21 +25,19 @@
 import android.util.Log;
 
 import java.io.BufferedInputStream;
-import java.io.InputStream;
 import java.io.IOException;
+import java.io.InputStream;
 import java.net.CookieHandler;
-import java.net.CookieManager;
-import java.net.Proxy;
-import java.net.URL;
 import java.net.HttpURLConnection;
 import java.net.MalformedURLException;
 import java.net.NoRouteToHostException;
 import java.net.ProtocolException;
+import java.net.Proxy;
+import java.net.URL;
 import java.net.UnknownServiceException;
 import java.util.HashMap;
 import java.util.Map;
-
-import static android.media.MediaPlayer.MEDIA_ERROR_UNSUPPORTED;
+import java.util.concurrent.atomic.AtomicBoolean;
 
 /** @hide */
 public class MediaHTTPConnection extends IMediaHTTPConnection.Stub {
@@ -67,6 +67,7 @@
     // from com.squareup.okhttp.internal.http
     private final static int HTTP_TEMP_REDIRECT = 307;
     private final static int MAX_REDIRECTS = 20;
+    private AtomicBoolean mIsConnected = new AtomicBoolean(false);
 
     @UnsupportedAppUsage
     public MediaHTTPConnection() {
@@ -90,6 +91,7 @@
             mAllowCrossDomainRedirect = true;
             mURL = new URL(uri);
             mHeaders = convertHeaderStringToMap(headers);
+            mIsConnected.set(true);
         } catch (MalformedURLException e) {
             return null;
         }
@@ -140,7 +142,14 @@
     @Override
     @UnsupportedAppUsage
     public void disconnect() {
-        teardownConnection();
+        if (mIsConnected.getAndSet(false)) {
+            (new Thread() {
+                @Override
+                public void run() {
+                    teardownConnection();
+                }
+            }).start();
+        }
         mHeaders = null;
         mURL = null;
     }
@@ -325,7 +334,14 @@
     @Override
     @UnsupportedAppUsage
     public int readAt(long offset, int size) {
-        return native_readAt(offset, size);
+        if (!mIsConnected.get()) {
+            return -1;
+        }
+        int result = native_readAt(offset, size);
+        if (!mIsConnected.get()) {
+            return -1;
+        }
+        return result;
     }
 
     private int readAt(long offset, byte[] data, int size) {
diff --git a/media/java/android/media/MicrophoneDirection.java b/media/java/android/media/MicrophoneDirection.java
index 99201c0..489e268 100644
--- a/media/java/android/media/MicrophoneDirection.java
+++ b/media/java/android/media/MicrophoneDirection.java
@@ -16,38 +16,46 @@
 
 package android.media;
 
-/**
- * @hide
- */
+import android.annotation.FloatRange;
+import android.annotation.IntDef;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
 public interface MicrophoneDirection {
     /**
-     * @hide
+     * Don't do any directionality processing of the activated microphone(s).
      */
     int MIC_DIRECTION_UNSPECIFIED = 0;
-
     /**
-     * @hide
+     * Optimize capture for audio coming from the screen-side of the device.
      */
     int MIC_DIRECTION_FRONT = 1;
-
     /**
-     * @hide
+     * Optimize capture for audio coming from the side of the device opposite the screen.
      */
     int MIC_DIRECTION_BACK = 2;
-
     /**
-     * @hide
+     * Optimize capture for audio coming from an off-device microphone.
      */
     int MIC_DIRECTION_EXTERNAL = 3;
 
+    /** @hide */
+    @IntDef({
+            MIC_DIRECTION_UNSPECIFIED,
+            MIC_DIRECTION_FRONT,
+            MIC_DIRECTION_BACK,
+            MIC_DIRECTION_EXTERNAL
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface Directionmode{};
     /**
      * Specifies the logical microphone (for processing).
      *
-     * @param direction Direction constant (MicrophoneDirection.MIC_DIRECTION_*)
-     * @return retval OK if the call is successful, an error code otherwise.
-     * @hide
+     * @param direction Direction constant.
+     * @return true if sucessful.
      */
-    int setMicrophoneDirection(int direction);
+    boolean setMicrophoneDirection(@Directionmode int direction);
 
     /**
      * Specifies the zoom factor (i.e. the field dimension) for the selected microphone
@@ -55,8 +63,7 @@
      *
      * @param zoom the desired field dimension of microphone capture. Range is from -1 (wide angle),
      * though 0 (no zoom) to 1 (maximum zoom).
-     * @return retval OK if the call is successful, an error code otherwise.
-     * @hide
+     * @return true if sucessful.
      */
-    int setMicrophoneFieldDimension(float zoom);
+    boolean setMicrophoneFieldDimension(@FloatRange(from = -1.0, to = 1.0) float zoom);
 }
diff --git a/packages/ExtServices/src/android/ext/services/notification/Assistant.java b/packages/ExtServices/src/android/ext/services/notification/Assistant.java
index c5e598d..31a3ff4 100644
--- a/packages/ExtServices/src/android/ext/services/notification/Assistant.java
+++ b/packages/ExtServices/src/android/ext/services/notification/Assistant.java
@@ -221,6 +221,12 @@
     }
 
     @Override
+    public Adjustment onNotificationEnqueued(StatusBarNotification sbn) {
+        // we use the version with channel, so this is never called.
+        return null;
+    }
+
+    @Override
     public Adjustment onNotificationEnqueued(StatusBarNotification sbn,
             NotificationChannel channel) {
         if (DEBUG) Log.i(TAG, "ENQUEUED " + sbn.getKey() + " on " + channel.getId());
diff --git a/packages/NetworkStack/src/com/android/server/NetworkStackService.java b/packages/NetworkStack/src/com/android/server/NetworkStackService.java
index c6a207f..90db207 100644
--- a/packages/NetworkStack/src/com/android/server/NetworkStackService.java
+++ b/packages/NetworkStack/src/com/android/server/NetworkStackService.java
@@ -247,6 +247,12 @@
         }
 
         @Override
+        public void notifyCaptivePortalAppFinished(int response) {
+            checkNetworkStackCallingPermission();
+            mNm.notifyCaptivePortalAppFinished(response);
+        }
+
+        @Override
         public void forceReevaluation(int uid) {
             checkNetworkStackCallingPermission();
             mNm.forceReevaluation(uid);
diff --git a/packages/NetworkStack/src/com/android/server/connectivity/NetworkMonitor.java b/packages/NetworkStack/src/com/android/server/connectivity/NetworkMonitor.java
index b9e901b..ec4a479 100644
--- a/packages/NetworkStack/src/com/android/server/connectivity/NetworkMonitor.java
+++ b/packages/NetworkStack/src/com/android/server/connectivity/NetworkMonitor.java
@@ -39,9 +39,7 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
-import android.net.CaptivePortal;
 import android.net.ConnectivityManager;
-import android.net.ICaptivePortal;
 import android.net.INetworkMonitor;
 import android.net.INetworkMonitorCallbacks;
 import android.net.LinkProperties;
@@ -460,6 +458,13 @@
         sendMessage(CMD_LAUNCH_CAPTIVE_PORTAL_APP);
     }
 
+    /**
+     * Notify that the captive portal app was closed with the provided response code.
+     */
+    public void notifyCaptivePortalAppFinished(int response) {
+        sendMessage(CMD_CAPTIVE_PORTAL_APP_FINISHED, response);
+    }
+
     @Override
     protected void log(String s) {
         if (DBG) Log.d(TAG + "/" + mNetwork.toString(), s);
@@ -671,29 +676,8 @@
                 case CMD_LAUNCH_CAPTIVE_PORTAL_APP:
                     final Bundle appExtras = new Bundle();
                     // OneAddressPerFamilyNetwork is not parcelable across processes.
-                    appExtras.putParcelable(
-                            ConnectivityManager.EXTRA_NETWORK, new Network(mNetwork));
-                    appExtras.putParcelable(ConnectivityManager.EXTRA_CAPTIVE_PORTAL,
-                            new CaptivePortal(new ICaptivePortal.Stub() {
-                                @Override
-                                public void appResponse(int response) {
-                                    if (response == APP_RETURN_WANTED_AS_IS) {
-                                        mContext.enforceCallingPermission(
-                                                PERMISSION_NETWORK_SETTINGS,
-                                                "CaptivePortal");
-                                    }
-                                    sendMessage(CMD_CAPTIVE_PORTAL_APP_FINISHED, response);
-                                }
-
-                                @Override
-                                public void logEvent(int eventId, String packageName)
-                                        throws RemoteException {
-                                    mContext.enforceCallingPermission(
-                                            PERMISSION_NETWORK_SETTINGS,
-                                            "CaptivePortal");
-                                    mCallback.logCaptivePortalLoginEvent(eventId, packageName);
-                                }
-                            }));
+                    final Network network = new Network(mNetwork);
+                    appExtras.putParcelable(ConnectivityManager.EXTRA_NETWORK, network);
                     final CaptivePortalProbeResult probeRes = mLastPortalProbeResult;
                     appExtras.putString(EXTRA_CAPTIVE_PORTAL_URL, probeRes.detectUrl);
                     if (probeRes.probeSpec != null) {
@@ -702,7 +686,7 @@
                     }
                     appExtras.putString(ConnectivityManager.EXTRA_CAPTIVE_PORTAL_USER_AGENT,
                             mCaptivePortalUserAgent);
-                    mCm.startCaptivePortalApp(appExtras);
+                    mCm.startCaptivePortalApp(network, appExtras);
                     return HANDLED;
                 default:
                     return NOT_HANDLED;
diff --git a/packages/NetworkStack/tests/Android.bp b/packages/NetworkStack/tests/Android.bp
index 4a09b3e..5c7b514 100644
--- a/packages/NetworkStack/tests/Android.bp
+++ b/packages/NetworkStack/tests/Android.bp
@@ -18,6 +18,7 @@
     name: "NetworkStackTests",
     certificate: "platform",
     srcs: ["src/**/*.java"],
+    test_suites: ["device-tests"],
     resource_dirs: ["res"],
     static_libs: [
         "android-support-test",
diff --git a/packages/NetworkStack/tests/src/android/net/apf/ApfTest.java b/packages/NetworkStack/tests/src/android/net/apf/ApfTest.java
index a4a1000..af71ac5 100644
--- a/packages/NetworkStack/tests/src/android/net/apf/ApfTest.java
+++ b/packages/NetworkStack/tests/src/android/net/apf/ApfTest.java
@@ -22,6 +22,7 @@
 import static android.system.OsConstants.ETH_P_IP;
 import static android.system.OsConstants.ETH_P_IPV6;
 import static android.system.OsConstants.IPPROTO_ICMPV6;
+import static android.system.OsConstants.IPPROTO_TCP;
 import static android.system.OsConstants.IPPROTO_UDP;
 import static android.system.OsConstants.SOCK_STREAM;
 
@@ -1017,6 +1018,7 @@
     private static final int IPV4_TCP_SEQ_NUM_OFFSET = IPV4_TCP_HEADER_OFFSET + 4;
     private static final int IPV4_TCP_ACK_NUM_OFFSET = IPV4_TCP_HEADER_OFFSET + 8;
     private static final int IPV4_TCP_HEADER_LENGTH_OFFSET = IPV4_TCP_HEADER_OFFSET + 12;
+    private static final int IPV4_TCP_HEADER_FLAG_OFFSET = IPV4_TCP_HEADER_OFFSET + 13;
     private static final byte[] IPV4_BROADCAST_ADDRESS =
             {(byte) 255, (byte) 255, (byte) 255, (byte) 255};
 
@@ -1568,7 +1570,7 @@
         // Verify IPv4 packet from another address is passed
         assertPass(program,
                 ipv4Packet(IPV4_ANOTHER_ADDR, IPV4_KEEPALIVE_SRC_ADDR, anotherSrcPort,
-                        anotherDstPort, anotherSeqNum, anotherAckNum));
+                        anotherDstPort, anotherSeqNum, anotherAckNum, 0 /* dataLength */));
 
         // Remove IPv4 keepalive filter
         apfFilter.removeKeepalivePacketFilter(slot1);
@@ -1613,15 +1615,15 @@
             // dst: 10.0.0.5, port: 12345
             assertDrop(program,
                     ipv4Packet(IPV4_KEEPALIVE_DST_ADDR, IPV4_KEEPALIVE_SRC_ADDR,
-                            dstPort, srcPort, ackNum, seqNum + 1));
+                            dstPort, srcPort, ackNum, seqNum + 1, 0 /* dataLength */));
             // Verify IPv4 non-keepalive ack packet from the same source address is passed
             assertPass(program,
                     ipv4Packet(IPV4_KEEPALIVE_DST_ADDR, IPV4_KEEPALIVE_SRC_ADDR,
-                            dstPort, srcPort, ackNum + 100, seqNum));
+                            dstPort, srcPort, ackNum + 100, seqNum, 0 /* dataLength */));
             // Verify IPv4 packet from another address is passed
             assertPass(program,
                     ipv4Packet(IPV4_ANOTHER_ADDR, IPV4_KEEPALIVE_SRC_ADDR, anotherSrcPort,
-                            anotherDstPort, anotherSeqNum, anotherAckNum));
+                            anotherDstPort, anotherSeqNum, anotherAckNum, 0 /* dataLength */));
 
             // Verify IPv6 keepalive ack packet is dropped
             // src: 2404:0:0:0:0:0:faf2, port: 54321
@@ -1650,13 +1652,13 @@
         // Verify IPv4, IPv6 packets are passed
         assertPass(program,
                 ipv4Packet(IPV4_KEEPALIVE_DST_ADDR, IPV4_KEEPALIVE_SRC_ADDR,
-                        dstPort, srcPort, ackNum, seqNum + 1));
+                        dstPort, srcPort, ackNum, seqNum + 1, 0 /* dataLength */));
         assertPass(program,
                 ipv6Packet(IPV6_KEEPALIVE_DST_ADDR, IPV6_KEEPALIVE_SRC_ADDR,
                         dstPort, srcPort, ackNum, seqNum + 1));
         assertPass(program,
                 ipv4Packet(IPV4_ANOTHER_ADDR, IPV4_KEEPALIVE_SRC_ADDR, srcPort,
-                        dstPort, anotherSeqNum, anotherAckNum));
+                        dstPort, anotherSeqNum, anotherAckNum, 0 /* dataLength */));
         assertPass(program,
                 ipv6Packet(IPV6_ANOTHER_ADDR, IPV6_KEEPALIVE_SRC_ADDR, srcPort,
                         dstPort, anotherSeqNum, anotherAckNum));
@@ -1664,28 +1666,30 @@
         apfFilter.shutdown();
     }
 
-    private static byte[] ipv4Packet(byte[] sip, byte[] tip, int sport,
-            int dport, int seq, int ack) {
-        ByteBuffer packet = ByteBuffer.wrap(new byte[100]);
+    private static byte[] ipv4Packet(byte[] sip, byte[] dip, int sport,
+            int dport, int seq, int ack, int dataLength) {
+        final int totalLength = dataLength + IPV4_HEADER_LEN + IPV4_TCP_HEADER_LEN;
+
+        ByteBuffer packet = ByteBuffer.wrap(new byte[totalLength + ETH_HEADER_LEN]);
+
+        // ether type
         packet.putShort(ETH_ETHERTYPE_OFFSET, (short) ETH_P_IP);
+
+        // IPv4 header
         packet.put(IPV4_VERSION_IHL_OFFSET, (byte) 0x45);
+        packet.putShort(IPV4_TOTAL_LENGTH_OFFSET, (short) totalLength);
+        packet.put(IPV4_PROTOCOL_OFFSET, (byte) IPPROTO_TCP);
         put(packet, IPV4_SRC_ADDR_OFFSET, sip);
-        put(packet, IPV4_DEST_ADDR_OFFSET, tip);
+        put(packet, IPV4_DEST_ADDR_OFFSET, dip);
         packet.putShort(IPV4_TCP_SRC_PORT_OFFSET, (short) sport);
         packet.putShort(IPV4_TCP_DEST_PORT_OFFSET, (short) dport);
         packet.putInt(IPV4_TCP_SEQ_NUM_OFFSET, seq);
         packet.putInt(IPV4_TCP_ACK_NUM_OFFSET, ack);
-        return packet.array();
-    }
 
-    private static byte[] ipv4Packet(byte[] sip, byte[] tip, int sport,
-            int dport, int seq, int ack, int dataLength) {
-        final int totalLength = dataLength + IPV4_HEADER_LEN + IPV4_TCP_HEADER_LEN;
-
-        ByteBuffer packet = ByteBuffer.wrap(ipv4Packet(sip, tip, sport, dport, seq, ack));
-        packet.putShort(IPV4_TOTAL_LENGTH_OFFSET, (short) totalLength);
-        // TCP header length 5, reserved 3 bits, NS=0
+        // TCP header length 5(20 bytes), reserved 3 bits, NS=0
         packet.put(IPV4_TCP_HEADER_LENGTH_OFFSET, (byte) 0x50);
+        // TCP flags: ACK set
+        packet.put(IPV4_TCP_HEADER_FLAG_OFFSET, (byte) 0x10);
         return packet.array();
     }
 
diff --git a/packages/NetworkStack/tests/src/com/android/server/connectivity/NetworkMonitorTest.java b/packages/NetworkStack/tests/src/com/android/server/connectivity/NetworkMonitorTest.java
index b98b0f7..9a16bb7 100644
--- a/packages/NetworkStack/tests/src/com/android/server/connectivity/NetworkMonitorTest.java
+++ b/packages/NetworkStack/tests/src/com/android/server/connectivity/NetworkMonitorTest.java
@@ -16,8 +16,7 @@
 
 package com.android.server.connectivity;
 
-import static android.net.ConnectivityManager.ACTION_CAPTIVE_PORTAL_SIGN_IN;
-import static android.net.ConnectivityManager.EXTRA_CAPTIVE_PORTAL;
+import static android.net.CaptivePortal.APP_RETURN_DISMISSED;
 import static android.net.INetworkMonitor.NETWORK_TEST_RESULT_INVALID;
 import static android.net.INetworkMonitor.NETWORK_TEST_RESULT_VALID;
 import static android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET;
@@ -41,8 +40,6 @@
 import static org.mockito.Mockito.when;
 
 import android.content.Context;
-import android.content.Intent;
-import android.net.CaptivePortal;
 import android.net.ConnectivityManager;
 import android.net.INetworkMonitorCallbacks;
 import android.net.InetAddresses;
@@ -54,10 +51,10 @@
 import android.net.metrics.IpConnectivityLog;
 import android.net.util.SharedLog;
 import android.net.wifi.WifiManager;
+import android.os.Bundle;
 import android.os.ConditionVariable;
 import android.os.Handler;
 import android.os.SystemClock;
-import android.os.UserHandle;
 import android.provider.Settings;
 import android.support.test.filters.SmallTest;
 import android.support.test.runner.AndroidJUnit4;
@@ -487,19 +484,23 @@
         // Check that startCaptivePortalApp sends the expected intent.
         nm.launchCaptivePortalApp();
 
-        final ArgumentCaptor<Intent> intentCaptor = ArgumentCaptor.forClass(Intent.class);
-        verify(mContext, timeout(HANDLER_TIMEOUT_MS).times(1))
-                .startActivityAsUser(intentCaptor.capture(), eq(UserHandle.CURRENT));
-        final Intent intent = intentCaptor.getValue();
-        assertEquals(ACTION_CAPTIVE_PORTAL_SIGN_IN, intent.getAction());
-        final Network network = intent.getParcelableExtra(ConnectivityManager.EXTRA_NETWORK);
-        assertEquals(TEST_NETID, network.netId);
+        final ArgumentCaptor<Bundle> bundleCaptor = ArgumentCaptor.forClass(Bundle.class);
+        final ArgumentCaptor<Network> networkCaptor = ArgumentCaptor.forClass(Network.class);
+        verify(mCm, timeout(HANDLER_TIMEOUT_MS).times(1))
+                .startCaptivePortalApp(networkCaptor.capture(), bundleCaptor.capture());
+        final Bundle bundle = bundleCaptor.getValue();
+        final Network bundleNetwork = bundle.getParcelable(ConnectivityManager.EXTRA_NETWORK);
+        assertEquals(TEST_NETID, bundleNetwork.netId);
+        // network is passed both in bundle and as parameter, as the bundle is opaque to the
+        // framework and only intended for the captive portal app, but the framework needs
+        // the network to identify the right NetworkMonitor.
+        assertEquals(TEST_NETID, networkCaptor.getValue().netId);
 
         // Have the app report that the captive portal is dismissed, and check that we revalidate.
         setStatus(mHttpsConnection, 204);
         setStatus(mHttpConnection, 204);
-        final CaptivePortal captivePortal = intent.getParcelableExtra(EXTRA_CAPTIVE_PORTAL);
-        captivePortal.reportCaptivePortalDismissed();
+
+        nm.notifyCaptivePortalAppFinished(APP_RETURN_DISMISSED);
         verify(mCallbacks, timeout(HANDLER_TIMEOUT_MS).times(1))
                 .notifyNetworkTested(NETWORK_TEST_RESULT_VALID, null);
     }
diff --git a/packages/SettingsLib/src/com/android/settingslib/SliceBroadcastRelay.java b/packages/SettingsLib/src/com/android/settingslib/SliceBroadcastRelay.java
index e92b36a..b7f7ad2 100644
--- a/packages/SettingsLib/src/com/android/settingslib/SliceBroadcastRelay.java
+++ b/packages/SettingsLib/src/com/android/settingslib/SliceBroadcastRelay.java
@@ -23,6 +23,10 @@
 import android.net.Uri;
 import android.os.Process;
 import android.os.UserHandle;
+import android.util.ArraySet;
+import android.util.Log;
+
+import java.util.Set;
 
 /**
  * Utility class that allows Settings to use SystemUI to relay broadcasts related to pinned slices.
@@ -38,12 +42,22 @@
     public static final String EXTRA_URI = "uri";
     public static final String EXTRA_RECEIVER = "receiver";
     public static final String EXTRA_FILTER = "filter";
+    private static final String TAG = "SliceBroadcastRelay";
 
-    public static void registerReceiver(Context context, Uri registerKey,
+    private static final Set<Uri> sRegisteredUris = new ArraySet<>();
+
+    /**
+     * Associate intent filter/sliceUri with corresponding receiver.
+     */
+    public static void registerReceiver(Context context, Uri sliceUri,
             Class<? extends BroadcastReceiver> receiver, IntentFilter filter) {
+
+        Log.d(TAG, "Registering Uri for broadcast relay: " + sliceUri);
+        sRegisteredUris.add(sliceUri);
+
         Intent registerBroadcast = new Intent(ACTION_REGISTER);
         registerBroadcast.setPackage(SYSTEMUI_PACKAGE);
-        registerBroadcast.putExtra(EXTRA_URI, ContentProvider.maybeAddUserId(registerKey,
+        registerBroadcast.putExtra(EXTRA_URI, ContentProvider.maybeAddUserId(sliceUri,
                 Process.myUserHandle().getIdentifier()));
         registerBroadcast.putExtra(EXTRA_RECEIVER,
                 new ComponentName(context.getPackageName(), receiver.getName()));
@@ -52,12 +66,21 @@
         context.sendBroadcastAsUser(registerBroadcast, UserHandle.SYSTEM);
     }
 
-    public static void unregisterReceivers(Context context, Uri registerKey) {
-        Intent registerBroadcast = new Intent(ACTION_UNREGISTER);
+    /**
+     * Unregisters all receivers for a given slice uri.
+     */
+
+    public static void unregisterReceivers(Context context, Uri sliceUri) {
+        if (!sRegisteredUris.contains(sliceUri)) {
+            return;
+        }
+        Log.d(TAG, "Unregistering uri broadcast relay: " + sliceUri);
+        final Intent registerBroadcast = new Intent(ACTION_UNREGISTER);
         registerBroadcast.setPackage(SYSTEMUI_PACKAGE);
-        registerBroadcast.putExtra(EXTRA_URI, ContentProvider.maybeAddUserId(registerKey,
+        registerBroadcast.putExtra(EXTRA_URI, ContentProvider.maybeAddUserId(sliceUri,
                 Process.myUserHandle().getIdentifier()));
 
         context.sendBroadcastAsUser(registerBroadcast, UserHandle.SYSTEM);
+        sRegisteredUris.remove(sliceUri);
     }
 }
diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java b/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java
index ac2c2c9..43affcd 100644
--- a/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java
+++ b/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java
@@ -1065,7 +1065,7 @@
     }
 
     public boolean isSaved() {
-        return networkId != WifiConfiguration.INVALID_NETWORK_ID;
+        return mConfig != null;
     }
 
     public Object getTag() {
diff --git a/packages/SystemUI/res/drawable/privacy_chip_bg.xml b/packages/SystemUI/res/drawable/privacy_chip_bg.xml
index f1158ef..b7b21fa 100644
--- a/packages/SystemUI/res/drawable/privacy_chip_bg.xml
+++ b/packages/SystemUI/res/drawable/privacy_chip_bg.xml
@@ -16,8 +16,8 @@
 -->
 
 <shape xmlns:android="http://schemas.android.com/apk/res/android">
-    <solid android:color="#4a4a4a" />
+    <solid android:color="#242424" /> <!-- 14% of white -->
     <padding android:paddingTop="@dimen/ongoing_appops_chip_bg_padding"
-        android:paddingBottom="@dimen/ongoing_appops_chip_bg_padding"/>
+        android:paddingBottom="@dimen/ongoing_appops_chip_bg_padding" />
     <corners android:radius="@dimen/ongoing_appops_chip_bg_corner_radius" />
 </shape>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/ongoing_privacy_chip.xml b/packages/SystemUI/res/layout/ongoing_privacy_chip.xml
index 58fe811..f64a64e6 100644
--- a/packages/SystemUI/res/layout/ongoing_privacy_chip.xml
+++ b/packages/SystemUI/res/layout/ongoing_privacy_chip.xml
@@ -15,6 +15,7 @@
      limitations under the License.
 -->
 
+
 <com.android.systemui.privacy.OngoingPrivacyChip
     xmlns:android="http://schemas.android.com/apk/res/android"
     android:id="@+id/privacy_chip"
@@ -22,47 +23,39 @@
     android:layout_width="wrap_content"
     android:layout_marginLeft="@dimen/ongoing_appops_chip_margin"
     android:layout_marginRight="@dimen/ongoing_appops_chip_margin"
-    android:layout_marginTop="@dimen/ongoing_appops_top_chip_margin"
-    android:layout_marginBottom="@dimen/ongoing_appops_top_chip_margin"
-    android:gravity="center_vertical|center_horizontal"
     android:layout_gravity="center_vertical|start"
+    android:gravity="center_vertical"
     android:orientation="horizontal"
-    android:paddingStart="@dimen/ongoing_appops_chip_side_padding"
-    android:paddingEnd="@dimen/ongoing_appops_chip_side_padding"
     android:focusable="true">
 
-        <TextView
-            android:id="@+id/in_use_text"
-            android:layout_height="match_parent"
-            android:layout_width="wrap_content"
-            android:layout_gravity="center_vertical|start"
-            android:layout_marginEnd="@dimen/ongoing_appops_chip_icon_margin_collapsed"
-            android:gravity="center_vertical"
-            android:textAppearance="@style/TextAppearance.StatusBar.Clock"
-            android:textColor="@color/status_bar_clock_color"
-            android:text="@string/ongoing_privacy_chip_in_use"
-            />
-
         <LinearLayout
-            android:id="@+id/icons_container"
-            android:layout_height="match_parent"
+            android:id="@+id/background"
+            android:layout_height="@dimen/ongoing_appops_chip_height"
             android:layout_width="wrap_content"
-            android:layout_gravity="center_vertical"
-            android:gravity="center_vertical"
-            />
+        >
+                <LinearLayout
+                    android:id="@+id/icons_container"
+                    android:layout_height="match_parent"
+                    android:layout_width="wrap_content"
+                    android:layout_marginStart="@dimen/ongoing_appops_chip_items_margin"
+                    android:layout_gravity="center_vertical"
+                    android:gravity="center_vertical"
+                    />
 
-        <TextView
-            android:id="@+id/text_container"
-            android:layout_height="match_parent"
-            android:layout_width="wrap_content"
-            android:singleLine="true"
-            android:ellipsize="end"
-            android:lines="1"
-            android:layout_gravity="center_vertical|end"
-            android:gravity="center_vertical"
-            android:textAppearance="@style/TextAppearance.StatusBar.Clock"
-            android:textColor="@color/status_bar_clock_color"
-            android:layout_marginStart="@dimen/ongoing_appops_chip_icon_margin_collapsed"
-            android:layout_marginEnd="@dimen/ongoing_appops_chip_icon_margin_collapsed"
-        />
+                <TextView
+                    android:id="@+id/text_container"
+                    android:layout_height="match_parent"
+                    android:layout_width="wrap_content"
+                    android:layout_gravity="center_vertical|end"
+                    android:paddingStart="@dimen/ongoing_appops_chip_text_padding"
+                    android:paddingEnd="@dimen/ongoing_appops_chip_text_padding"
+                    android:gravity="center_vertical"
+                    android:singleLine="true"
+                    android:ellipsize="end"
+                    android:lines="1"
+                    android:textAppearance="@style/TextAppearance.StatusBar.Clock"
+                    android:textSize="@dimen/ongoing_appops_chip_text_size"
+                    android:textColor="@color/status_bar_clock_color"
+                />
+          </LinearLayout>
 </com.android.systemui.privacy.OngoingPrivacyChip>
\ No newline at end of file
diff --git a/packages/SystemUI/res/values-land/dimens.xml b/packages/SystemUI/res/values-land/dimens.xml
index df858f0..bb0c6f6 100644
--- a/packages/SystemUI/res/values-land/dimens.xml
+++ b/packages/SystemUI/res/values-land/dimens.xml
@@ -34,5 +34,4 @@
     <bool name="quick_settings_wide">true</bool>
     <dimen name="qs_detail_margin_top">0dp</dimen>
     <dimen name="qs_paged_tile_layout_padding_bottom">0dp</dimen>
-    <dimen name="ongoing_appops_top_chip_margin">2dp</dimen>
 </resources>
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 1e1245f..1c7ee36 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -980,26 +980,32 @@
     <dimen name="ongoing_appops_dialog_items_bottom_margin">24dp</dimen>
     <!-- Top and bottom margin of title in Ongoing App Ops dialog -->
     <dimen name="ongoing_appops_dialog_title_margin_top_bottom">18dp</dimen>
-    <!-- Side margins around the Ongoing App Ops chip-->
-    <dimen name="ongoing_appops_chip_margin">12dp</dimen>
-    <!-- Top and bottom margins around the Ongoing App Ops chip -->
-    <dimen name="ongoing_appops_top_chip_margin">12dp</dimen>
-    <!-- Start and End padding for Ongoing App Ops chip -->
-    <dimen name="ongoing_appops_chip_side_padding">6dp</dimen>
-    <!-- Padding between background of Ongoing App Ops chip and content -->
-    <dimen name="ongoing_appops_chip_bg_padding">0dp</dimen>
-    <!-- Margin between icons of Ongoing App Ops chip when QQS-->
-    <dimen name="ongoing_appops_chip_icon_margin_collapsed">0dp</dimen>
-    <!-- Margin between icons of Ongoing App Ops chip when QS-->
-    <dimen name="ongoing_appops_chip_icon_margin_expanded">8dp</dimen>
-    <!-- Icon size of Ongoing App Ops chip -->
-    <dimen name="ongoing_appops_chip_icon_size">18dp</dimen>
-    <!-- Radius of Ongoing App Ops chip corners -->
-    <dimen name="ongoing_appops_chip_bg_corner_radius">4dp</dimen>
     <!-- Text size for Ongoing App Ops dialog title -->
     <dimen name="ongoing_appops_dialog_title_size">20sp</dimen>
     <!-- Text size for Ongoing App Ops dialog items -->
     <dimen name="ongoing_appops_dialog_item_size">16sp</dimen>
+    <!-- Side margins around the Ongoing App Ops chip-->
+    <dimen name="ongoing_appops_chip_margin">0dp</dimen>
+    <!-- Height of the Ongoing App Ops chip -->
+    <dimen name="ongoing_appops_chip_height">32dp</dimen>
+    <!-- Start and End padding for Ongoing App Ops chip -->
+    <dimen name="ongoing_appops_chip_text_padding">8dp</dimen>
+    <!-- Padding between background of Ongoing App Ops chip and content -->
+    <dimen name="ongoing_appops_chip_bg_padding">0dp</dimen>
+    <!-- Side padding between background of Ongoing App Ops chip and content -->
+    <dimen name="ongoing_appops_chip_side_padding">8dp</dimen>
+    <!-- Margin between icons of Ongoing App Ops chip when QQS-->
+    <dimen name="ongoing_appops_chip_icon_margin_collapsed">0dp</dimen>
+    <!-- Margin between icons of Ongoing App Ops chip when QS-->
+    <dimen name="ongoing_appops_chip_icon_margin_expanded">2dp</dimen>
+    <!-- Icon size of Ongoing App Ops chip -->
+    <dimen name="ongoing_appops_chip_icon_size">@*android:dimen/status_bar_icon_size</dimen>
+    <!-- Radius of Ongoing App Ops chip corners -->
+    <dimen name="ongoing_appops_chip_bg_corner_radius">16dp</dimen>
+    <!-- Size of text of Ongoing App Ops chip -->
+    <dimen name="ongoing_appops_chip_text_size">12sp</dimen>
+    <!-- Margin between items in Ongoing App Ops chip -->
+    <dimen name="ongoing_appops_chip_items_margin">8dp</dimen>
 
     <!-- How much a bubble is elevated -->
     <dimen name="bubble_elevation">8dp</dimen>
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index b4131d7..01595f0 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -1582,19 +1582,19 @@
     <!-- Notification Inline controls: continue receiving notifications prompt, channel level -->
     <string name="inline_keep_showing">Keep showing these notifications?</string>
 
-    <!-- Notification inline controls: block notifications button -->
+    <!-- Notification inline controls: block notifications button [CHAR_LIMIT=25] -->
     <string name="inline_stop_button">Stop notifications</string>
 
     <!-- Notification inline controls: button to deliver notifications silently from this channel [CHAR_LIMIT=35] -->
     <string name="inline_deliver_silently_button">Deliver Silently</string>
 
-    <!-- Notification inline controls: button to block notifications from this channel [CHAR_LIMIT=35] -->
+    <!-- Notification inline controls: button to block notifications from this channel [CHAR_LIMIT=20] -->
     <string name="inline_block_button">Block</string>
 
-    <!-- Notification inline controls: keep getting notifications button -->
+    <!-- Notification inline controls: keep getting notifications button [CHAR_LIMIT=25] -->
     <string name="inline_keep_button">Keep showing</string>
 
-    <!-- Notification inline controls: minimize notifications button -->
+    <!-- Notification inline controls: minimize notifications button [CHAR_LIMIT=20] -->
     <string name="inline_minimize_button">Minimize</string>
 
     <!-- Notification inline controls: button to show notifications silently, without alerting the user [CHAR_LIMIT=35] -->
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
index be2dd67..e62c77d 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
@@ -52,7 +52,7 @@
 import com.android.systemui.statusbar.notification.NotificationEntryManager;
 import com.android.systemui.statusbar.notification.NotificationInterruptionStateProvider;
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
-import com.android.systemui.statusbar.notification.row.NotificationInflater;
+import com.android.systemui.statusbar.notification.row.NotificationContentInflater.InflationFlag;
 import com.android.systemui.statusbar.phone.StatusBarWindowController;
 
 import java.util.List;
@@ -315,8 +315,7 @@
         }
 
         @Override
-        public void onEntryInflated(NotificationEntry entry,
-                @NotificationInflater.InflationFlag int inflatedFlags) {
+        public void onEntryInflated(NotificationEntry entry, @InflationFlag int inflatedFlags) {
             if (!areBubblesEnabled(mContext)) {
                 return;
             }
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
index 62cc889..ed9b38b 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
@@ -58,7 +58,7 @@
 /**
  * Renders bubbles in a stack and handles animating expanded and collapsed states.
  */
-public class BubbleStackView extends FrameLayout implements BubbleTouchHandler.FloatingView {
+public class BubbleStackView extends FrameLayout {
     private static final String TAG = "BubbleStackView";
 
     /**
@@ -146,8 +146,9 @@
 
         mBubbleData = data;
         mInflater = LayoutInflater.from(context);
-        mTouchHandler = new BubbleTouchHandler(context);
+        mTouchHandler = new BubbleTouchHandler(context, this);
         setOnTouchListener(mTouchHandler);
+        mInflater = LayoutInflater.from(context);
 
         Resources res = getResources();
         mBubbleSize = res.getDimensionPixelSize(R.dimen.individual_bubble_size);
@@ -199,6 +200,8 @@
                         .setDampingRatio(SpringForce.DAMPING_RATIO_LOW_BOUNCY));
 
         setClipChildren(false);
+
+        mBubbleContainer.bringToFront();
     }
 
     @Override
@@ -484,9 +487,10 @@
             if (shouldExpand) {
                 mBubbleContainer.setController(mExpandedAnimationController);
                 mExpandedAnimationController.expandFromStack(
-                                mStackAnimationController.getStackPosition(), () -> {
-                                updatePointerPosition();
-                                updateAfter.run();
+                        mStackAnimationController.getStackPosition(),
+                        () -> {
+                            updatePointerPosition();
+                            updateAfter.run();
                         });
             } else {
                 mBubbleContainer.cancelAllAnimations();
@@ -544,55 +548,49 @@
                 : null;
     }
 
-    @Override
-    public void setPosition(float x, float y) {
-        mStackAnimationController.moveFirstBubbleWithStackFollowing(x, y);
-    }
-
-    @Override
-    public void setPositionX(float x) {
-        // Unsupported, use setPosition(x, y).
-    }
-
-    @Override
-    public void setPositionY(float y) {
-        // Unsupported, use setPosition(x, y).
-    }
-
-    @Override
-    public PointF getPosition() {
+    public PointF getStackPosition() {
         return mStackAnimationController.getStackPosition();
     }
 
     /** Called when a drag operation on an individual bubble has started. */
-    public void onBubbleDragStart(BubbleView bubble) {
-        // TODO: Save position and snap back if not dismissed.
+    public void onBubbleDragStart(View bubble) {
+        mExpandedAnimationController.prepareForBubbleDrag(bubble);
     }
 
     /** Called with the coordinates to which an individual bubble has been dragged. */
-    public void onBubbleDragged(BubbleView bubble, float x, float y) {
-        bubble.setTranslationX(x);
-        bubble.setTranslationY(y);
+    public void onBubbleDragged(View bubble, float x, float y) {
+        if (!mIsExpanded || mIsAnimating) {
+            return;
+        }
+
+        mExpandedAnimationController.dragBubbleOut(bubble, x, y);
     }
 
     /** Called when a drag operation on an individual bubble has finished. */
-    public void onBubbleDragFinish(BubbleView bubble, float x, float y, float velX, float velY) {
-        // TODO: Add fling to bottom to dismiss.
+    public void onBubbleDragFinish(
+            View bubble, float x, float y, float velX, float velY, boolean dismissed) {
+        if (!mIsExpanded || mIsAnimating) {
+            return;
+        }
+
+        if (dismissed) {
+            mExpandedAnimationController.prepareForDismissalWithVelocity(bubble, velX, velY);
+        } else {
+            mExpandedAnimationController.snapBubbleBack(bubble, velX, velY);
+        }
     }
 
     void onDragStart() {
-        if (mIsExpanded) {
+        if (mIsExpanded || mIsAnimating) {
             return;
         }
 
         mStackAnimationController.cancelStackPositionAnimations();
         mBubbleContainer.setController(mStackAnimationController);
-        mIsAnimating = false;
     }
 
     void onDragged(float x, float y) {
-        // TODO: We can drag if animating - just need to reroute inflight anims to drag point.
-        if (mIsExpanded) {
+        if (mIsExpanded || mIsAnimating) {
             return;
         }
 
@@ -744,9 +742,9 @@
 
     private void updatePointerPosition() {
         if (mExpandedBubble != null) {
-            float pointerPosition = mExpandedBubble.iconView.getPosition().x
+            float pointerPosition = mExpandedBubble.iconView.getTranslationX()
                     + (mExpandedBubble.iconView.getWidth() / 2f);
-            mExpandedBubble.expandedView.setPointerPosition(pointerPosition);
+            mExpandedBubble.expandedView.setPointerPosition((int) pointerPosition);
         }
     }
 
@@ -772,7 +770,7 @@
      * @return the normalized x-axis position of the bubble stack rounded to 4 decimal places.
      */
     public float getNormalizedXPosition() {
-        return new BigDecimal(getPosition().x / mDisplaySize.x)
+        return new BigDecimal(getStackPosition().x / mDisplaySize.x)
                 .setScale(4, RoundingMode.CEILING.HALF_UP)
                 .floatValue();
     }
@@ -781,7 +779,7 @@
      * @return the normalized y-axis position of the bubble stack rounded to 4 decimal places.
      */
     public float getNormalizedYPosition() {
-        return new BigDecimal(getPosition().y / mDisplaySize.y)
+        return new BigDecimal(getStackPosition().y / mDisplaySize.y)
                 .setScale(4, RoundingMode.CEILING.HALF_UP)
                 .floatValue();
     }
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleTouchHandler.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleTouchHandler.java
index 7d3c0f8..165eb1d 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleTouchHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleTouchHandler.java
@@ -34,17 +34,16 @@
  * dismissing, and flings.
  */
 class BubbleTouchHandler implements View.OnTouchListener {
+    /** Velocity required to dismiss a bubble without dragging it into the dismiss target. */
+    private static final float DISMISS_MIN_VELOCITY = 4000f;
+
+    private final PointF mTouchDown = new PointF();
+    private final PointF mViewPositionOnTouchDown = new PointF();
+    private final BubbleStackView mStack;
 
     private BubbleController mController = Dependency.get(BubbleController.class);
     private PipDismissViewController mDismissViewController;
 
-    // The position of the bubble on down event
-    private float mBubbleDownPosX;
-    private float mBubbleDownPosY;
-    // The touch position on down event
-    private float mDownX = -1;
-    private float mDownY = -1;
-
     private boolean mMovedEnough;
     private int mTouchSlopSquared;
     private VelocityTracker mVelocityTracker;
@@ -58,65 +57,42 @@
         }
     };
 
-    // Bubble being dragged from the row of bubbles when the stack is expanded
-    private BubbleView mBubbleDraggingOut;
+    /** View that was initially touched, when we received the first ACTION_DOWN event. */
+    private View mTouchedView;
 
-    /**
-     * Views movable by this touch handler should implement this interface.
-     */
-    public interface FloatingView {
-
-        /**
-         * Sets the position of the view.
-         */
-        void setPosition(float x, float y);
-
-        /**
-         * Sets the x position of the view.
-         */
-        void setPositionX(float x);
-
-        /**
-         * Sets the y position of the view.
-         */
-        void setPositionY(float y);
-
-        /**
-         * @return the position of the view.
-         */
-        PointF getPosition();
-    }
-
-    public BubbleTouchHandler(Context context) {
+    BubbleTouchHandler(Context context, BubbleStackView stackView) {
         final int touchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
         mTouchSlopSquared = touchSlop * touchSlop;
         mDismissViewController = new PipDismissViewController(context);
+        mStack = stackView;
     }
 
     @Override
     public boolean onTouch(View v, MotionEvent event) {
-        int action = event.getActionMasked();
+        final int action = event.getActionMasked();
 
-        BubbleStackView stack = (BubbleStackView) v;
-        View targetView = mBubbleDraggingOut != null
-                ? mBubbleDraggingOut
-                : stack.getTargetView(event);
-        boolean isFloating = targetView instanceof FloatingView;
-        if (!isFloating || targetView == null || action == MotionEvent.ACTION_OUTSIDE) {
-            stack.collapseStack();
+        // If we aren't currently in the process of touching a view, figure out what we're touching.
+        // It'll be the stack, an individual bubble, or nothing.
+        if (mTouchedView == null) {
+            mTouchedView = mStack.getTargetView(event);
+        }
+
+        // If this is an ACTION_OUTSIDE event, or the stack reported that we aren't touching
+        // anything, collapse the stack.
+        if (action == MotionEvent.ACTION_OUTSIDE || mTouchedView == null) {
+            mStack.collapseStack();
             cleanUpDismissTarget();
-            resetTouches();
+            mTouchedView = null;
             return false;
         }
 
-        FloatingView floatingView = (FloatingView) targetView;
-        boolean isBubbleStack = floatingView instanceof BubbleStackView;
+        final boolean isStack = mStack.equals(mTouchedView);
+        final float rawX = event.getRawX();
+        final float rawY = event.getRawY();
 
-        PointF startPos = floatingView.getPosition();
-        float rawX = event.getRawX();
-        float rawY = event.getRawY();
-        float x = mBubbleDownPosX + rawX - mDownX;
-        float y = mBubbleDownPosY + rawY - mDownY;
+        // The coordinates of the touch event, in terms of the touched view's position.
+        final float viewX = mViewPositionOnTouchDown.x + rawX - mTouchDown.x;
+        final float viewY = mViewPositionOnTouchDown.y + rawY - mTouchDown.y;
         switch (action) {
             case MotionEvent.ACTION_DOWN:
                 trackMovement(event);
@@ -124,87 +100,83 @@
                 mDismissViewController.createDismissTarget();
                 mHandler.postDelayed(mShowDismissAffordance, SHOW_TARGET_DELAY);
 
-                mBubbleDownPosX = startPos.x;
-                mBubbleDownPosY = startPos.y;
-                mDownX = rawX;
-                mDownY = rawY;
-                mMovedEnough = false;
+                mTouchDown.set(rawX, rawY);
 
-                if (isBubbleStack) {
-                    stack.onDragStart();
+                if (isStack) {
+                    mViewPositionOnTouchDown.set(mStack.getStackPosition());
+                    mStack.onDragStart();
                 } else {
-                    stack.onBubbleDragStart((BubbleView) floatingView);
+                    mViewPositionOnTouchDown.set(
+                            mTouchedView.getTranslationX(), mTouchedView.getTranslationY());
+                    mStack.onBubbleDragStart(mTouchedView);
                 }
 
                 break;
-
             case MotionEvent.ACTION_MOVE:
                 trackMovement(event);
+                final float deltaX = rawX - mTouchDown.x;
+                final float deltaY = rawY - mTouchDown.y;
 
-                if (mBubbleDownPosX == -1 || mDownX == -1) {
-                    mBubbleDownPosX = startPos.x;
-                    mBubbleDownPosY = startPos.y;
-                    mDownX = rawX;
-                    mDownY = rawY;
-                }
-                final float deltaX = rawX - mDownX;
-                final float deltaY = rawY - mDownY;
                 if ((deltaX * deltaX) + (deltaY * deltaY) > mTouchSlopSquared && !mMovedEnough) {
                     mMovedEnough = true;
                 }
 
                 if (mMovedEnough) {
-                    if (floatingView instanceof BubbleView) {
-                        mBubbleDraggingOut = ((BubbleView) floatingView);
-                        stack.onBubbleDragged(mBubbleDraggingOut, x, y);
+                    if (isStack) {
+                        mStack.onDragged(viewX, viewY);
                     } else {
-                        stack.onDragged(x, y);
+                        mStack.onBubbleDragged(mTouchedView, viewX, viewY);
                     }
                 }
+
                 // TODO - when we're in the target stick to it / animate in some way?
                 mInDismissTarget = mDismissViewController.updateTarget(
-                        isBubbleStack ? stack.getBubbleAt(0) : (View) floatingView);
+                        isStack ? mStack.getBubbleAt(0) : mTouchedView);
                 break;
 
             case MotionEvent.ACTION_CANCEL:
-                resetTouches();
+                mTouchedView = null;
                 cleanUpDismissTarget();
                 break;
 
             case MotionEvent.ACTION_UP:
                 trackMovement(event);
-                if (mInDismissTarget) {
-                    if (isBubbleStack) {
-                        mController.dismissStack();
-                    } else {
-                        mController.removeBubble(((BubbleView) floatingView).getKey());
-                    }
+                if (mInDismissTarget && isStack) {
+                    mController.dismissStack();
                 } else if (mMovedEnough) {
                     mVelocityTracker.computeCurrentVelocity(1000);
                     final float velX = mVelocityTracker.getXVelocity();
                     final float velY = mVelocityTracker.getYVelocity();
-                    if (isBubbleStack) {
-                        stack.onDragFinish(x, y, velX, velY);
+                    if (isStack) {
+                        mStack.onDragFinish(viewX, viewY, velX, velY);
                     } else {
-                        stack.onBubbleDragFinish(mBubbleDraggingOut, x, y, velX, velY);
+                        final boolean dismissed = mInDismissTarget || velY > DISMISS_MIN_VELOCITY;
+                        mStack.onBubbleDragFinish(
+                                mTouchedView, viewX, viewY, velX, velY, /* dismissed */ dismissed);
+                        if (dismissed) {
+                            mController.removeBubble(((BubbleView) mTouchedView).getKey());
+                        }
                     }
-                } else if (floatingView.equals(stack.getExpandedBubbleView())) {
-                    stack.collapseStack();
-                } else if (isBubbleStack) {
-                    if (stack.isExpanded()) {
-                        stack.collapseStack();
+                } else if (mTouchedView.equals(mStack.getExpandedBubbleView())) {
+                    mStack.collapseStack();
+                } else if (isStack) {
+                    if (mStack.isExpanded()) {
+                        mStack.collapseStack();
                     } else {
-                        stack.expandStack();
+                        mStack.expandStack();
                     }
                 } else {
-                    stack.setExpandedBubble(((BubbleView) floatingView).getKey());
+                    mStack.setExpandedBubble(((BubbleView) mTouchedView).getKey());
                 }
+
                 cleanUpDismissTarget();
                 mVelocityTracker.recycle();
                 mVelocityTracker = null;
-                resetTouches();
+                mTouchedView = null;
+                mMovedEnough = false;
                 break;
         }
+
         return true;
     }
 
@@ -216,16 +188,6 @@
         mDismissViewController.destroyDismissTarget();
     }
 
-    /**
-     * Resets anything we care about after a gesture is complete.
-     */
-    private void resetTouches() {
-        mDownX = -1;
-        mDownY = -1;
-        mBubbleDownPosX = -1;
-        mBubbleDownPosY = -1;
-        mBubbleDraggingOut = null;
-    }
 
     private void trackMovement(MotionEvent event) {
         if (mVelocityTracker == null) {
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleView.java
index 1a4b1994..b409a31 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleView.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleView.java
@@ -20,7 +20,6 @@
 import android.app.Notification;
 import android.content.Context;
 import android.graphics.Color;
-import android.graphics.PointF;
 import android.graphics.drawable.ColorDrawable;
 import android.graphics.drawable.Drawable;
 import android.graphics.drawable.Icon;
@@ -39,7 +38,7 @@
 /**
  * A floating object on the screen that can post message updates.
  */
-public class BubbleView extends FrameLayout implements BubbleTouchHandler.FloatingView {
+public class BubbleView extends FrameLayout {
     private static final String TAG = "BubbleView";
 
     // Same value as Launcher3 badge code
@@ -217,25 +216,4 @@
         // XXX: should we pull from the drawable, app icon, notif tint?
         return ColorUtils.blendARGB(defaultTint, Color.WHITE, WHITE_SCRIM_ALPHA);
     }
-
-    @Override
-    public void setPosition(float x, float y) {
-        setPositionX(x);
-        setPositionY(y);
-    }
-
-    @Override
-    public void setPositionX(float x) {
-        setTranslationX(x);
-    }
-
-    @Override
-    public void setPositionY(float y) {
-        setTranslationY(y);
-    }
-
-    @Override
-    public PointF getPosition() {
-        return new PointF(getTranslationX(), getTranslationY());
-    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/animation/ExpandedAnimationController.java b/packages/SystemUI/src/com/android/systemui/bubbles/animation/ExpandedAnimationController.java
index 5b158e9..9fd26b8 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/animation/ExpandedAnimationController.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/animation/ExpandedAnimationController.java
@@ -44,6 +44,9 @@
      */
     private static final int ANIMATE_TRANSLATION_FACTOR = 4;
 
+    /** How much to scale down bubbles when they're animating in/out. */
+    private static final float ANIMATE_SCALE_PERCENT = 0.5f;
+
     /**
      * The stack position from which the bubbles were expanded. Saved in {@link #expandFromStack}
      * and used to return to stack form in {@link #collapseBackToStack}.
@@ -59,6 +62,22 @@
     /** Height of the status bar. */
     private float mStatusBarHeight;
 
+    /**
+     * Whether the individual bubble has been dragged out of the row of bubbles far enough to cause
+     * the rest of the bubbles to animate to fill the gap.
+     */
+    private boolean mBubbleDraggedOutEnough = false;
+
+    /** The bubble currently being dragged out of the row (to potentially be dismissed). */
+    private View mBubbleDraggingOut;
+
+    /**
+     * Drag velocities for the dragging-out bubble when the drag finished. These are used by
+     * {@link #onChildRemoved} to animate out the bubble while respecting touch velocity.
+     */
+    private float mBubbleDraggingOutVelX;
+    private float mBubbleDraggingOutVelY;
+
     @Override
     protected void setLayout(PhysicsAnimationLayout layout) {
         super.setLayout(layout);
@@ -102,6 +121,87 @@
         runAfterTranslationsEnd(after);
     }
 
+    /** Prepares the given bubble to be dragged out. */
+    public void prepareForBubbleDrag(View bubble) {
+        mLayout.cancelAnimationsOnView(bubble);
+
+        mBubbleDraggingOut = bubble;
+        mBubbleDraggingOut.setTranslationZ(Short.MAX_VALUE);
+    }
+
+    /**
+     * Drags an individual bubble to the given coordinates. Bubbles to the right will animate to
+     * take its place once it's dragged out of the row of bubbles, and animate out of the way if the
+     * bubble is dragged back into the row.
+     */
+    public void dragBubbleOut(View bubbleView, float x, float y) {
+        bubbleView.setTranslationX(x);
+        bubbleView.setTranslationY(y);
+
+        final boolean draggedOutEnough =
+                y > getExpandedY() + mBubbleSizePx || y < getExpandedY() - mBubbleSizePx;
+        if (draggedOutEnough != mBubbleDraggedOutEnough) {
+            animateStackByBubbleWidthsStartingFrom(
+                    /* numBubbleWidths */ draggedOutEnough ? -1 : 0,
+                    /* startIndex */ mLayout.indexOfChild(bubbleView) + 1);
+            mBubbleDraggedOutEnough = draggedOutEnough;
+        }
+    }
+
+    /**
+     * Snaps a bubble back to its position within the bubble row, and animates the rest of the
+     * bubbles to accommodate it if it was previously dragged out past the threshold.
+     */
+    public void snapBubbleBack(View bubbleView, float velX, float velY) {
+        final int index = mLayout.indexOfChild(bubbleView);
+
+        // Snap the bubble back, respecting its current velocity.
+        mLayout.animateValueForChildAtIndex(
+                DynamicAnimation.TRANSLATION_X, index, getXForChildAtIndex(index), velX);
+        mLayout.animateValueForChildAtIndex(
+                DynamicAnimation.TRANSLATION_Y, index, getExpandedY(), velY);
+        mLayout.setEndListenerForProperties(
+                mLayout.new OneTimeMultiplePropertyEndListener() {
+                    @Override
+                    void onAllAnimationsForPropertiesEnd() {
+                        // Reset Z translation once the bubble is done snapping back.
+                        bubbleView.setTranslationZ(0f);
+                    }
+                },
+                DynamicAnimation.TRANSLATION_X, DynamicAnimation.TRANSLATION_Y);
+
+        animateStackByBubbleWidthsStartingFrom(
+                /* numBubbleWidths */ 0, /* startIndex */ index + 1);
+
+        mBubbleDraggingOut = null;
+        mBubbleDraggedOutEnough = false;
+    }
+
+    /**
+     * Sets configuration variables so that when the given bubble is removed, the animations are
+     * started with the given velocities.
+     */
+    public void prepareForDismissalWithVelocity(View bubbleView, float velX, float velY) {
+        mBubbleDraggingOut = bubbleView;
+        mBubbleDraggingOutVelX = velX;
+        mBubbleDraggingOutVelY = velY;
+        mBubbleDraggedOutEnough = false;
+    }
+
+    /**
+     * Animates the bubbles, starting at the given index, to the left or right by the given number
+     * of bubble widths. Passing zero for numBubbleWidths will animate the bubbles to their normal
+     * positions.
+     */
+    private void animateStackByBubbleWidthsStartingFrom(int numBubbleWidths, int startIndex) {
+        for (int i = startIndex; i < mLayout.getChildCount(); i++) {
+            mLayout.animateValueForChildAtIndex(
+                    DynamicAnimation.TRANSLATION_X,
+                    i,
+                    getXForChildAtIndex(i + numBubbleWidths));
+        }
+    }
+
     /** The Y value of the row of expanded bubbles. */
     private float getExpandedY() {
         final WindowInsets insets = mLayout.getRootWindowInsets();
@@ -165,12 +265,7 @@
         child.setTranslationX(getXForChildAtIndex(index));
         child.setTranslationY(getExpandedY() - mBubbleSizePx * ANIMATE_TRANSLATION_FACTOR);
         mLayout.animateValueForChild(DynamicAnimation.TRANSLATION_Y, child, getExpandedY());
-
-        // Animate the remaining bubbles to the correct X position.
-        for (int i = index + 1; i < mLayout.getChildCount(); i++) {
-            mLayout.animateValueForChildAtIndex(
-                    DynamicAnimation.TRANSLATION_X, i, getXForChildAtIndex(i));
-        }
+        animateBubblesAfterIndexToCorrectX(index);
     }
 
     @Override
@@ -179,16 +274,36 @@
         // TODO: Reverse this when bubbles are at the bottom.
         mLayout.animateValueForChild(
                 DynamicAnimation.ALPHA, child, 0f, finishRemoval);
-        mLayout.animateValueForChild(
-                DynamicAnimation.TRANSLATION_Y,
-                child,
-                getExpandedY() - mBubbleSizePx * ANIMATE_TRANSLATION_FACTOR);
 
-        // Animate the remaining bubbles to the correct X position.
-        for (int i = index; i < mLayout.getChildCount(); i++) {
-            mLayout.animateValueForChildAtIndex(
-                    DynamicAnimation.TRANSLATION_X, i, getXForChildAtIndex(i));
+        // If we're removing the dragged-out bubble, that means it got dismissed.
+        if (child.equals(mBubbleDraggingOut)) {
+            // Throw it to the bottom of the screen, towards the center horizontally.
+            mLayout.animateValueForChild(
+                    DynamicAnimation.TRANSLATION_X,
+                    child,
+                    mLayout.getWidth() / 2f - mBubbleSizePx / 2f,
+                    mBubbleDraggingOutVelX);
+            mLayout.animateValueForChild(
+                    DynamicAnimation.TRANSLATION_Y,
+                    child,
+                    mLayout.getHeight() + mBubbleSizePx,
+                    mBubbleDraggingOutVelY);
+
+            // Scale it down a bit so it looks like it's disappearing.
+            mLayout.animateValueForChild(DynamicAnimation.SCALE_X, child, ANIMATE_SCALE_PERCENT);
+            mLayout.animateValueForChild(DynamicAnimation.SCALE_Y, child, ANIMATE_SCALE_PERCENT);
+
+            mBubbleDraggingOut = null;
+        } else {
+            // If we're removing some random bubble just throw it off the top.
+            mLayout.animateValueForChild(
+                    DynamicAnimation.TRANSLATION_Y,
+                    child,
+                    getExpandedY() - mBubbleSizePx * ANIMATE_TRANSLATION_FACTOR);
         }
+
+        // Animate all the other bubbles to their new positions sans this bubble.
+        animateBubblesAfterIndexToCorrectX(index);
     }
 
     @Override
@@ -207,6 +322,23 @@
                 () -> super.setChildVisibility(child, index, visibility));
     }
 
+    /**
+     * Animates the bubbles after the given index to the X position they should be in according to
+     * {@link #getXForChildAtIndex}.
+     */
+    private void animateBubblesAfterIndexToCorrectX(int start) {
+        for (int i = start; i < mLayout.getChildCount(); i++) {
+            final View bubble = mLayout.getChildAt(i);
+
+            // Don't animate the dragging out bubble, or it'll jump around while being dragged. It
+            // will be snapped to the correct X value after the drag (if it's not dismissed).
+            if (!bubble.equals(mBubbleDraggingOut)) {
+                mLayout.animateValueForChild(
+                        DynamicAnimation.TRANSLATION_X, bubble, getXForChildAtIndex(i));
+            }
+        }
+    }
+
     /** Returns the appropriate X translation value for a bubble at the given index. */
     private float getXForChildAtIndex(int index) {
         return mBubblePaddingPx + (mBubbleSizePx + mBubblePaddingPx) * index;
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/animation/PhysicsAnimationLayout.java b/packages/SystemUI/src/com/android/systemui/bubbles/animation/PhysicsAnimationLayout.java
index a4ddbf7..dfdcfc9 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/animation/PhysicsAnimationLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/animation/PhysicsAnimationLayout.java
@@ -187,6 +187,20 @@
     }
 
     /**
+     * Sets an end listener that will be called whenever any of the given properties' animations
+     * end. For example, setting a listener for TRANSLATION_X and TRANSLATION_Y will result in that
+     * listener being called twice - once when all TRANSLATION_X animations end, and again when all
+     * TRANSLATION_Y animations end.
+     */
+    public void setEndListenerForProperties(
+            DynamicAnimation.OnAnimationEndListener endListener,
+            DynamicAnimation.ViewProperty... properties) {
+        for (DynamicAnimation.ViewProperty property : properties) {
+            setEndListenerForProperty(endListener, property);
+        }
+    }
+
+    /**
      * Removes the end listener that would have been called when all child animations for a given
      * property stopped running.
      */
@@ -197,7 +211,6 @@
     @Override
     public void addView(View child, int index, ViewGroup.LayoutParams params) {
         super.addView(child, index, params);
-        setChildrenVisibility();
 
         // Set up animations for the new view, if the controller is set. If it isn't set, we'll be
         // setting up animations for all children when setController is called.
@@ -208,6 +221,8 @@
 
             mController.onChildAdded(child, index);
         }
+
+        setChildrenVisibility();
     }
 
     @Override
@@ -294,6 +309,13 @@
         }
     }
 
+    /** Cancels all of the physics animations running on the given view. */
+    public void cancelAnimationsOnView(View view) {
+        for (DynamicAnimation.ViewProperty property : mController.getAnimatedProperties()) {
+            getAnimationFromView(property, view).cancel();
+        }
+    }
+
     /**
      * Animates the property of the given child view, then runs the callback provided when the
      * animation ends.
@@ -318,6 +340,11 @@
                 });
             }
 
+            // Set the start velocity if it's something other than the not-set value.
+            if (startVel != Float.MAX_VALUE) {
+                animation.setStartVelocity(startVel);
+            }
+
             animation.animateToFinalPosition(value);
         }
     }
@@ -337,6 +364,14 @@
         animateValueForChild(property, view, value, Float.MAX_VALUE, /* after */ null);
     }
 
+    protected void animateValueForChild(
+            DynamicAnimation.ViewProperty property,
+            View view,
+            float value,
+            float startVel) {
+        animateValueForChild(property, view, value, startVel, /* after */ null);
+    }
+
     /**
      * Animates the property of the child at the given index to the given value, then runs the
      * callback provided when the animation ends.
@@ -414,7 +449,13 @@
      */
     private SpringAnimation getAnimationAtIndex(
             DynamicAnimation.ViewProperty property, int index) {
-        return (SpringAnimation) getChildAt(index).getTag(getTagIdForProperty(property));
+        return getAnimationFromView(property, getChildAt(index));
+    }
+
+    /** Retrieves the animation of the given property from the view via the view tag system. */
+    private SpringAnimation getAnimationFromView(
+            DynamicAnimation.ViewProperty property, View view) {
+        return (SpringAnimation) view.getTag(getTagIdForProperty(property));
     }
 
     /** Sets up SpringAnimations of the given property for each child view in the layout. */
@@ -528,4 +569,33 @@
             }
         }
     }
+
+    /**
+     * One time end listener that waits for every animation on every given property to finish. At
+     * that point, it calls {@link #onAllAnimationsForPropertiesEnd} and then removes itself as an
+     * end listener from each property.
+     */
+    public abstract class OneTimeMultiplePropertyEndListener
+            implements DynamicAnimation.OnAnimationEndListener {
+        final DynamicAnimation.ViewProperty[] mViewProperties;
+
+        OneTimeMultiplePropertyEndListener(DynamicAnimation.ViewProperty... properties) {
+            mViewProperties = properties;
+        }
+
+        @Override
+        public void onAnimationEnd(DynamicAnimation animation, boolean canceled, float value,
+                float velocity) {
+            if (!arePropertiesAnimating(mViewProperties)) {
+                onAllAnimationsForPropertiesEnd();
+
+                for (DynamicAnimation.ViewProperty property : mViewProperties) {
+                    removeEndListenerForProperty(property);
+                }
+            }
+        }
+
+        /** Called when every animation for every property has finished. */
+        abstract void onAllAnimationsForPropertiesEnd();
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/privacy/OngoingPrivacyChip.kt b/packages/SystemUI/src/com/android/systemui/privacy/OngoingPrivacyChip.kt
index 1765dc8..15dc43f 100644
--- a/packages/SystemUI/src/com/android/systemui/privacy/OngoingPrivacyChip.kt
+++ b/packages/SystemUI/src/com/android/systemui/privacy/OngoingPrivacyChip.kt
@@ -16,7 +16,6 @@
 
 import android.content.Context
 import android.util.AttributeSet
-import android.view.View
 import android.view.ViewGroup
 import android.widget.ImageView
 import android.widget.LinearLayout
@@ -40,10 +39,12 @@
             context.resources.getDimensionPixelSize(R.dimen.ongoing_appops_chip_icon_size)
     private val iconColor = context.resources.getColor(
             R.color.status_bar_clock_color, context.theme)
+    private val sidePadding =
+            context.resources.getDimensionPixelSize(R.dimen.ongoing_appops_chip_side_padding)
     private val backgroundDrawable = context.getDrawable(R.drawable.privacy_chip_bg)
     private lateinit var text: TextView
     private lateinit var iconsContainer: LinearLayout
-    private lateinit var inUseText: TextView
+    private lateinit var back: LinearLayout
     var expanded = false
         set(value) {
             if (value != field) {
@@ -64,15 +65,15 @@
     override fun onFinishInflate() {
         super.onFinishInflate()
 
-        inUseText = findViewById(R.id.in_use_text)
+        back = findViewById(R.id.background)
         text = findViewById(R.id.text_container)
         iconsContainer = findViewById(R.id.icons_container)
     }
 
     // Should only be called if the builder icons or app changed
     private fun updateView() {
-        inUseText.visibility = if (expanded) View.GONE else View.VISIBLE
-        background = if (expanded) backgroundDrawable else null
+        back.background = if (expanded) backgroundDrawable else null
+        back.setPaddingRelative(0, 0, if (expanded) sidePadding else 0, 0)
         fun setIcons(dialogBuilder: PrivacyDialogBuilder, iconsContainer: ViewGroup) {
             iconsContainer.removeAllViews()
             dialogBuilder.generateIcons().forEachIndexed { i, it ->
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
index c0f87cb..6a8c19a 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
@@ -81,6 +81,7 @@
 import com.android.systemui.statusbar.policy.NextAlarmController;
 import com.android.systemui.statusbar.policy.ZenModeController;
 
+import java.util.ArrayList;
 import java.util.List;
 import java.util.Locale;
 import java.util.Objects;
@@ -200,6 +201,8 @@
         mSystemIconsView = findViewById(R.id.quick_status_bar_system_icons);
         mQuickQsStatusIcons = findViewById(R.id.quick_qs_status_icons);
         StatusIconContainer iconContainer = findViewById(R.id.statusIcons);
+        // Ignore privacy icons because they show in the space above QQS
+        iconContainer.addIgnoredSlots(getIgnoredIconSlots());
         iconContainer.setShouldRestrictIcons(false);
         mIconManager = new TintedIconManager(iconContainer);
 
@@ -241,6 +244,18 @@
         updateShowPercent();
     }
 
+    private List<String> getIgnoredIconSlots() {
+        ArrayList<String> ignored = new ArrayList<>();
+        ignored.add(mContext.getResources().getString(
+                com.android.internal.R.string.status_bar_camera));
+        ignored.add(mContext.getResources().getString(
+                com.android.internal.R.string.status_bar_microphone));
+        ignored.add(mContext.getResources().getString(
+                com.android.internal.R.string.status_bar_location));
+
+        return ignored;
+    }
+
     private void updateStatusText() {
         boolean changed = updateRingerStatus() || updateAlarmStatus();
 
@@ -372,15 +387,6 @@
 
         setLayoutParams(lp);
 
-        if (mPrivacyChip != null) {
-            MarginLayoutParams lm = (MarginLayoutParams) mPrivacyChip.getLayoutParams();
-            int sideMargins = lm.leftMargin;
-            int topBottomMargins = resources.getDimensionPixelSize(
-                    R.dimen.ongoing_appops_top_chip_margin);
-            lm.setMargins(sideMargins, topBottomMargins, sideMargins, topBottomMargins);
-            mPrivacyChip.setLayoutParams(lm);
-        }
-
         updateStatusIconAlphaAnimator();
         updateHeaderTextContainerAlphaAnimator();
         updatePrivacyChipAlphaAnimator();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/AlertingNotificationManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/AlertingNotificationManager.java
index 2f19630..a6af82a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/AlertingNotificationManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/AlertingNotificationManager.java
@@ -28,7 +28,7 @@
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
-import com.android.systemui.statusbar.notification.row.NotificationInflater.InflationFlag;
+import com.android.systemui.statusbar.notification.row.NotificationContentInflater.InflationFlag;
 
 import java.util.stream.Stream;
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/AmbientPulseManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/AmbientPulseManager.java
index a3beb96..57d0588 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/AmbientPulseManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/AmbientPulseManager.java
@@ -16,7 +16,7 @@
 
 package com.android.systemui.statusbar;
 
-import static com.android.systemui.statusbar.notification.row.NotificationInflater.FLAG_CONTENT_VIEW_AMBIENT;
+import static com.android.systemui.statusbar.notification.row.NotificationContentInflater.FLAG_CONTENT_VIEW_AMBIENT;
 
 import android.annotation.NonNull;
 import android.content.Context;
@@ -26,7 +26,7 @@
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.systemui.R;
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
-import com.android.systemui.statusbar.notification.row.NotificationInflater.InflationFlag;
+import com.android.systemui.statusbar.notification.row.NotificationContentInflater.InflationFlag;
 
 import javax.inject.Inject;
 import javax.inject.Singleton;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/MediaArtworkProcessor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/MediaArtworkProcessor.kt
index 4944732..52b8cc2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/MediaArtworkProcessor.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/MediaArtworkProcessor.kt
@@ -49,7 +49,7 @@
 
         context.display.getSize(mTmpSize)
         val renderScript = RenderScript.create(context)
-        val rect = Rect(0, 0,artwork.width, artwork.height)
+        val rect = Rect(0, 0, artwork.width, artwork.height)
         MathUtils.fitRect(rect, Math.max(mTmpSize.x / DOWNSAMPLE, mTmpSize.y / DOWNSAMPLE))
         val inBitmap = Bitmap.createScaledBitmap(artwork, rect.width(), rect.height(),
                 true /* filter */)
@@ -67,6 +67,7 @@
         input.destroy()
         output.destroy()
         inBitmap.recycle()
+        blur.destroy()
 
         val canvas = Canvas(outBitmap)
         canvas.drawColor(ColorUtils.setAlphaComponent(color, COLOR_ALPHA))
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationAlertingManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationAlertingManager.java
index 5605f3d..f6d3cdf 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationAlertingManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationAlertingManager.java
@@ -17,8 +17,8 @@
 package com.android.systemui.statusbar.notification;
 
 import static com.android.systemui.statusbar.NotificationRemoteInputManager.FORCE_REMOTE_INPUT_HISTORY;
-import static com.android.systemui.statusbar.notification.row.NotificationInflater.FLAG_CONTENT_VIEW_AMBIENT;
-import static com.android.systemui.statusbar.notification.row.NotificationInflater.FLAG_CONTENT_VIEW_HEADS_UP;
+import static com.android.systemui.statusbar.notification.row.NotificationContentInflater.FLAG_CONTENT_VIEW_AMBIENT;
+import static com.android.systemui.statusbar.notification.row.NotificationContentInflater.FLAG_CONTENT_VIEW_HEADS_UP;
 
 import android.app.Notification;
 import android.service.notification.StatusBarNotification;
@@ -30,7 +30,7 @@
 import com.android.systemui.statusbar.NotificationListener;
 import com.android.systemui.statusbar.NotificationRemoteInputManager;
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
-import com.android.systemui.statusbar.notification.row.NotificationInflater;
+import com.android.systemui.statusbar.notification.row.NotificationContentInflater.InflationFlag;
 import com.android.systemui.statusbar.phone.ShadeController;
 import com.android.systemui.statusbar.policy.HeadsUpManager;
 
@@ -102,8 +102,7 @@
      * @param entry         entry to add
      * @param inflatedFlags flags representing content views that were inflated
      */
-    private void showAlertingView(NotificationEntry entry,
-            @NotificationInflater.InflationFlag int inflatedFlags) {
+    private void showAlertingView(NotificationEntry entry, @InflationFlag int inflatedFlags) {
         if ((inflatedFlags & FLAG_CONTENT_VIEW_HEADS_UP) != 0) {
             // Possible for shouldHeadsUp to change between the inflation starting and ending.
             // If it does and we no longer need to heads up, we should free the view.
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryListener.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryListener.java
index 1ed671f..a5a6d87 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryListener.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryListener.java
@@ -20,7 +20,7 @@
 
 import com.android.internal.statusbar.NotificationVisibility;
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
-import com.android.systemui.statusbar.notification.row.NotificationInflater;
+import com.android.systemui.statusbar.notification.row.NotificationContentInflater.InflationFlag;
 
 /**
  * Listener interface for changes sent by NotificationEntryManager.
@@ -65,8 +65,7 @@
     /**
      * Called when a notification's views are inflated for the first time.
      */
-    default void onEntryInflated(NotificationEntry entry,
-            @NotificationInflater.InflationFlag int inflatedFlags) {
+    default void onEntryInflated(NotificationEntry entry, @InflationFlag int inflatedFlags) {
     }
 
     /**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
index 56922be..3fbc641 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
@@ -36,8 +36,8 @@
 import com.android.systemui.statusbar.notification.collection.NotificationData.KeyguardEnvironment;
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.notification.logging.NotificationLogger;
-import com.android.systemui.statusbar.notification.row.NotificationInflater;
-import com.android.systemui.statusbar.notification.row.NotificationInflater.InflationFlag;
+import com.android.systemui.statusbar.notification.row.NotificationContentInflater;
+import com.android.systemui.statusbar.notification.row.NotificationContentInflater.InflationFlag;
 import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
 import com.android.systemui.statusbar.policy.HeadsUpManager;
 import com.android.systemui.util.leak.LeakDetector;
@@ -56,7 +56,7 @@
  */
 public class NotificationEntryManager implements
         Dumpable,
-        NotificationInflater.InflationCallback,
+        NotificationContentInflater.InflationCallback,
         NotificationUpdateHandler,
         VisualStabilityManager.Callback {
     private static final String TAG = "NotificationEntryMgr";
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationRowBinder.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationRowBinder.java
index f1bb0d7..6f5baf9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationRowBinder.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationRowBinder.java
@@ -19,8 +19,8 @@
 import static com.android.internal.util.Preconditions.checkNotNull;
 import static com.android.systemui.Dependency.ALLOW_NOTIFICATION_LONG_PRESS_NAME;
 import static com.android.systemui.statusbar.NotificationRemoteInputManager.ENABLE_REMOTE_INPUT;
-import static com.android.systemui.statusbar.notification.row.NotificationInflater.FLAG_CONTENT_VIEW_AMBIENT;
-import static com.android.systemui.statusbar.notification.row.NotificationInflater.FLAG_CONTENT_VIEW_HEADS_UP;
+import static com.android.systemui.statusbar.notification.row.NotificationContentInflater.FLAG_CONTENT_VIEW_AMBIENT;
+import static com.android.systemui.statusbar.notification.row.NotificationContentInflater.FLAG_CONTENT_VIEW_HEADS_UP;
 
 import android.annotation.Nullable;
 import android.content.Context;
@@ -42,8 +42,8 @@
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.notification.logging.NotificationLogger;
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
+import com.android.systemui.statusbar.notification.row.NotificationContentInflater;
 import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
-import com.android.systemui.statusbar.notification.row.NotificationInflater;
 import com.android.systemui.statusbar.notification.row.RowInflaterTask;
 import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
 import com.android.systemui.statusbar.phone.NotificationGroupManager;
@@ -78,7 +78,7 @@
     private NotificationPresenter mPresenter;
     private NotificationListContainer mListContainer;
     private HeadsUpManager mHeadsUpManager;
-    private NotificationInflater.InflationCallback mInflationCallback;
+    private NotificationContentInflater.InflationCallback mInflationCallback;
     private ExpandableNotificationRow.OnAppOpsClickListener mOnAppOpsClickListener;
     private BindRowCallback mBindRowCallback;
     private NotificationClicker mNotificationClicker;
@@ -105,7 +105,7 @@
     public void setUpWithPresenter(NotificationPresenter presenter,
             NotificationListContainer listContainer,
             HeadsUpManager headsUpManager,
-            NotificationInflater.InflationCallback inflationCallback,
+            NotificationContentInflater.InflationCallback inflationCallback,
             BindRowCallback bindRowCallback) {
         mPresenter = presenter;
         mListContainer = listContainer;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java
index f6d4ce22..3bf4d4b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java
@@ -54,8 +54,8 @@
 import com.android.systemui.statusbar.StatusBarIconView;
 import com.android.systemui.statusbar.notification.InflationException;
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
+import com.android.systemui.statusbar.notification.row.NotificationContentInflater.InflationFlag;
 import com.android.systemui.statusbar.notification.row.NotificationGuts;
-import com.android.systemui.statusbar.notification.row.NotificationInflater;
 
 import java.util.ArrayList;
 import java.util.Collections;
@@ -515,7 +515,7 @@
         if (row != null) row.resetUserExpansion();
     }
 
-    public void freeContentViewWhenSafe(@NotificationInflater.InflationFlag int inflationFlag) {
+    public void freeContentViewWhenSafe(@InflationFlag int inflationFlag) {
         if (row != null) row.freeContentViewWhenSafe(inflationFlag);
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
index 69828c1..2b643d0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
@@ -17,13 +17,13 @@
 package com.android.systemui.statusbar.notification.row;
 
 import static com.android.systemui.statusbar.notification.ActivityLaunchAnimator.ExpandAnimationParameters;
+import static com.android.systemui.statusbar.notification.row.NotificationContentInflater.FLAG_CONTENT_VIEW_AMBIENT;
+import static com.android.systemui.statusbar.notification.row.NotificationContentInflater.FLAG_CONTENT_VIEW_HEADS_UP;
+import static com.android.systemui.statusbar.notification.row.NotificationContentInflater.FLAG_CONTENT_VIEW_PUBLIC;
+import static com.android.systemui.statusbar.notification.row.NotificationContentInflater.InflationCallback;
 import static com.android.systemui.statusbar.notification.row.NotificationContentView.VISIBLE_TYPE_AMBIENT;
 import static com.android.systemui.statusbar.notification.row.NotificationContentView.VISIBLE_TYPE_CONTRACTED;
 import static com.android.systemui.statusbar.notification.row.NotificationContentView.VISIBLE_TYPE_HEADSUP;
-import static com.android.systemui.statusbar.notification.row.NotificationInflater.FLAG_CONTENT_VIEW_AMBIENT;
-import static com.android.systemui.statusbar.notification.row.NotificationInflater.FLAG_CONTENT_VIEW_HEADS_UP;
-import static com.android.systemui.statusbar.notification.row.NotificationInflater.FLAG_CONTENT_VIEW_PUBLIC;
-import static com.android.systemui.statusbar.notification.row.NotificationInflater.InflationCallback;
 
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
@@ -89,7 +89,7 @@
 import com.android.systemui.statusbar.notification.VisualStabilityManager;
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.notification.logging.NotificationCounters;
-import com.android.systemui.statusbar.notification.row.NotificationInflater.InflationFlag;
+import com.android.systemui.statusbar.notification.row.NotificationContentInflater.InflationFlag;
 import com.android.systemui.statusbar.notification.row.wrapper.NotificationMediaTemplateViewWrapper;
 import com.android.systemui.statusbar.notification.row.wrapper.NotificationViewWrapper;
 import com.android.systemui.statusbar.notification.stack.AmbientState;
@@ -134,7 +134,7 @@
     }
 
     private LayoutListener mLayoutListener;
-    private final NotificationInflater mNotificationInflater;
+    private final NotificationContentInflater mNotificationInflater;
     private int mIconTransformContentShift;
     private int mIconTransformContentShiftNoIcon;
     private int mMaxHeadsUpHeightBeforeN;
@@ -1616,7 +1616,7 @@
     }
 
     @VisibleForTesting
-    public NotificationInflater getNotificationInflater() {
+    public NotificationContentInflater getNotificationInflater() {
         return mNotificationInflater;
     }
 
@@ -1631,7 +1631,7 @@
     public ExpandableNotificationRow(Context context, AttributeSet attrs) {
         super(context, attrs);
         mFalsingManager = FalsingManager.getInstance(context);
-        mNotificationInflater = new NotificationInflater(this);
+        mNotificationInflater = new NotificationContentInflater(this);
         mMenuRow = new NotificationMenuRow(mContext);
         mImageResolver = new NotificationInlineImageResolver(context,
                 new NotificationInlineImageCache());
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInflater.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java
similarity index 98%
rename from packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInflater.java
rename to packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java
index 42ebfce..b34907d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInflater.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java
@@ -55,9 +55,9 @@
 /**
  * A utility that inflates the right kind of contentView based on the state
  */
-public class NotificationInflater {
+public class NotificationContentInflater {
 
-    public static final String TAG = "NotificationInflater";
+    public static final String TAG = "NotifContentInflater";
 
     @Retention(RetentionPolicy.SOURCE)
     @IntDef(flag = true,
@@ -127,7 +127,7 @@
     private boolean mRedactAmbient;
     private final ArrayMap<Integer, RemoteViews> mCachedContentViews = new ArrayMap<>();
 
-    public NotificationInflater(ExpandableNotificationRow row) {
+    public NotificationContentInflater(ExpandableNotificationRow row) {
         mRow = row;
     }
 
@@ -232,8 +232,7 @@
      * will reinflate it.
      *
      * @param reInflateFlags flags which views should be inflated. Should be a subset of
-     *                       {@link NotificationInflater#mInflationFlags} as only those will be
-     *                       inflated/reinflated.
+     *                       {@link #mInflationFlags} as only those will be inflated/reinflated.
      */
     private void inflateNotificationViews(@InflationFlag int reInflateFlags) {
         if (mRow.isRemoved()) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInlineImageCache.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInlineImageCache.java
index a5411ec..6eb376b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInlineImageCache.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInlineImageCache.java
@@ -52,7 +52,7 @@
     @Override
     public void preload(Uri uri) {
         PreloadImageTask newTask = new PreloadImageTask(mResolver);
-        newTask.executeOnExecutor(NotificationInflater.EXECUTOR, uri);
+        newTask.executeOnExecutor(NotificationContentInflater.EXECUTOR, uri);
         mCache.put(uri, newTask);
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationViewWrapper.java
index 4c06ff6..3808702 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationViewWrapper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationViewWrapper.java
@@ -26,12 +26,15 @@
 import android.graphics.Paint;
 import android.graphics.drawable.ColorDrawable;
 import android.graphics.drawable.Drawable;
+import android.os.Build;
 import android.view.NotificationHeaderView;
 import android.view.View;
 import android.view.ViewGroup;
 import android.widget.TextView;
 
+import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.graphics.ColorUtils;
+import com.android.internal.util.ContrastColorUtil;
 import com.android.systemui.statusbar.CrossFadeHelper;
 import com.android.systemui.statusbar.TransformableView;
 import com.android.systemui.statusbar.notification.TransformState;
@@ -108,6 +111,11 @@
             return false;
         }
 
+        // Apps targeting Q should fix their dark mode bugs.
+        if (mRow.getEntry().targetSdk >= Build.VERSION_CODES.Q) {
+            return false;
+        }
+
         int background = getBackgroundColor(view);
         if (background == Color.TRANSPARENT) {
             background = defaultBackgroundColor;
@@ -138,17 +146,19 @@
         }
     }
 
-    private boolean childrenNeedInversion(@ColorInt int parentBackground, ViewGroup viewGroup) {
+    @VisibleForTesting
+    boolean childrenNeedInversion(@ColorInt int parentBackground, ViewGroup viewGroup) {
         if (viewGroup == null) {
             return false;
         }
 
+        int backgroundColor = getBackgroundColor(viewGroup);
+        if (Color.alpha(backgroundColor) != 255) {
+            backgroundColor = ContrastColorUtil.compositeColors(backgroundColor, parentBackground);
+            backgroundColor = ColorUtils.setAlphaComponent(backgroundColor, 255);
+        }
         for (int i = 0; i < viewGroup.getChildCount(); i++) {
             View child = viewGroup.getChildAt(i);
-            int backgroundColor = getBackgroundColor(viewGroup);
-            if (backgroundColor == Color.TRANSPARENT) {
-                backgroundColor = parentBackground;
-            }
             if (child instanceof TextView) {
                 int foreground = ((TextView) child).getCurrentTextColor();
                 if (ColorUtils.calculateContrast(foreground, backgroundColor) < 3) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragment.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragment.java
index 6410860..195d02d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragment.java
@@ -191,7 +191,7 @@
     }
 
     protected int adjustDisableFlags(int state) {
-        if (!mStatusBarComponent.isLaunchTransitionFadingAway()
+        if (!mKeyguardMonitor.isLaunchTransitionFadingAway()
                 && !mKeyguardMonitor.isKeyguardFadingAway()
                 && shouldHideNotificationIcons()) {
             state |= DISABLE_NOTIFICATION_ICONS;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarEdgePanel.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarEdgePanel.java
index dae4da7..64209a7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarEdgePanel.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarEdgePanel.java
@@ -16,9 +16,17 @@
 
 package com.android.systemui.statusbar.phone;
 
+import android.animation.ObjectAnimator;
 import android.annotation.NonNull;
 import android.content.Context;
+import android.graphics.Canvas;
+import android.graphics.Paint;
 import android.graphics.PixelFormat;
+import android.util.FloatProperty;
+import android.util.MathUtils;
+import android.view.Gravity;
+import android.view.HapticFeedbackConstants;
+import android.view.MotionEvent;
 import android.view.View;
 import android.view.WindowManager;
 
@@ -27,6 +35,71 @@
 public class NavigationBarEdgePanel extends View {
     private static final String TAG = "NavigationBarEdgePanel";
 
+    // TODO: read from resources once drawing is finalized.
+    private static final boolean SHOW_PROTECTION_STROKE = true;
+    private static final int PROTECTION_COLOR = 0xffc0c0c0;
+    private static final int STROKE_COLOR = 0xffe5e5e5;
+    private static final int PROTECTION_WIDTH_PX = 4;
+    private static final int BASE_EXTENT = 32;
+    private static final int ARROW_HEIGHT_DP = 32;
+    private static final int POINT_EXTENT_DP = 8;
+    private static final int ARROW_THICKNESS_DP = 4;
+    private static final float TRACK_LENGTH_MULTIPLIER = 1.5f;
+    private static final float START_POINTING_RATIO = 0.3f;
+    private static final float POINTEDNESS_BEFORE_SNAP_RATIO = 0.4f;
+    private static final int ANIM_DURATION_MS = 150;
+
+    private final Paint mPaint = new Paint();
+    private final Paint mProtectionPaint = new Paint();
+
+    private final ObjectAnimator mEndAnimator;
+    private final ObjectAnimator mLegAnimator;
+
+    private final float mDensity;
+    private final float mBaseExtent;
+    private final float mPointExtent;
+    private final float mHeight;
+    private final float mStrokeThickness;
+    private final boolean mIsLeftPanel;
+
+    private float mStartY;
+    private float mStartX;
+
+    private boolean mGestureDetected;
+    private boolean mArrowsPointLeft;
+    private float mGestureLength;
+    private float mLegProgress;
+    private float mDragProgress;
+
+    // How much the "legs" of the back arrow have proceeded from being a line to an arrow.
+    private static final FloatProperty<NavigationBarEdgePanel> LEG_PROGRESS =
+            new FloatProperty<NavigationBarEdgePanel>("legProgress") {
+        @Override
+        public void setValue(NavigationBarEdgePanel object, float value) {
+            object.setLegProgress(value);
+        }
+
+        @Override
+        public Float get(NavigationBarEdgePanel object) {
+            return object.getLegProgress();
+        }
+    };
+
+    // How far across the view the arrow should be drawn.
+    private static final FloatProperty<NavigationBarEdgePanel> DRAG_PROGRESS =
+            new FloatProperty<NavigationBarEdgePanel>("dragProgress") {
+
+                @Override
+                public void setValue(NavigationBarEdgePanel object, float value) {
+                    object.setDragProgress(value);
+                }
+
+                @Override
+                public Float get(NavigationBarEdgePanel object) {
+                    return object.getDragProgress();
+                }
+            };
+
     public static NavigationBarEdgePanel create(@NonNull Context context, int width, int height,
             int gravity) {
         final WindowManager.LayoutParams lp = new WindowManager.LayoutParams(width, height,
@@ -40,13 +113,43 @@
         lp.setTitle(TAG + context.getDisplayId());
         lp.accessibilityTitle = context.getString(R.string.nav_bar_edge_panel);
         lp.windowAnimations = 0;
-        NavigationBarEdgePanel panel = new NavigationBarEdgePanel(context);
+        NavigationBarEdgePanel panel = new NavigationBarEdgePanel(
+                context, (gravity & Gravity.LEFT) == Gravity.LEFT);
         panel.setLayoutParams(lp);
         return panel;
     }
 
-    private NavigationBarEdgePanel(Context context) {
+    private NavigationBarEdgePanel(Context context, boolean isLeftPanel) {
         super(context);
+
+        mEndAnimator = ObjectAnimator.ofFloat(this, DRAG_PROGRESS, 1f);
+        mEndAnimator.setAutoCancel(true);
+        mEndAnimator.setDuration(ANIM_DURATION_MS);
+
+        mLegAnimator = ObjectAnimator.ofFloat(this, LEG_PROGRESS, 1f);
+        mLegAnimator.setAutoCancel(true);
+        mLegAnimator.setDuration(ANIM_DURATION_MS);
+
+        mDensity = context.getResources().getDisplayMetrics().density;
+
+        mBaseExtent = dp(BASE_EXTENT);
+        mHeight = dp(ARROW_HEIGHT_DP);
+        mPointExtent = dp(POINT_EXTENT_DP);
+        mStrokeThickness = dp(ARROW_THICKNESS_DP);
+
+        mPaint.setStrokeWidth(mStrokeThickness);
+        mPaint.setStrokeCap(Paint.Cap.ROUND);
+        mPaint.setColor(STROKE_COLOR);
+        mPaint.setAntiAlias(true);
+
+        mProtectionPaint.setStrokeWidth(mStrokeThickness + PROTECTION_WIDTH_PX);
+        mProtectionPaint.setStrokeCap(Paint.Cap.ROUND);
+        mProtectionPaint.setColor(PROTECTION_COLOR);
+        mProtectionPaint.setAntiAlias(true);
+
+        // Both panels arrow point the same way
+        mArrowsPointLeft = getLayoutDirection() == LAYOUT_DIRECTION_LTR;
+        mIsLeftPanel = isLeftPanel;
     }
 
     public void setWindowFlag(int flags, boolean enable) {
@@ -62,6 +165,58 @@
         updateLayout(lp);
     }
 
+    @Override
+    public boolean onTouchEvent(MotionEvent event) {
+        switch (event.getActionMasked()) {
+            case MotionEvent.ACTION_DOWN : {
+                show(event.getX(), event.getY());
+                break;
+            }
+            case MotionEvent.ACTION_MOVE: {
+                handleNewSwipePoint(event.getX());
+                break;
+            }
+            // Fall through
+            case MotionEvent.ACTION_UP:
+            case MotionEvent.ACTION_CANCEL: {
+                hide();
+                break;
+            }
+        }
+
+        return false;
+    }
+
+    @Override
+    protected void onDraw(Canvas canvas) {
+        float edgeOffset = mBaseExtent * mDragProgress - mStrokeThickness;
+        float animatedOffset = mPointExtent * mLegProgress;
+        canvas.save();
+        canvas.translate(
+                mIsLeftPanel ? edgeOffset : getWidth() - edgeOffset,
+                mStartY - mHeight * 0.5f);
+
+        float outsideX = mArrowsPointLeft ? animatedOffset : 0;
+        float middleX = mArrowsPointLeft ? 0 : animatedOffset;
+
+        if (SHOW_PROTECTION_STROKE) {
+            canvas.drawLine(outsideX, 0, middleX, mHeight * 0.5f, mProtectionPaint);
+            canvas.drawLine(middleX, mHeight * 0.5f, outsideX, mHeight, mProtectionPaint);
+        }
+
+        canvas.drawLine(outsideX, 0, middleX, mHeight * 0.5f, mPaint);
+        canvas.drawLine(middleX, mHeight * 0.5f, outsideX, mHeight, mPaint);
+        canvas.restore();
+    }
+
+    @Override
+    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
+        super.onLayout(changed, left, top, right, bottom);
+
+        // TODO: read the gesture length from the nav controller.
+        mGestureLength = getWidth();
+    }
+
     public void setDimensions(int width, int height) {
         final WindowManager.LayoutParams lp = (WindowManager.LayoutParams) getLayoutParams();
         if (lp.width != width || lp.height != height) {
@@ -71,8 +226,81 @@
         }
     }
 
+    private void setLegProgress(float progress) {
+        mLegProgress = progress;
+        invalidate();
+    }
+
+    private float getLegProgress() {
+        return mLegProgress;
+    }
+
+    private void setDragProgress(float dragProgress) {
+        mDragProgress = dragProgress;
+        invalidate();
+    }
+
+    private float getDragProgress() {
+        return mDragProgress;
+    }
+
+    private void hide() {
+        animate().alpha(0f).setDuration(ANIM_DURATION_MS);
+    }
+
+    private void show(float x, float y) {
+        mEndAnimator.cancel();
+        mLegAnimator.cancel();
+        setLegProgress(0f);
+        setDragProgress(0f);
+        setAlpha(1f);
+
+        float halfHeight = mHeight * 0.5f;
+        mStartY = MathUtils.constrain(y, halfHeight, getHeight() - halfHeight);
+        mStartX = x;
+    }
+
+    private void handleNewSwipePoint(float x) {
+        float dist = MathUtils.abs(x - mStartX);
+
+        setDragProgress(MathUtils.constrainedMap(
+                0, 1.0f,
+                0, mGestureLength * TRACK_LENGTH_MULTIPLIER,
+                dist));
+
+        if (dist < mGestureLength) {
+            float calculatedLegProgress = MathUtils.constrainedMap(
+                    0f, POINTEDNESS_BEFORE_SNAP_RATIO,
+                    mGestureLength * START_POINTING_RATIO, mGestureLength,
+                    dist);
+
+            // Blend animated value with drag calculated value, allow the gesture to continue
+            // while the animation is playing with jump cuts in the animation.
+            setLegProgress(MathUtils.lerp(calculatedLegProgress, mLegProgress, mDragProgress));
+
+            if (mGestureDetected) {
+                mGestureDetected = false;
+
+                mLegAnimator.setFloatValues(POINTEDNESS_BEFORE_SNAP_RATIO);
+                mLegAnimator.start();
+            }
+        } else {
+            if (!mGestureDetected) {
+                performHapticFeedback(HapticFeedbackConstants.CLOCK_TICK);
+                mGestureDetected = true;
+
+                mLegAnimator.setFloatValues(1f);
+                mLegAnimator.start();
+            }
+        }
+    }
+
     private void updateLayout(WindowManager.LayoutParams lp) {
         WindowManager wm = (WindowManager) getContext().getSystemService(Context.WINDOW_SERVICE);
         wm.updateViewLayout(this, lp);
     }
+
+    private float dp(float dp) {
+        return mDensity * dp;
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
index ebd4204..8152206 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
@@ -1227,10 +1227,11 @@
                     .getSystemService(Context.WINDOW_SERVICE);
             int width = mPrototypeController.getEdgeSensitivityWidth();
             int height = mPrototypeController.getEdgeSensitivityHeight();
+            // Explicitly left and right, not start and end as this is device relative.
             mLeftEdgePanel = NavigationBarEdgePanel.create(getContext(), width, height,
-                    Gravity.START | Gravity.BOTTOM);
+                    Gravity.LEFT | Gravity.BOTTOM);
             mRightEdgePanel = NavigationBarEdgePanel.create(getContext(), width, height,
-                    Gravity.END | Gravity.BOTTOM);
+                    Gravity.RIGHT | Gravity.BOTTOM);
             mLeftEdgePanel.setOnTouchListener(mEdgePanelTouchListener);
             mRightEdgePanel.setOnTouchListener(mEdgePanelTouchListener);
             wm.addView(mLeftEdgePanel, mLeftEdgePanel.getLayoutParams());
@@ -1252,15 +1253,12 @@
             mButtonDispatchers.valueAt(i).onDestroy();
         }
 
-        if (mPrototypeController.isEnabled()) {
-            WindowManager wm = (WindowManager) getContext()
-                    .getSystemService(Context.WINDOW_SERVICE);
-            if (mLeftEdgePanel != null) {
-                wm.removeView(mLeftEdgePanel);
-            }
-            if (mRightEdgePanel != null) {
-                wm.removeView(mRightEdgePanel);
-            }
+        WindowManager wm = (WindowManager) getContext().getSystemService(Context.WINDOW_SERVICE);
+        if (mLeftEdgePanel != null) {
+            wm.removeView(mLeftEdgePanel);
+        }
+        if (mRightEdgePanel != null) {
+            wm.removeView(mRightEdgePanel);
         }
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupAlertTransferHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupAlertTransferHelper.java
index b613e8e..4dbd854 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupAlertTransferHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupAlertTransferHelper.java
@@ -34,8 +34,8 @@
 import com.android.systemui.statusbar.notification.NotificationEntryListener;
 import com.android.systemui.statusbar.notification.NotificationEntryManager;
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
-import com.android.systemui.statusbar.notification.row.NotificationInflater.AsyncInflationTask;
-import com.android.systemui.statusbar.notification.row.NotificationInflater.InflationFlag;
+import com.android.systemui.statusbar.notification.row.NotificationContentInflater.AsyncInflationTask;
+import com.android.systemui.statusbar.notification.row.NotificationContentInflater.InflationFlag;
 import com.android.systemui.statusbar.phone.NotificationGroupManager.NotificationGroup;
 import com.android.systemui.statusbar.phone.NotificationGroupManager.OnGroupChangeListener;
 import com.android.systemui.statusbar.policy.HeadsUpManager;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
index e42004a..30d5b65 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -117,7 +117,6 @@
 import android.view.accessibility.AccessibilityManager;
 import android.view.animation.AccelerateInterpolator;
 import android.widget.DateTimeView;
-import android.widget.ImageView;
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.colorextraction.ColorExtractor;
@@ -460,9 +459,6 @@
     protected boolean mDozing;
     private boolean mDozingRequested;
 
-    protected BackDropView mBackdrop;
-    protected ImageView mBackdropFront, mBackdropBack;
-
     private NotificationMediaManager mMediaManager;
     protected NotificationLockscreenUserManager mLockscreenUserManager;
     protected NotificationRemoteInputManager mRemoteInputManager;
@@ -492,7 +488,6 @@
     };
 
     private Runnable mLaunchTransitionEndRunnable;
-    protected boolean mLaunchTransitionFadingAway;
     private NotificationEntry mDraggedDownEntry;
     private boolean mLaunchCameraOnScreenTurningOn;
     private boolean mLaunchCameraOnFinishedGoingToSleep;
@@ -932,11 +927,9 @@
                 mHeadsUpManager, mNotificationIconAreaController, mScrimController);
         mDozeScrimController = new DozeScrimController(DozeParameters.getInstance(context));
 
-        mBackdrop = mStatusBarWindow.findViewById(R.id.backdrop);
-        mBackdropFront = mBackdrop.findViewById(R.id.backdrop_front);
-        mBackdropBack = mBackdrop.findViewById(R.id.backdrop_back);
-        mMediaManager.setup(mBackdrop, mBackdropFront, mBackdropBack,
-                mScrimController, mLockscreenWallpaper);
+        BackDropView backdrop = mStatusBarWindow.findViewById(R.id.backdrop);
+        mMediaManager.setup(backdrop, backdrop.findViewById(R.id.backdrop_front),
+                backdrop.findViewById(R.id.backdrop_back), mScrimController, mLockscreenWallpaper);
 
         // Other icons
         mVolumeComponent = getComponent(VolumeComponent.class);
@@ -1592,10 +1585,6 @@
         return mPulsing;
     }
 
-    public boolean isLaunchTransitionFadingAway() {
-        return mLaunchTransitionFadingAway;
-    }
-
     public boolean hideStatusBarIconsWhenExpanded() {
         return mNotificationPanel.hideStatusBarIconsWhenExpanded();
     }
@@ -2974,7 +2963,7 @@
 
     public void showKeyguardImpl() {
         mIsKeyguard = true;
-        if (mLaunchTransitionFadingAway) {
+        if (mKeyguardMonitor.isLaunchTransitionFadingAway()) {
             mNotificationPanel.animate().cancel();
             onLaunchTransitionFadingEnded();
         }
@@ -3006,7 +2995,7 @@
         mNotificationPanel.onAffordanceLaunchEnded();
         releaseGestureWakeLock();
         runLaunchTransitionEndRunnable();
-        mLaunchTransitionFadingAway = false;
+        mKeyguardMonitor.setLaunchTransitionFadingAway(false);
         mPresenter.updateMediaMetaData(true /* metaDataChanged */, true);
     }
 
@@ -3032,7 +3021,6 @@
         mLaunchTransitionEndRunnable = endRunnable;
         Runnable hideRunnable = () -> {
             mKeyguardMonitor.setLaunchTransitionFadingAway(true);
-            mLaunchTransitionFadingAway = true;
             if (beforeFading != null) {
                 beforeFading.run();
             }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusIconContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusIconContainer.java
index 6495910..6e36c01 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusIconContainer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusIconContainer.java
@@ -38,6 +38,7 @@
 import com.android.systemui.statusbar.notification.stack.ViewState;
 
 import java.util.ArrayList;
+import java.util.List;
 
 /**
  * A container for Status bar system icons. Limits the number of system icons and handles overflow
@@ -67,6 +68,8 @@
     private ArrayList<StatusIconState> mLayoutStates = new ArrayList<>();
     // So we can count and measure properly
     private ArrayList<View> mMeasureViews = new ArrayList<>();
+    // Any ignored icon will never be added as a child
+    private ArrayList<String> mIgnoredSlots = new ArrayList<>();
 
     public StatusIconContainer(Context context) {
         this(context, null);
@@ -146,7 +149,8 @@
         // Collect all of the views which want to be laid out
         for (int i = 0; i < count; i++) {
             StatusIconDisplayable icon = (StatusIconDisplayable) getChildAt(i);
-            if (icon.isIconVisible() && !icon.isIconBlocked()) {
+            if (icon.isIconVisible() && !icon.isIconBlocked()
+                    && !mIgnoredSlots.contains(icon.getSlot())) {
                 mMeasureViews.add((View) icon);
             }
         }
@@ -205,6 +209,47 @@
     }
 
     /**
+     * Add a name of an icon slot to be ignored. It will not show up nor be measured
+     * @param slotName name of the icon as it exists in
+     * frameworks/base/core/res/res/values/config.xml
+     */
+    public void addIgnoredSlot(String slotName) {
+        addIgnoredSlotInternal(slotName);
+        requestLayout();
+    }
+
+    /**
+     * Add a list of slots to be ignored
+     * @param slots names of the icons to ignore
+     */
+    public void addIgnoredSlots(List<String> slots) {
+        for (String slot : slots) {
+            addIgnoredSlotInternal(slot);
+        }
+
+        requestLayout();
+    }
+
+    private void addIgnoredSlotInternal(String slotName) {
+        if (!mIgnoredSlots.contains(slotName)) {
+            mIgnoredSlots.add(slotName);
+        }
+    }
+
+    /**
+     * Remove a slot from the list of ignored icon slots. It will then be shown when set to visible
+     * by the {@link StatusBarIconController}.
+     * @param slotName name of the icon slot to remove from the ignored list
+     */
+    public void removeIgnoredSlot(String slotName) {
+        if (mIgnoredSlots.contains(slotName)) {
+            mIgnoredSlots.remove(slotName);
+        }
+
+        requestLayout();
+    }
+
+    /**
      * Layout is happening from end -> start
      */
     private void calculateIconTranslations() {
@@ -223,7 +268,8 @@
             StatusIconDisplayable iconView = (StatusIconDisplayable) child;
             StatusIconState childState = getViewStateFromChild(child);
 
-            if (!iconView.isIconVisible() || iconView.isIconBlocked()) {
+            if (!iconView.isIconVisible() || iconView.isIconBlocked()
+                    || mIgnoredSlots.contains(iconView.getSlot())) {
                 childState.visibleState = STATE_HIDDEN;
                 if (DEBUG) Log.d(TAG, "skipping child (" + iconView.getSlot() + ") not visible");
                 continue;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java
index fd3f680..0461057 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java
@@ -16,7 +16,7 @@
 
 package com.android.systemui.statusbar.policy;
 
-import static com.android.systemui.statusbar.notification.row.NotificationInflater.FLAG_CONTENT_VIEW_HEADS_UP;
+import static com.android.systemui.statusbar.notification.row.NotificationContentInflater.FLAG_CONTENT_VIEW_HEADS_UP;
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -31,7 +31,7 @@
 import com.android.systemui.R;
 import com.android.systemui.statusbar.AlertingNotificationManager;
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
-import com.android.systemui.statusbar.notification.row.NotificationInflater.InflationFlag;
+import com.android.systemui.statusbar.notification.row.NotificationContentInflater.InflationFlag;
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bubbles/animation/ExpandedAnimationControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/bubbles/animation/ExpandedAnimationControllerTest.java
index c0aac7e..3bd582f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bubbles/animation/ExpandedAnimationControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/bubbles/animation/ExpandedAnimationControllerTest.java
@@ -22,6 +22,8 @@
 import android.graphics.PointF;
 import android.support.test.filters.SmallTest;
 import android.testing.AndroidTestingRunner;
+import android.view.View;
+import android.widget.FrameLayout;
 
 import androidx.dynamicanimation.animation.DynamicAnimation;
 
@@ -63,15 +65,13 @@
     public void testExpansionAndCollapse() throws InterruptedException {
         Runnable afterExpand = Mockito.mock(Runnable.class);
         mExpandedController.expandFromStack(mExpansionPoint, afterExpand);
-
         waitForPropertyAnimations(DynamicAnimation.TRANSLATION_X, DynamicAnimation.TRANSLATION_Y);
 
-        testExpanded();
+        testBubblesInCorrectExpandedPositions();
         Mockito.verify(afterExpand).run();
 
         Runnable afterCollapse = Mockito.mock(Runnable.class);
         mExpandedController.collapseBackToStack(afterCollapse);
-
         waitForPropertyAnimations(DynamicAnimation.TRANSLATION_X, DynamicAnimation.TRANSLATION_Y);
 
         testStackedAtPosition(mExpansionPoint.x, mExpansionPoint.y, -1);
@@ -79,17 +79,70 @@
     }
 
     @Test
-    public void testOnChildRemoved() throws InterruptedException {
-        Runnable afterExpand = Mockito.mock(Runnable.class);
-        mExpandedController.expandFromStack(mExpansionPoint, afterExpand);
+    public void testOnChildAdded() throws InterruptedException {
+        expand();
+
+        // Add another new view and wait for its animation.
+        final View newView = new FrameLayout(getContext());
+        mLayout.addView(newView, 0);
         waitForPropertyAnimations(DynamicAnimation.TRANSLATION_X, DynamicAnimation.TRANSLATION_Y);
-        testExpanded();
+
+        testBubblesInCorrectExpandedPositions();
+    }
+
+    @Test
+    public void testOnChildRemoved() throws InterruptedException {
+        expand();
 
         // Remove some views and see if the remaining child views still pass the expansion test.
         mLayout.removeView(mViews.get(0));
         mLayout.removeView(mViews.get(3));
         waitForPropertyAnimations(DynamicAnimation.TRANSLATION_X, DynamicAnimation.TRANSLATION_Y);
-        testExpanded();
+        testBubblesInCorrectExpandedPositions();
+    }
+
+    @Test
+    public void testBubbleDraggedNotDismissedSnapsBack() throws InterruptedException {
+        expand();
+
+        final View draggedBubble = mViews.get(0);
+        mExpandedController.prepareForBubbleDrag(draggedBubble);
+        mExpandedController.dragBubbleOut(draggedBubble, 500f, 500f);
+
+        assertEquals(500f, draggedBubble.getTranslationX(), 1f);
+        assertEquals(500f, draggedBubble.getTranslationY(), 1f);
+
+        // Snap it back and make sure it made it back correctly.
+        mExpandedController.snapBubbleBack(draggedBubble, 0f, 0f);
+        waitForPropertyAnimations(DynamicAnimation.TRANSLATION_X, DynamicAnimation.TRANSLATION_Y);
+        testBubblesInCorrectExpandedPositions();
+    }
+
+    @Test
+    public void testBubbleDismissed() throws InterruptedException {
+        expand();
+
+        final View draggedBubble = mViews.get(0);
+        mExpandedController.prepareForBubbleDrag(draggedBubble);
+        mExpandedController.dragBubbleOut(draggedBubble, 500f, 500f);
+
+        assertEquals(500f, draggedBubble.getTranslationX(), 1f);
+        assertEquals(500f, draggedBubble.getTranslationY(), 1f);
+
+        // Snap it back and make sure it made it back correctly.
+        mExpandedController.prepareForDismissalWithVelocity(draggedBubble, 0f, 0f);
+        mLayout.removeView(draggedBubble);
+        waitForLayoutMessageQueue();
+        waitForPropertyAnimations(DynamicAnimation.TRANSLATION_X, DynamicAnimation.TRANSLATION_Y);
+
+        assertEquals(-1, mLayout.indexOfChild(draggedBubble));
+        testBubblesInCorrectExpandedPositions();
+    }
+
+    /** Expand the stack and wait for animations to finish. */
+    private void expand() throws InterruptedException {
+        mExpandedController.expandFromStack(mExpansionPoint, Mockito.mock(Runnable.class));
+        waitForPropertyAnimations(DynamicAnimation.TRANSLATION_X, DynamicAnimation.TRANSLATION_Y);
     }
 
     /** Check that children are in the correct positions for being stacked. */
@@ -108,7 +161,7 @@
     }
 
     /** Check that children are in the correct positions for being expanded. */
-    private void testExpanded() {
+    private void testBubblesInCorrectExpandedPositions() {
         // Check all the visible bubbles to see if they're in the right place.
         for (int i = 0; i < Math.min(mLayout.getChildCount(), mMaxRenderedBubbles); i++) {
             assertEquals(mBubblePadding + (i * (mBubbleSize + mBubblePadding)),
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bubbles/animation/PhysicsAnimationLayoutTestCase.java b/packages/SystemUI/tests/src/com/android/systemui/bubbles/animation/PhysicsAnimationLayoutTestCase.java
index 31e44d7..d94b669 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bubbles/animation/PhysicsAnimationLayoutTestCase.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/bubbles/animation/PhysicsAnimationLayoutTestCase.java
@@ -155,6 +155,11 @@
         }
 
         @Override
+        public void cancelAnimationsOnView(View view) {
+            mMainThreadHandler.post(() -> super.cancelAnimationsOnView(view));
+        }
+
+        @Override
         protected void animateValueForChildAtIndex(DynamicAnimation.ViewProperty property,
                 int index, float value, float startVel, Runnable after) {
             mMainThreadHandler.post(() ->
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/AlertingNotificationManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/AlertingNotificationManagerTest.java
index 660f853..7b1253a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/AlertingNotificationManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/AlertingNotificationManagerTest.java
@@ -17,7 +17,7 @@
 
 package com.android.systemui.statusbar;
 
-import static com.android.systemui.statusbar.notification.row.NotificationInflater.FLAG_CONTENT_VIEW_CONTRACTED;
+import static com.android.systemui.statusbar.notification.row.NotificationContentInflater.FLAG_CONTENT_VIEW_CONTRACTED;
 
 import static junit.framework.Assert.assertFalse;
 import static junit.framework.Assert.assertTrue;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationTestHelper.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationTestHelper.java
index a2c880a5..3c919a1 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationTestHelper.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationTestHelper.java
@@ -39,8 +39,8 @@
 import com.android.systemui.bubbles.BubblesTestActivity;
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
-import com.android.systemui.statusbar.notification.row.NotificationInflater.InflationFlag;
-import com.android.systemui.statusbar.notification.row.NotificationInflaterTest;
+import com.android.systemui.statusbar.notification.row.NotificationContentInflater.InflationFlag;
+import com.android.systemui.statusbar.notification.row.NotificationContentInflaterTest;
 import com.android.systemui.statusbar.phone.HeadsUpManagerPhone;
 import com.android.systemui.statusbar.phone.NotificationGroupManager;
 import com.android.systemui.statusbar.policy.HeadsUpManager;
@@ -279,7 +279,7 @@
         entry.channel.setBlockableSystem(true);
         row.setEntry(entry);
         row.getNotificationInflater().addInflationFlags(extraInflationFlags);
-        NotificationInflaterTest.runThenWaitForInflation(
+        NotificationContentInflaterTest.runThenWaitForInflation(
                 () -> row.inflateViews(),
                 row.getNotificationInflater());
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java
index 79bc0a3..04e7cab 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java
@@ -68,8 +68,8 @@
 import com.android.systemui.statusbar.notification.collection.NotificationData.KeyguardEnvironment;
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
+import com.android.systemui.statusbar.notification.row.NotificationContentInflater.InflationFlag;
 import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
-import com.android.systemui.statusbar.notification.row.NotificationInflater;
 import com.android.systemui.statusbar.notification.row.RowInflaterTask;
 import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
 import com.android.systemui.statusbar.phone.NotificationGroupManager;
@@ -143,7 +143,7 @@
 
         @Override
         public void onAsyncInflationFinished(NotificationEntry entry,
-                @NotificationInflater.InflationFlag int inflatedFlags) {
+                @InflationFlag int inflatedFlags) {
             super.onAsyncInflationFinished(entry, inflatedFlags);
 
             mCountDownLatch.countDown();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java
index 6d35539..f6fb416 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java
@@ -18,9 +18,9 @@
 
 import static android.app.NotificationManager.IMPORTANCE_DEFAULT;
 
-import static com.android.systemui.statusbar.notification.row.NotificationInflater.FLAG_CONTENT_VIEW_ALL;
-import static com.android.systemui.statusbar.notification.row.NotificationInflater.FLAG_CONTENT_VIEW_HEADS_UP;
-import static com.android.systemui.statusbar.notification.row.NotificationInflater.FLAG_CONTENT_VIEW_PUBLIC;
+import static com.android.systemui.statusbar.notification.row.NotificationContentInflater.FLAG_CONTENT_VIEW_ALL;
+import static com.android.systemui.statusbar.notification.row.NotificationContentInflater.FLAG_CONTENT_VIEW_HEADS_UP;
+import static com.android.systemui.statusbar.notification.row.NotificationContentInflater.FLAG_CONTENT_VIEW_PUBLIC;
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationInflaterTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationContentInflaterTest.java
similarity index 85%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationInflaterTest.java
rename to packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationContentInflaterTest.java
index 648df3c..dfaa76a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationInflaterTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationContentInflaterTest.java
@@ -16,11 +16,11 @@
 
 package com.android.systemui.statusbar.notification.row;
 
-import static com.android.systemui.statusbar.notification.row.NotificationInflater.FLAG_CONTENT_VIEW_ALL;
-import static com.android.systemui.statusbar.notification.row.NotificationInflater.FLAG_CONTENT_VIEW_AMBIENT;
-import static com.android.systemui.statusbar.notification.row.NotificationInflater.FLAG_CONTENT_VIEW_EXPANDED;
-import static com.android.systemui.statusbar.notification.row.NotificationInflater.FLAG_CONTENT_VIEW_HEADS_UP;
-import static com.android.systemui.statusbar.notification.row.NotificationInflater.FLAG_CONTENT_VIEW_PUBLIC;
+import static com.android.systemui.statusbar.notification.row.NotificationContentInflater.FLAG_CONTENT_VIEW_ALL;
+import static com.android.systemui.statusbar.notification.row.NotificationContentInflater.FLAG_CONTENT_VIEW_AMBIENT;
+import static com.android.systemui.statusbar.notification.row.NotificationContentInflater.FLAG_CONTENT_VIEW_EXPANDED;
+import static com.android.systemui.statusbar.notification.row.NotificationContentInflater.FLAG_CONTENT_VIEW_HEADS_UP;
+import static com.android.systemui.statusbar.notification.row.NotificationContentInflater.FLAG_CONTENT_VIEW_PUBLIC;
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotNull;
@@ -49,6 +49,7 @@
 import com.android.systemui.statusbar.InflationTask;
 import com.android.systemui.statusbar.NotificationTestHelper;
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
+import com.android.systemui.statusbar.notification.row.NotificationContentInflater.InflationCallback;
 
 import org.junit.Assert;
 import org.junit.Before;
@@ -64,9 +65,9 @@
 @SmallTest
 @RunWith(AndroidTestingRunner.class)
 @RunWithLooper(setAsMainLooper = true)
-public class NotificationInflaterTest extends SysuiTestCase {
+public class NotificationContentInflaterTest extends SysuiTestCase {
 
-    private NotificationInflater mNotificationInflater;
+    private NotificationContentInflater mNotificationInflater;
     private Notification.Builder mBuilder;
     private ExpandableNotificationRow mRow;
 
@@ -80,8 +81,8 @@
         ExpandableNotificationRow row = new NotificationTestHelper(mContext).createRow(
                 mBuilder.build());
         mRow = spy(row);
-        mNotificationInflater = new NotificationInflater(mRow);
-        mNotificationInflater.setInflationCallback(new NotificationInflater.InflationCallback() {
+        mNotificationInflater = new NotificationContentInflater(mRow);
+        mNotificationInflater.setInflationCallback(new InflationCallback() {
             @Override
             public void handleInflationException(StatusBarNotification notification,
                     Exception e) {
@@ -89,7 +90,7 @@
 
             @Override
             public void onAsyncInflationFinished(NotificationEntry entry,
-                    @NotificationInflater.InflationFlag int inflatedFlags) {
+                    @NotificationContentInflater.InflationFlag int inflatedFlags) {
             }
         });
     }
@@ -158,14 +159,14 @@
     @Test
     @Ignore
     public void testInflationIsRetriedIfAsyncFails() throws Exception {
-        NotificationInflater.InflationProgress result =
-                new NotificationInflater.InflationProgress();
+        NotificationContentInflater.InflationProgress result =
+                new NotificationContentInflater.InflationProgress();
         result.packageContext = mContext;
         CountDownLatch countDownLatch = new CountDownLatch(1);
-        NotificationInflater.applyRemoteView(result, FLAG_CONTENT_VIEW_EXPANDED, 0,
+        NotificationContentInflater.applyRemoteView(result, FLAG_CONTENT_VIEW_EXPANDED, 0,
                 new ArrayMap() /* cachedContentViews */, mRow, false /* redactAmbient */,
                 true /* isNewView */, (v, p, r) -> true,
-                new NotificationInflater.InflationCallback() {
+                new InflationCallback() {
                     @Override
                     public void handleInflationException(StatusBarNotification notification,
                             Exception e) {
@@ -175,11 +176,11 @@
 
                     @Override
                     public void onAsyncInflationFinished(NotificationEntry entry,
-                            @NotificationInflater.InflationFlag int inflatedFlags) {
+                            @NotificationContentInflater.InflationFlag int inflatedFlags) {
                         countDownLatch.countDown();
                     }
                 }, mRow.getPrivateLayout(), null, null, new HashMap<>(),
-                new NotificationInflater.ApplyCallback() {
+                new NotificationContentInflater.ApplyCallback() {
                     @Override
                     public void setResultView(View v) {
                     }
@@ -199,8 +200,8 @@
         mNotificationInflater.updateInflationFlag(FLAG_CONTENT_VIEW_PUBLIC, true);
         mNotificationInflater.updateNeedsRedaction(true);
 
-        NotificationInflater.AsyncInflationTask asyncInflationTask =
-                (NotificationInflater.AsyncInflationTask) mRow.getEntry().getRunningTask();
+        NotificationContentInflater.AsyncInflationTask asyncInflationTask =
+                (NotificationContentInflater.AsyncInflationTask) mRow.getEntry().getRunningTask();
         assertEquals(FLAG_CONTENT_VIEW_AMBIENT | FLAG_CONTENT_VIEW_PUBLIC,
                 asyncInflationTask.getReInflateFlags());
         asyncInflationTask.abort();
@@ -217,8 +218,8 @@
         mNotificationInflater.setIsChildInGroup(true);
 
         InflationTask runningTask = mRow.getEntry().getRunningTask();
-        NotificationInflater.AsyncInflationTask asyncInflationTask =
-                (NotificationInflater.AsyncInflationTask) runningTask;
+        NotificationContentInflater.AsyncInflationTask asyncInflationTask =
+                (NotificationContentInflater.AsyncInflationTask) runningTask;
         assertEquals("Successive inflations don't inherit the previous flags!",
                 FLAG_CONTENT_VIEW_ALL, asyncInflationTask.getReInflateFlags());
         runningTask.abort();
@@ -233,19 +234,19 @@
                 R.layout.custom_view_dark));
         RemoteViews decoratedMediaView = mBuilder.createContentView();
         Assert.assertFalse("The decorated media style doesn't allow a view to be reapplied!",
-                NotificationInflater.canReapplyRemoteView(mediaView, decoratedMediaView));
+                NotificationContentInflater.canReapplyRemoteView(mediaView, decoratedMediaView));
     }
 
     public static void runThenWaitForInflation(Runnable block,
-            NotificationInflater inflater) throws Exception {
+            NotificationContentInflater inflater) throws Exception {
         runThenWaitForInflation(block, false /* expectingException */, inflater);
     }
 
     private static void runThenWaitForInflation(Runnable block, boolean expectingException,
-            NotificationInflater inflater) throws Exception {
+            NotificationContentInflater inflater) throws Exception {
         CountDownLatch countDownLatch = new CountDownLatch(1);
         final ExceptionHolder exceptionHolder = new ExceptionHolder();
-        inflater.setInflationCallback(new NotificationInflater.InflationCallback() {
+        inflater.setInflationCallback(new InflationCallback() {
             @Override
             public void handleInflationException(StatusBarNotification notification,
                     Exception e) {
@@ -257,7 +258,7 @@
 
             @Override
             public void onAsyncInflationFinished(NotificationEntry entry,
-                    @NotificationInflater.InflationFlag int inflatedFlags) {
+                    @NotificationContentInflater.InflationFlag int inflatedFlags) {
                 if (expectingException) {
                     exceptionHolder.setException(new RuntimeException(
                             "Inflation finished even though there should be an error"));
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationViewWrapperTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationViewWrapperTest.java
similarity index 63%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationViewWrapperTest.java
rename to packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationViewWrapperTest.java
index 24aa772..637b30c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationViewWrapperTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationViewWrapperTest.java
@@ -14,7 +14,9 @@
  * limitations under the License.
  */
 
-package com.android.systemui.statusbar.notification;
+package com.android.systemui.statusbar.notification.row.wrapper;
+
+import static org.mockito.Mockito.mock;
 
 import android.content.Context;
 import android.support.test.filters.SmallTest;
@@ -22,13 +24,15 @@
 import android.testing.TestableLooper;
 import android.testing.TestableLooper.RunWithLooper;
 import android.view.View;
+import android.widget.LinearLayout;
+import android.widget.TextView;
 
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.statusbar.NotificationTestHelper;
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
-import com.android.systemui.statusbar.notification.row.wrapper.NotificationViewWrapper;
 import com.android.systemui.util.Assert;
 
+import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
@@ -37,12 +41,26 @@
 @RunWithLooper
 public class NotificationViewWrapperTest extends SysuiTestCase {
 
-    @Test
-    public void constructor_doesntUseViewContext() throws Exception {
+    private View mView;
+    private ExpandableNotificationRow mRow;
+    private TestableNotificationViewWrapper mNotificationViewWrapper;
+
+    @Before
+    public void setup() throws Exception {
         Assert.sMainLooper = TestableLooper.get(this).getLooper();
-        new TestableNotificationViewWrapper(mContext,
-                new View(mContext),
-                new NotificationTestHelper(getContext()).createRow());
+        mView = mock(View.class);
+        mRow = new NotificationTestHelper(getContext()).createRow();
+        mNotificationViewWrapper = new TestableNotificationViewWrapper(mContext, mView, mRow);
+    }
+
+    @Test
+    public void childrenNeedInversion_doesntCrash_whenOpacity() {
+        LinearLayout viewGroup = new LinearLayout(mContext);
+        TextView textView = new TextView(mContext);
+        textView.setTextColor(0xcc000000);
+        viewGroup.addView(textView);
+
+        mNotificationViewWrapper.childrenNeedInversion(0xcc000000, viewGroup);
     }
 
     static class TestableNotificationViewWrapper extends NotificationViewWrapper {
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index dad428a..915c131 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -57,8 +57,10 @@
 import android.content.IntentFilter;
 import android.content.res.Configuration;
 import android.database.ContentObserver;
+import android.net.CaptivePortal;
 import android.net.ConnectionInfo;
 import android.net.ConnectivityManager;
+import android.net.ICaptivePortal;
 import android.net.IConnectivityManager;
 import android.net.IIpConnectivityMetrics;
 import android.net.INetd;
@@ -86,6 +88,7 @@
 import android.net.NetworkRequest;
 import android.net.NetworkSpecifier;
 import android.net.NetworkStack;
+import android.net.NetworkStackClient;
 import android.net.NetworkState;
 import android.net.NetworkUtils;
 import android.net.NetworkWatchlistManager;
@@ -917,7 +920,8 @@
 
         mPermissionMonitor = new PermissionMonitor(mContext, mNMS);
 
-        //set up the listener for user state for creating user VPNs
+        // Set up the listener for user state for creating user VPNs.
+        // Should run on mHandler to avoid any races.
         IntentFilter intentFilter = new IntentFilter();
         intentFilter.addAction(Intent.ACTION_USER_STARTED);
         intentFilter.addAction(Intent.ACTION_USER_STOPPED);
@@ -925,7 +929,11 @@
         intentFilter.addAction(Intent.ACTION_USER_REMOVED);
         intentFilter.addAction(Intent.ACTION_USER_UNLOCKED);
         mContext.registerReceiverAsUser(
-                mIntentReceiver, UserHandle.ALL, intentFilter, null, null);
+                mIntentReceiver,
+                UserHandle.ALL,
+                intentFilter,
+                null /* broadcastPermission */,
+                mHandler);
         mContext.registerReceiverAsUser(mUserPresentReceiver, UserHandle.SYSTEM,
                 new IntentFilter(Intent.ACTION_USER_PRESENT), null, null);
 
@@ -936,7 +944,11 @@
         intentFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);
         intentFilter.addDataScheme("package");
         mContext.registerReceiverAsUser(
-                mIntentReceiver, UserHandle.ALL, intentFilter, null, null);
+                mIntentReceiver,
+                UserHandle.ALL,
+                intentFilter,
+                null /* broadcastPermission */,
+                mHandler);
 
         try {
             mNMS.registerObserver(mTethering);
@@ -2690,11 +2702,6 @@
                     EVENT_PROVISIONING_NOTIFICATION, PROVISIONING_NOTIFICATION_HIDE,
                     mNai.network.netId));
         }
-
-        @Override
-        public void logCaptivePortalLoginEvent(int eventId, String packageName) {
-            new MetricsLogger().action(eventId, packageName);
-        }
     }
 
     private boolean networkRequiresValidation(NetworkAgentInfo nai) {
@@ -3249,22 +3256,63 @@
     /**
      * NetworkStack endpoint to start the captive portal app. The NetworkStack needs to use this
      * endpoint as it does not have INTERACT_ACROSS_USERS_FULL itself.
+     * @param network Network on which the captive portal was detected.
      * @param appExtras Bundle to use as intent extras for the captive portal application.
      *                  Must be treated as opaque to avoid preventing the captive portal app to
      *                  update its arguments.
      */
     @Override
-    public void startCaptivePortalAppInternal(Bundle appExtras) {
+    public void startCaptivePortalAppInternal(Network network, Bundle appExtras) {
         mContext.checkCallingOrSelfPermission(NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK);
 
         final Intent appIntent = new Intent(ConnectivityManager.ACTION_CAPTIVE_PORTAL_SIGN_IN);
         appIntent.putExtras(appExtras);
+        appIntent.putExtra(ConnectivityManager.EXTRA_CAPTIVE_PORTAL,
+                new CaptivePortal(new CaptivePortalImpl(network).asBinder()));
         appIntent.setFlags(Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT | Intent.FLAG_ACTIVITY_NEW_TASK);
 
         Binder.withCleanCallingIdentity(() ->
                 mContext.startActivityAsUser(appIntent, UserHandle.CURRENT));
     }
 
+    private class CaptivePortalImpl extends ICaptivePortal.Stub {
+        private final Network mNetwork;
+
+        private CaptivePortalImpl(Network network) {
+            mNetwork = network;
+        }
+
+        @Override
+        public void appResponse(final int response) throws RemoteException {
+            if (response == CaptivePortal.APP_RETURN_WANTED_AS_IS) {
+                enforceSettingsPermission();
+            }
+
+            // getNetworkAgentInfoForNetwork is thread-safe
+            final NetworkAgentInfo nai = getNetworkAgentInfoForNetwork(mNetwork);
+            if (nai == null) return;
+
+            // nai.networkMonitor() is thread-safe
+            final INetworkMonitor nm = nai.networkMonitor();
+            if (nm == null) return;
+
+            final long token = Binder.clearCallingIdentity();
+            try {
+                nm.notifyCaptivePortalAppFinished(response);
+            } finally {
+                // Not using Binder.withCleanCallingIdentity() to keep the checked RemoteException
+                Binder.restoreCallingIdentity(token);
+            }
+        }
+
+        @Override
+        public void logEvent(int eventId, String packageName) {
+            enforceSettingsPermission();
+
+            new MetricsLogger().action(eventId, packageName);
+        }
+    }
+
     public boolean avoidBadWifi() {
         return mMultinetworkPolicyTracker.getAvoidBadWifi();
     }
@@ -4099,17 +4147,27 @@
      * handler thread through their agent, this is asynchronous. When the capabilities objects
      * are computed they will be up-to-date as they are computed synchronously from here and
      * this is running on the ConnectivityService thread.
-     * TODO : Fix this and call updateCapabilities inline to remove out-of-order events.
      */
     private void updateAllVpnsCapabilities() {
+        Network defaultNetwork = getNetwork(getDefaultNetwork());
         synchronized (mVpns) {
             for (int i = 0; i < mVpns.size(); i++) {
                 final Vpn vpn = mVpns.valueAt(i);
-                vpn.updateCapabilities();
+                NetworkCapabilities nc = vpn.updateCapabilities(defaultNetwork);
+                updateVpnCapabilities(vpn, nc);
             }
         }
     }
 
+    private void updateVpnCapabilities(Vpn vpn, @Nullable NetworkCapabilities nc) {
+        ensureRunningOnConnectivityServiceThread();
+        NetworkAgentInfo vpnNai = getNetworkAgentInfoForNetId(vpn.getNetId());
+        if (vpnNai == null || nc == null) {
+            return;
+        }
+        updateCapabilities(vpnNai.getCurrentScore(), vpnNai, nc);
+    }
+
     @Override
     public boolean updateLockdownVpn() {
         if (Binder.getCallingUid() != Process.SYSTEM_UID) {
@@ -4450,22 +4508,28 @@
 
     private void onUserAdded(int userId) {
         mPermissionMonitor.onUserAdded(userId);
+        Network defaultNetwork = getNetwork(getDefaultNetwork());
         synchronized (mVpns) {
             final int vpnsSize = mVpns.size();
             for (int i = 0; i < vpnsSize; i++) {
                 Vpn vpn = mVpns.valueAt(i);
                 vpn.onUserAdded(userId);
+                NetworkCapabilities nc = vpn.updateCapabilities(defaultNetwork);
+                updateVpnCapabilities(vpn, nc);
             }
         }
     }
 
     private void onUserRemoved(int userId) {
         mPermissionMonitor.onUserRemoved(userId);
+        Network defaultNetwork = getNetwork(getDefaultNetwork());
         synchronized (mVpns) {
             final int vpnsSize = mVpns.size();
             for (int i = 0; i < vpnsSize; i++) {
                 Vpn vpn = mVpns.valueAt(i);
                 vpn.onUserRemoved(userId);
+                NetworkCapabilities nc = vpn.updateCapabilities(defaultNetwork);
+                updateVpnCapabilities(vpn, nc);
             }
         }
     }
@@ -4534,6 +4598,7 @@
     private BroadcastReceiver mIntentReceiver = new BroadcastReceiver() {
         @Override
         public void onReceive(Context context, Intent intent) {
+            ensureRunningOnConnectivityServiceThread();
             final String action = intent.getAction();
             final int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, UserHandle.USER_NULL);
             final int uid = intent.getIntExtra(Intent.EXTRA_UID, -1);
@@ -5043,6 +5108,19 @@
         return getNetworkForRequest(mDefaultRequest.requestId);
     }
 
+    @Nullable
+    private Network getNetwork(@Nullable NetworkAgentInfo nai) {
+        return nai != null ? nai.network : null;
+    }
+
+    private void ensureRunningOnConnectivityServiceThread() {
+        if (mHandler.getLooper().getThread() != Thread.currentThread()) {
+            throw new IllegalStateException(
+                    "Not running on ConnectivityService thread: "
+                            + Thread.currentThread().getName());
+        }
+    }
+
     private boolean isDefaultNetwork(NetworkAgentInfo nai) {
         return nai == getDefaultNetwork();
     }
@@ -5099,7 +5177,7 @@
         if (DBG) log("registerNetworkAgent " + nai);
         final long token = Binder.clearCallingIdentity();
         try {
-            mContext.getSystemService(NetworkStack.class).makeNetworkMonitor(
+            getNetworkStack().makeNetworkMonitor(
                     toStableParcelable(nai.network), name, new NetworkMonitorCallbacks(nai));
         } finally {
             Binder.restoreCallingIdentity(token);
@@ -5111,6 +5189,11 @@
         return nai.network.netId;
     }
 
+    @VisibleForTesting
+    protected NetworkStackClient getNetworkStack() {
+        return NetworkStackClient.getInstance();
+    }
+
     private void handleRegisterNetworkAgent(NetworkAgentInfo nai, INetworkMonitor networkMonitor) {
         nai.onNetworkMonitorCreated(networkMonitor);
         if (VDBG) log("Got NetworkAgent Messenger");
@@ -5669,6 +5752,8 @@
         updateTcpBufferSizes(newNetwork.linkProperties.getTcpBufferSizes());
         mDnsManager.setDefaultDnsSystemProperties(newNetwork.linkProperties.getDnsServers());
         notifyIfacesChangedForNetworkStats();
+        // Fix up the NetworkCapabilities of any VPNs that don't specify underlying networks.
+        updateAllVpnsCapabilities();
     }
 
     private void processListenRequests(NetworkAgentInfo nai, boolean capabilitiesChanged) {
@@ -6108,6 +6193,10 @@
             // doing.
             updateSignalStrengthThresholds(networkAgent, "CONNECT", null);
 
+            if (networkAgent.isVPN()) {
+                updateAllVpnsCapabilities();
+            }
+
             // Consider network even though it is not yet validated.
             final long now = SystemClock.elapsedRealtime();
             rematchNetworkAndRequests(networkAgent, ReapUnvalidatedNetworks.REAP, now);
@@ -6369,7 +6458,11 @@
             success = mVpns.get(user).setUnderlyingNetworks(networks);
         }
         if (success) {
-            mHandler.post(() -> notifyIfacesChangedForNetworkStats());
+            mHandler.post(() -> {
+                // Update VPN's capabilities based on updated underlying network set.
+                updateAllVpnsCapabilities();
+                notifyIfacesChangedForNetworkStats();
+            });
         }
         return success;
     }
diff --git a/services/core/java/com/android/server/ExtconUEventObserver.java b/services/core/java/com/android/server/ExtconUEventObserver.java
index eb59152..775e4c8 100644
--- a/services/core/java/com/android/server/ExtconUEventObserver.java
+++ b/services/core/java/com/android/server/ExtconUEventObserver.java
@@ -68,7 +68,7 @@
      * Subclasses of ExtconUEventObserver should override this method to handle UEvents.
      *
      * @param extconInfo that matches the {@code DEVPATH} of {@code event}
-     * @param event the event
+     * @param event      the event
      */
     protected abstract void onUEvent(ExtconInfo extconInfo, UEvent event);
 
@@ -91,6 +91,9 @@
 
         /** Returns a new list of all external connections whose name matches {@code regex}. */
         public static List<ExtconInfo> getExtconInfos(@Nullable String regex) {
+            if (!extconExists()) {
+                return new ArrayList<>(0);  // Always return a new list.
+            }
             Pattern p = regex == null ? null : Pattern.compile(regex);
             File file = new File("/sys/class/extcon");
             File[] files = file.listFiles();
diff --git a/services/core/java/com/android/server/FgThread.java b/services/core/java/com/android/server/FgThread.java
index fe30057..5d0e308 100644
--- a/services/core/java/com/android/server/FgThread.java
+++ b/services/core/java/com/android/server/FgThread.java
@@ -17,9 +17,12 @@
 package com.android.server;
 
 import android.os.Handler;
+import android.os.HandlerExecutor;
 import android.os.Looper;
 import android.os.Trace;
 
+import java.util.concurrent.Executor;
+
 /**
  * Shared singleton foreground thread for the system.  This is a thread for regular
  * foreground service operations, which shouldn't be blocked by anything running in
@@ -34,6 +37,7 @@
 
     private static FgThread sInstance;
     private static Handler sHandler;
+    private static HandlerExecutor sHandlerExecutor;
 
     private FgThread() {
         super("android.fg", android.os.Process.THREAD_PRIORITY_DEFAULT, true /*allowIo*/);
@@ -48,6 +52,7 @@
             looper.setSlowLogThresholdMs(
                     SLOW_DISPATCH_THRESHOLD_MS, SLOW_DELIVERY_THRESHOLD_MS);
             sHandler = new Handler(sInstance.getLooper());
+            sHandlerExecutor = new HandlerExecutor(sHandler);
         }
     }
 
@@ -64,4 +69,11 @@
             return sHandler;
         }
     }
+
+    public static Executor getExecutor() {
+        synchronized (FgThread.class) {
+            ensureThreadLocked();
+            return sHandlerExecutor;
+        }
+    }
 }
diff --git a/services/core/java/com/android/server/IoThread.java b/services/core/java/com/android/server/IoThread.java
index bfe825a..21fd29c 100644
--- a/services/core/java/com/android/server/IoThread.java
+++ b/services/core/java/com/android/server/IoThread.java
@@ -17,8 +17,11 @@
 package com.android.server;
 
 import android.os.Handler;
+import android.os.HandlerExecutor;
 import android.os.Trace;
 
+import java.util.concurrent.Executor;
+
 /**
  * Shared singleton I/O thread for the system.  This is a thread for non-background
  * service operations that can potential block briefly on network IO operations
@@ -27,6 +30,7 @@
 public final class IoThread extends ServiceThread {
     private static IoThread sInstance;
     private static Handler sHandler;
+    private static HandlerExecutor sHandlerExecutor;
 
     private IoThread() {
         super("android.io", android.os.Process.THREAD_PRIORITY_DEFAULT, true /*allowIo*/);
@@ -38,6 +42,7 @@
             sInstance.start();
             sInstance.getLooper().setTraceTag(Trace.TRACE_TAG_SYSTEM_SERVER);
             sHandler = new Handler(sInstance.getLooper());
+            sHandlerExecutor = new HandlerExecutor(sHandler);
         }
     }
 
@@ -54,4 +59,11 @@
             return sHandler;
         }
     }
+
+    public static Executor getExecutor() {
+        synchronized (IoThread.class) {
+            ensureThreadLocked();
+            return sHandlerExecutor;
+        }
+    }
 }
diff --git a/services/core/java/com/android/server/LocationManagerService.java b/services/core/java/com/android/server/LocationManagerService.java
index 9d92ea2b..5989a46 100644
--- a/services/core/java/com/android/server/LocationManagerService.java
+++ b/services/core/java/com/android/server/LocationManagerService.java
@@ -2136,6 +2136,13 @@
         }
     }
 
+    @Override
+    public String[] getIgnoreSettingsWhitelist() {
+        synchronized (mLock) {
+            return mIgnoreSettingsPackageWhitelist.toArray(new String[0]);
+        }
+    }
+
     @GuardedBy("mLock")
     private boolean isThrottlingExemptLocked(CallerIdentity callerIdentity) {
         if (callerIdentity.mUid == Process.SYSTEM_UID) {
@@ -2794,8 +2801,7 @@
         } catch (RemoteException e) {
             // if the remote process registering the listener is already dead, just swallow the
             // exception and return
-            Log.w(TAG, "Could not link " + linkedListener.mListenerName + " death callback.",
-                    e);
+            Log.w(TAG, "Could not link " + linkedListener.mListenerName + " death callback.", e);
             return false;
         }
     }
@@ -2808,8 +2814,7 @@
         } catch (NoSuchElementException e) {
             // if the death callback isn't connected (it should be...), log error,
             // swallow the exception and return
-            Log.w(TAG, "Could not unlink " + linkedListener.mListenerName + " death callback.",
-                    e);
+            Log.w(TAG, "Could not unlink " + linkedListener.mListenerName + " death callback.", e);
             return false;
         }
     }
diff --git a/services/core/java/com/android/server/UiModeManagerService.java b/services/core/java/com/android/server/UiModeManagerService.java
index 6b57fcd..710a0ba3 100644
--- a/services/core/java/com/android/server/UiModeManagerService.java
+++ b/services/core/java/com/android/server/UiModeManagerService.java
@@ -30,11 +30,13 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
+import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageManager;
 import android.content.res.Configuration;
 import android.content.res.Resources;
 import android.os.BatteryManager;
 import android.os.Binder;
+import android.os.Build;
 import android.os.Handler;
 import android.os.PowerManager;
 import android.os.PowerManager.ServiceType;
@@ -354,8 +356,12 @@
             try {
                 synchronized (mLock) {
                     if (mNightMode != mode) {
-                        Settings.Secure.putIntForUser(getContext().getContentResolver(),
-                                Settings.Secure.UI_NIGHT_MODE, mode, user);
+                        // Only persist setting if not transient night mode or not in car mode
+                        if (!shouldTransientNightWhenInCarMode() || !mCarModeEnabled) {
+                            Settings.Secure.putIntForUser(getContext().getContentResolver(),
+                                    Settings.Secure.UI_NIGHT_MODE, mode, user);
+                        }
+
                         mNightMode = mode;
                         updateLocked(0, 0);
                     }
@@ -438,9 +444,39 @@
         }
     }
 
+    // Night mode settings in car mode are only persisted below Q.
+    // When targeting Q, changes are not saved and night mode will be re-read
+    // from settings when exiting car mode.
+    private boolean shouldTransientNightWhenInCarMode() {
+        int uid = Binder.getCallingUid();
+        PackageManager packageManager = getContext().getPackageManager();
+        String[] packagesForUid = packageManager.getPackagesForUid(uid);
+        if (packagesForUid == null || packagesForUid.length == 0) {
+            return false;
+        }
+
+        try {
+            ApplicationInfo appInfo = packageManager.getApplicationInfoAsUser(
+                    packagesForUid[0], 0, uid);
+
+            return appInfo.targetSdkVersion >= Build.VERSION_CODES.Q;
+        } catch (PackageManager.NameNotFoundException ignored) {
+        }
+
+        return false;
+    }
+
     void setCarModeLocked(boolean enabled, int flags) {
         if (mCarModeEnabled != enabled) {
             mCarModeEnabled = enabled;
+
+            // When transient night mode and exiting car mode, restore night mode from settings
+            if (shouldTransientNightWhenInCarMode() && !mCarModeEnabled) {
+                Context context = getContext();
+                updateNightModeFromSettings(context,
+                        context.getResources(),
+                        UserHandle.getCallingUserId());
+            }
         }
         mCarModeEnableFlags = flags;
     }
@@ -498,7 +534,9 @@
             uiMode |= mNightMode << 4;
         }
 
-        if (mPowerSave) {
+        // Override night mode in power save mode if not transient night mode or not in car mode
+        boolean shouldOverrideNight = !mCarModeEnabled || !shouldTransientNightWhenInCarMode();
+        if (mPowerSave && shouldOverrideNight) {
             uiMode &= ~Configuration.UI_MODE_NIGHT_NO;
             uiMode |= Configuration.UI_MODE_NIGHT_YES;
         }
diff --git a/services/core/java/com/android/server/am/OomAdjuster.java b/services/core/java/com/android/server/am/OomAdjuster.java
index 93a71e5..4e03b72 100644
--- a/services/core/java/com/android/server/am/OomAdjuster.java
+++ b/services/core/java/com/android/server/am/OomAdjuster.java
@@ -1692,7 +1692,8 @@
                         (app.curAdj == ProcessList.PREVIOUS_APP_ADJ ||
                                 app.curAdj == ProcessList.HOME_APP_ADJ)) {
                     mAppCompact.compactAppSome(app);
-                } else if (app.setAdj < ProcessList.CACHED_APP_MIN_ADJ
+                } else if ((app.setAdj < ProcessList.CACHED_APP_MIN_ADJ
+                                || app.setAdj > ProcessList.CACHED_APP_MAX_ADJ)
                         && app.curAdj >= ProcessList.CACHED_APP_MIN_ADJ
                         && app.curAdj <= ProcessList.CACHED_APP_MAX_ADJ) {
                     mAppCompact.compactAppFull(app);
diff --git a/services/core/java/com/android/server/appbinding/finders/CarrierMessagingClientServiceFinder.java b/services/core/java/com/android/server/appbinding/finders/CarrierMessagingClientServiceFinder.java
index 753d3b0..3663518 100644
--- a/services/core/java/com/android/server/appbinding/finders/CarrierMessagingClientServiceFinder.java
+++ b/services/core/java/com/android/server/appbinding/finders/CarrierMessagingClientServiceFinder.java
@@ -99,7 +99,7 @@
     @Override
     public void startMonitoring() {
         mRoleManager.addOnRoleHoldersChangedListenerAsUser(
-                mContext.getMainExecutor(), mRoleHolderChangedListener, UserHandle.ALL);
+                BackgroundThread.getExecutor(), mRoleHolderChangedListener, UserHandle.ALL);
     }
 
     @Override
@@ -120,9 +120,7 @@
 
     private final OnRoleHoldersChangedListener mRoleHolderChangedListener = (role, user) -> {
         if (RoleManager.ROLE_SMS.equals(role)) {
-            BackgroundThread.getHandler().post(() -> {
-                mListener.accept(CarrierMessagingClientServiceFinder.this, user.getIdentifier());
-            });
+            mListener.accept(CarrierMessagingClientServiceFinder.this, user.getIdentifier());
         }
     };
 }
diff --git a/services/core/java/com/android/server/attention/AttentionManagerService.java b/services/core/java/com/android/server/attention/AttentionManagerService.java
index 9a1d7bf..47c9b86 100644
--- a/services/core/java/com/android/server/attention/AttentionManagerService.java
+++ b/services/core/java/com/android/server/attention/AttentionManagerService.java
@@ -104,8 +104,7 @@
 
     @Override
     public void onSwitchUser(int userId) {
-        cancelAndUnbindLocked(peekUserStateLocked(userId),
-                AttentionService.ATTENTION_FAILURE_UNKNOWN);
+        cancelAndUnbindLocked(peekUserStateLocked(userId));
     }
 
     /** Resolves and sets up the attention service if it had not been done yet. */
@@ -152,7 +151,8 @@
         }
 
         synchronized (mLock) {
-            unbindAfterTimeoutLocked();
+            final long now = SystemClock.uptimeMillis();
+            freeIfInactiveLocked();
 
             final UserState userState = getOrCreateCurrentUserStateLocked();
             // lazily start the service, which should be very lightweight to start
@@ -172,7 +172,7 @@
                 try {
                     // throttle frequent requests
                     final AttentionCheckCache attentionCheckCache = userState.mAttentionCheckCache;
-                    if (attentionCheckCache != null && SystemClock.uptimeMillis()
+                    if (attentionCheckCache != null && now
                             < attentionCheckCache.mLastComputed + STALE_AFTER_MILLIS) {
                         callback.onSuccess(requestCode, attentionCheckCache.mResult,
                                 attentionCheckCache.mTimestamp);
@@ -190,6 +190,7 @@
                                 userState.mAttentionCheckCache = new AttentionCheckCache(
                                         SystemClock.uptimeMillis(), result,
                                         timestamp);
+                                userState.mCurrentAttentionCheckIsFulfilled = true;
                             }
                             StatsLog.write(StatsLog.ATTENTION_MANAGER_SERVICE_RESULT_REPORTED,
                                     result);
@@ -198,14 +199,10 @@
                         @Override
                         public void onFailure(int requestCode, int error) {
                             callback.onFailure(requestCode, error);
+                            userState.mCurrentAttentionCheckIsFulfilled = true;
                             StatsLog.write(StatsLog.ATTENTION_MANAGER_SERVICE_RESULT_REPORTED,
                                     error);
                         }
-
-                        @Override
-                        public IBinder asBinder() {
-                            return null;
-                        }
                     });
                 } catch (RemoteException e) {
                     Slog.e(LOG_TAG, "Cannot call into the AttentionService");
@@ -219,7 +216,10 @@
     /** Cancels the specified attention check. */
     public void cancelAttentionCheck(int requestCode) {
         synchronized (mLock) {
-            final UserState userState = getOrCreateCurrentUserStateLocked();
+            final UserState userState = peekCurrentUserStateLocked();
+            if (userState == null) {
+                return;
+            }
             if (userState.mService == null) {
                 if (userState.mPendingAttentionCheck != null
                         && userState.mPendingAttentionCheck.mRequestCode == requestCode) {
@@ -236,8 +236,12 @@
     }
 
     @GuardedBy("mLock")
-    private void unbindAfterTimeoutLocked() {
-        mAttentionHandler.sendEmptyMessageDelayed(AttentionHandler.CONNECTION_EXPIRED,
+    private void freeIfInactiveLocked() {
+        // If we are called here, it means someone used the API again - reset the timer then.
+        mAttentionHandler.removeMessages(AttentionHandler.CHECK_CONNECTION_EXPIRATION);
+
+        // Schedule resources cleanup if no one calls the API again.
+        mAttentionHandler.sendEmptyMessageDelayed(AttentionHandler.CHECK_CONNECTION_EXPIRATION,
                 CONNECTION_TTL_MILLIS);
     }
 
@@ -264,12 +268,14 @@
     }
 
     @GuardedBy("mLock")
-    UserState peekCurrentUserStateLocked() {
+    @Nullable
+    private UserState peekCurrentUserStateLocked() {
         return peekUserStateLocked(ActivityManager.getCurrentUser());
     }
 
     @GuardedBy("mLock")
-    UserState peekUserStateLocked(int userId) {
+    @Nullable
+    private UserState peekUserStateLocked(int userId) {
         return mUserStates.get(userId);
     }
 
@@ -406,6 +412,8 @@
         @GuardedBy("mLock")
         int mCurrentAttentionCheckRequestCode;
         @GuardedBy("mLock")
+        boolean mCurrentAttentionCheckIsFulfilled;
+        @GuardedBy("mLock")
         PendingAttentionCheck mPendingAttentionCheck;
 
         @GuardedBy("mLock")
@@ -501,7 +509,7 @@
     }
 
     private class AttentionHandler extends Handler {
-        private static final int CONNECTION_EXPIRED = 1;
+        private static final int CHECK_CONNECTION_EXPIRATION = 1;
         private static final int ATTENTION_CHECK_TIMEOUT = 2;
 
         AttentionHandler() {
@@ -511,19 +519,26 @@
         public void handleMessage(Message msg) {
             switch (msg.what) {
                 // Do not occupy resources when not in use - unbind proactively.
-                case CONNECTION_EXPIRED: {
+                case CHECK_CONNECTION_EXPIRATION: {
                     for (int i = 0; i < mUserStates.size(); i++) {
-                        cancelAndUnbindLocked(mUserStates.valueAt(i),
-                                AttentionService.ATTENTION_FAILURE_UNKNOWN);
+                        cancelAndUnbindLocked(mUserStates.valueAt(i));
                     }
-
                 }
                 break;
 
                 // Callee is no longer interested in the attention check result - cancel.
                 case ATTENTION_CHECK_TIMEOUT: {
-                    cancelAndUnbindLocked(peekCurrentUserStateLocked(),
-                            AttentionService.ATTENTION_FAILURE_TIMED_OUT);
+                    synchronized (mLock) {
+                        final UserState userState = peekCurrentUserStateLocked();
+                        if (userState != null) {
+                            // If not called back already.
+                            if (!userState.mCurrentAttentionCheckIsFulfilled) {
+                                cancel(userState,
+                                        AttentionService.ATTENTION_FAILURE_TIMED_OUT);
+                            }
+
+                        }
+                    }
                 }
                 break;
 
@@ -533,25 +548,29 @@
         }
     }
 
-    @GuardedBy("mLock")
-    private void cancelAndUnbindLocked(UserState userState,
-            @AttentionFailureCodes int failureCode) {
-        synchronized (mLock) {
-            if (userState != null && userState.mService != null) {
-                try {
-                    userState.mService.cancelAttentionCheck(
-                            userState.mCurrentAttentionCheckRequestCode);
-                } catch (RemoteException e) {
-                    Slog.e(LOG_TAG, "Unable to cancel attention check");
-                }
-
-                if (userState.mPendingAttentionCheck != null) {
-                    userState.mPendingAttentionCheck.cancel(failureCode);
-                }
-                mContext.unbindService(userState.mConnection);
-                userState.mConnection.cleanupService();
-                mUserStates.remove(userState.mUserId);
+    private void cancel(UserState userState, @AttentionFailureCodes int failureCode) {
+        if (userState != null && userState.mService != null) {
+            try {
+                userState.mService.cancelAttentionCheck(
+                        userState.mCurrentAttentionCheckRequestCode);
+            } catch (RemoteException e) {
+                Slog.e(LOG_TAG, "Unable to cancel attention check");
             }
+
+            if (userState.mPendingAttentionCheck != null) {
+                userState.mPendingAttentionCheck.cancel(failureCode);
+            }
+        }
+    }
+
+    @GuardedBy("mLock")
+    private void cancelAndUnbindLocked(UserState userState) {
+        synchronized (mLock) {
+            cancel(userState, AttentionService.ATTENTION_FAILURE_UNKNOWN);
+
+            mContext.unbindService(userState.mConnection);
+            userState.mConnection.cleanupService();
+            mUserStates.remove(userState.mUserId);
         }
     }
 
@@ -563,8 +582,7 @@
         @Override
         public void onReceive(Context context, Intent intent) {
             if (Intent.ACTION_SCREEN_OFF.equals(intent.getAction())) {
-                cancelAndUnbindLocked(peekCurrentUserStateLocked(),
-                        AttentionService.ATTENTION_FAILURE_UNKNOWN);
+                cancelAndUnbindLocked(peekCurrentUserStateLocked());
             }
         }
     }
diff --git a/services/core/java/com/android/server/audio/AudioDeviceBroker.java b/services/core/java/com/android/server/audio/AudioDeviceBroker.java
index 6df60d6..9af57da 100644
--- a/services/core/java/com/android/server/audio/AudioDeviceBroker.java
+++ b/services/core/java/com/android/server/audio/AudioDeviceBroker.java
@@ -280,9 +280,9 @@
                     AudioSystem.FOR_COMMUNICATION, mForcedUseForComm, eventSource);
             sendIILMsgNoDelay(MSG_IIL_SET_FORCE_USE, SENDMSG_QUEUE,
                     AudioSystem.FOR_RECORD, mForcedUseForComm, eventSource);
-            // Un-mute ringtone stream volume
-            mAudioService.setUpdateRingerModeServiceInt();
         }
+        // Un-mute ringtone stream volume
+        mAudioService.postUpdateRingerModeServiceInt();
     }
 
     /*package*/ AudioRoutesInfo startWatchingRoutes(IAudioRoutesObserver observer) {
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index a6643d4..1a62d4f 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -248,6 +248,7 @@
     private static final int MSG_NOTIFY_VOL_EVENT = 22;
     private static final int MSG_DISPATCH_AUDIO_SERVER_STATE = 23;
     private static final int MSG_ENABLE_SURROUND_FORMATS = 24;
+    private static final int MSG_UPDATE_RINGER_MODE = 25;
     // start of messages handled under wakelock
     //   these messages can only be queued, i.e. sent with queueMsgUnderWakeLock(),
     //   and not with sendMsg(..., ..., SENDMSG_QUEUE, ...)
@@ -2720,7 +2721,11 @@
         }
     }
 
-    /*package*/ void setUpdateRingerModeServiceInt() {
+    /*package*/ void postUpdateRingerModeServiceInt() {
+        sendMsg(mAudioHandler, MSG_UPDATE_RINGER_MODE, SENDMSG_QUEUE, 0, 0, null, 0);
+    }
+
+    private void onUpdateRingerModeServiceInt() {
         setRingerModeInt(getRingerModeInternal(), false);
     }
 
@@ -3217,6 +3222,21 @@
         if (!checkAudioSettingsPermission("setSpeakerphoneOn()")) {
             return;
         }
+
+        if (mContext.checkCallingOrSelfPermission(
+                android.Manifest.permission.MODIFY_PHONE_STATE)
+                != PackageManager.PERMISSION_GRANTED) {
+            synchronized (mSetModeDeathHandlers) {
+                for (SetModeDeathHandler h : mSetModeDeathHandlers) {
+                    if (h.getMode() == AudioSystem.MODE_IN_CALL) {
+                        Log.w(TAG, "getMode is call, Permission Denial: setSpeakerphoneOn from pid="
+                                + Binder.getCallingPid() + ", uid=" + Binder.getCallingUid());
+                        return;
+                    }
+                }
+            }
+        }
+
         // for logging only
         final String eventSource = new StringBuilder("setSpeakerphoneOn(").append(on)
                 .append(") from u/pid:").append(Binder.getCallingUid()).append("/")
@@ -4944,6 +4964,10 @@
                 case MSG_ENABLE_SURROUND_FORMATS:
                     onEnableSurroundFormats((ArrayList<Integer>) msg.obj);
                     break;
+
+                case MSG_UPDATE_RINGER_MODE:
+                    onUpdateRingerModeServiceInt();
+                    break;
             }
         }
     }
diff --git a/services/core/java/com/android/server/connectivity/Tethering.java b/services/core/java/com/android/server/connectivity/Tethering.java
index 19bdc09..c91e1a1 100644
--- a/services/core/java/com/android/server/connectivity/Tethering.java
+++ b/services/core/java/com/android/server/connectivity/Tethering.java
@@ -1859,7 +1859,7 @@
         final TetherState tetherState = new TetherState(
                 new IpServer(iface, mLooper, interfaceType, mLog, mNMService, mStatsService,
                              makeControlCallback(), mConfig.enableLegacyDhcpServer,
-                             mDeps.getIpServerDependencies(mContext)));
+                             mDeps.getIpServerDependencies()));
         mTetherStates.put(iface, tetherState);
         tetherState.ipServer.start();
     }
diff --git a/services/core/java/com/android/server/connectivity/Vpn.java b/services/core/java/com/android/server/connectivity/Vpn.java
index e1af81b..da547ea 100644
--- a/services/core/java/com/android/server/connectivity/Vpn.java
+++ b/services/core/java/com/android/server/connectivity/Vpn.java
@@ -241,7 +241,7 @@
         mNetworkCapabilities = new NetworkCapabilities();
         mNetworkCapabilities.addTransportType(NetworkCapabilities.TRANSPORT_VPN);
         mNetworkCapabilities.removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VPN);
-        updateCapabilities();
+        updateCapabilities(null /* defaultNetwork */);
 
         loadAlwaysOnPackage();
     }
@@ -268,22 +268,44 @@
         updateAlwaysOnNotification(detailedState);
     }
 
-    public void updateCapabilities() {
-        final Network[] underlyingNetworks = (mConfig != null) ? mConfig.underlyingNetworks : null;
-        // Only apps targeting Q and above can explicitly declare themselves as metered.
-        final boolean isAlwaysMetered =
-                mIsPackageTargetingAtLeastQ && (mConfig == null || mConfig.isMetered);
-        updateCapabilities(mContext.getSystemService(ConnectivityManager.class), underlyingNetworks,
-                mNetworkCapabilities, isAlwaysMetered);
-
-        if (mNetworkAgent != null) {
-            mNetworkAgent.sendNetworkCapabilities(mNetworkCapabilities);
+    /**
+     * Updates {@link #mNetworkCapabilities} based on current underlying networks and returns a
+     * defensive copy.
+     *
+     * <p>Does not propagate updated capabilities to apps.
+     *
+     * @param defaultNetwork underlying network for VPNs following platform's default
+     */
+    public synchronized NetworkCapabilities updateCapabilities(
+            @Nullable Network defaultNetwork) {
+        if (mConfig == null) {
+            // VPN is not running.
+            return null;
         }
+
+        Network[] underlyingNetworks = mConfig.underlyingNetworks;
+        if (underlyingNetworks == null && defaultNetwork != null) {
+            // null underlying networks means to track the default.
+            underlyingNetworks = new Network[] { defaultNetwork };
+        }
+        // Only apps targeting Q and above can explicitly declare themselves as metered.
+        final boolean isAlwaysMetered = mIsPackageTargetingAtLeastQ && mConfig.isMetered;
+
+        applyUnderlyingCapabilities(
+                mContext.getSystemService(ConnectivityManager.class),
+                underlyingNetworks,
+                mNetworkCapabilities,
+                isAlwaysMetered);
+
+        return new NetworkCapabilities(mNetworkCapabilities);
     }
 
     @VisibleForTesting
-    public static void updateCapabilities(ConnectivityManager cm, Network[] underlyingNetworks,
-            NetworkCapabilities caps, boolean isAlwaysMetered) {
+    public static void applyUnderlyingCapabilities(
+            ConnectivityManager cm,
+            Network[] underlyingNetworks,
+            NetworkCapabilities caps,
+            boolean isAlwaysMetered) {
         int[] transportTypes = new int[] { NetworkCapabilities.TRANSPORT_VPN };
         int downKbps = NetworkCapabilities.LINK_BANDWIDTH_UNSPECIFIED;
         int upKbps = NetworkCapabilities.LINK_BANDWIDTH_UNSPECIFIED;
@@ -296,6 +318,7 @@
         boolean hadUnderlyingNetworks = false;
         if (null != underlyingNetworks) {
             for (Network underlying : underlyingNetworks) {
+                // TODO(b/124469351): Get capabilities directly from ConnectivityService instead.
                 final NetworkCapabilities underlyingCaps = cm.getNetworkCapabilities(underlying);
                 if (underlyingCaps == null) continue;
                 hadUnderlyingNetworks = true;
@@ -1007,9 +1030,8 @@
     }
 
     /**
-     * Establish a VPN network and return the file descriptor of the VPN
-     * interface. This methods returns {@code null} if the application is
-     * revoked or not prepared.
+     * Establish a VPN network and return the file descriptor of the VPN interface. This methods
+     * returns {@code null} if the application is revoked or not prepared.
      *
      * @param config The parameters to configure the network.
      * @return The file descriptor of the VPN interface.
@@ -1101,8 +1123,6 @@
                 // as rules are deleted. This prevents data leakage as the rules are moved over.
                 agentDisconnect(oldNetworkAgent);
             }
-            // Set up VPN's capabilities such as meteredness.
-            updateCapabilities();
 
             if (oldConnection != null) {
                 mContext.unbindService(oldConnection);
@@ -1258,6 +1278,11 @@
         return ranges;
     }
 
+    /**
+     * Updates UID ranges for this VPN and also updates its internal capabilities.
+     *
+     * <p>Should be called on primary ConnectivityService thread.
+     */
     public void onUserAdded(int userHandle) {
         // If the user is restricted tie them to the parent user's VPN
         UserInfo user = UserManager.get(mContext).getUserInfo(userHandle);
@@ -1268,8 +1293,9 @@
                     try {
                         addUserToRanges(existingRanges, userHandle, mConfig.allowedApplications,
                                 mConfig.disallowedApplications);
+                        // ConnectivityService will call {@link #updateCapabilities} and apply
+                        // those for VPN network.
                         mNetworkCapabilities.setUids(existingRanges);
-                        updateCapabilities();
                     } catch (Exception e) {
                         Log.wtf(TAG, "Failed to add restricted user to owner", e);
                     }
@@ -1279,6 +1305,11 @@
         }
     }
 
+    /**
+     * Updates UID ranges for this VPN and also updates its capabilities.
+     *
+     * <p>Should be called on primary ConnectivityService thread.
+     */
     public void onUserRemoved(int userHandle) {
         // clean up if restricted
         UserInfo user = UserManager.get(mContext).getUserInfo(userHandle);
@@ -1290,8 +1321,9 @@
                         final List<UidRange> removedRanges =
                             uidRangesForUser(userHandle, existingRanges);
                         existingRanges.removeAll(removedRanges);
+                        // ConnectivityService will call {@link #updateCapabilities} and
+                        // apply those for VPN network.
                         mNetworkCapabilities.setUids(existingRanges);
-                        updateCapabilities();
                     } catch (Exception e) {
                         Log.wtf(TAG, "Failed to remove restricted user to owner", e);
                     }
@@ -1504,6 +1536,12 @@
         return success;
     }
 
+    /**
+     * Updates underlying network set.
+     *
+     * <p>Note: Does not updates capabilities. Call {@link #updateCapabilities} from
+     * ConnectivityService thread to get updated capabilities.
+     */
     public synchronized boolean setUnderlyingNetworks(Network[] networks) {
         if (!isCallerEstablishedOwnerLocked()) {
             return false;
@@ -1520,7 +1558,6 @@
                 }
             }
         }
-        updateCapabilities();
         return true;
     }
 
diff --git a/services/core/java/com/android/server/connectivity/tethering/TetheringDependencies.java b/services/core/java/com/android/server/connectivity/tethering/TetheringDependencies.java
index 3fddac1..173d786 100644
--- a/services/core/java/com/android/server/connectivity/tethering/TetheringDependencies.java
+++ b/services/core/java/com/android/server/connectivity/tethering/TetheringDependencies.java
@@ -61,8 +61,8 @@
     /**
      * Get dependencies to be used by IpServer.
      */
-    public IpServer.Dependencies getIpServerDependencies(Context context) {
-        return new IpServer.Dependencies(context);
+    public IpServer.Dependencies getIpServerDependencies() {
+        return new IpServer.Dependencies();
     }
 
     /**
diff --git a/services/core/java/com/android/server/display/ColorDisplayService.java b/services/core/java/com/android/server/display/ColorDisplayService.java
index 45f169c..7dd3b36 100644
--- a/services/core/java/com/android/server/display/ColorDisplayService.java
+++ b/services/core/java/com/android/server/display/ColorDisplayService.java
@@ -129,240 +129,9 @@
     private final NightDisplayTintController mNightDisplayTintController =
             new NightDisplayTintController();
 
-    private final TintController mDisplayWhiteBalanceTintController = new TintController() {
-        // Three chromaticity coordinates per color: X, Y, and Z
-        private final int NUM_VALUES_PER_PRIMARY = 3;
-        // Four colors: red, green, blue, and white
-        private final int NUM_DISPLAY_PRIMARIES_VALS = 4 * NUM_VALUES_PER_PRIMARY;
-
-        private final Object mLock = new Object();
-        private int mTemperatureMin;
-        private int mTemperatureMax;
-        private int mTemperatureDefault;
-        private float[] mDisplayNominalWhiteXYZ = new float[NUM_VALUES_PER_PRIMARY];
-        private ColorSpace.Rgb mDisplayColorSpaceRGB;
-        private float[] mChromaticAdaptationMatrix;
-        private int mCurrentColorTemperature;
-        private float[] mCurrentColorTemperatureXYZ;
-        private boolean mSetUp = false;
-        private float[] mMatrixDisplayWhiteBalance = new float[16];
-        private Boolean mIsAvailable;
-
-        @Override
-        public void setUp(Context context, boolean needsLinear) {
-            mSetUp = false;
-            final Resources res = context.getResources();
-
-            ColorSpace.Rgb displayColorSpaceRGB = getDisplayColorSpaceFromSurfaceControl();
-            if (displayColorSpaceRGB == null) {
-                Slog.w(TAG, "Failed to get display color space from SurfaceControl, trying res");
-                displayColorSpaceRGB = getDisplayColorSpaceFromResources(res);
-                if (displayColorSpaceRGB == null) {
-                    Slog.e(TAG, "Failed to get display color space from resources");
-                    return;
-                }
-            }
-
-            final String[] nominalWhiteValues = res.getStringArray(
-                    R.array.config_displayWhiteBalanceDisplayNominalWhite);
-            float[] displayNominalWhiteXYZ = new float[NUM_VALUES_PER_PRIMARY];
-            for (int i = 0; i < nominalWhiteValues.length; i++) {
-                displayNominalWhiteXYZ[i] = Float.parseFloat(nominalWhiteValues[i]);
-            }
-
-            final int colorTemperatureMin = res.getInteger(
-                    R.integer.config_displayWhiteBalanceColorTemperatureMin);
-            if (colorTemperatureMin <= 0) {
-                Slog.e(TAG, "Display white balance minimum temperature must be greater than 0");
-                return;
-            }
-
-            final int colorTemperatureMax = res.getInteger(
-                    R.integer.config_displayWhiteBalanceColorTemperatureMax);
-            if (colorTemperatureMax < colorTemperatureMin) {
-                Slog.e(TAG, "Display white balance max temp must be greater or equal to min");
-                return;
-            }
-
-            final int colorTemperature = res.getInteger(
-                    R.integer.config_displayWhiteBalanceColorTemperatureDefault);
-
-            synchronized (mLock) {
-                mDisplayColorSpaceRGB = displayColorSpaceRGB;
-                mDisplayNominalWhiteXYZ = displayNominalWhiteXYZ;
-                mTemperatureMin = colorTemperatureMin;
-                mTemperatureMax = colorTemperatureMax;
-                mTemperatureDefault = colorTemperature;
-                mSetUp = true;
-            }
-
-            setMatrix(mTemperatureDefault);
-        }
-
-        @Override
-        public float[] getMatrix() {
-            return mSetUp && isActivated() ? mMatrixDisplayWhiteBalance : MATRIX_IDENTITY;
-        }
-
-        @Override
-        public void setMatrix(int cct) {
-            if (!mSetUp) {
-                Slog.w(TAG, "Can't set display white balance temperature: uninitialized");
-                return;
-            }
-
-            if (cct < mTemperatureMin) {
-                Slog.w(TAG, "Requested display color temperature is below allowed minimum");
-                cct = mTemperatureMin;
-            } else if (cct > mTemperatureMax) {
-                Slog.w(TAG, "Requested display color temperature is above allowed maximum");
-                cct = mTemperatureMax;
-            }
-
-            Slog.d(TAG, "setDisplayWhiteBalanceTemperatureMatrix: cct = " + cct);
-
-            synchronized (mLock) {
-                mCurrentColorTemperature = cct;
-
-                // Adapt the display's nominal white point to match the requested CCT value
-                mCurrentColorTemperatureXYZ = ColorSpace.cctToIlluminantdXyz(cct);
-
-                mChromaticAdaptationMatrix =
-                        ColorSpace.chromaticAdaptation(ColorSpace.Adaptation.BRADFORD,
-                                mDisplayNominalWhiteXYZ, mCurrentColorTemperatureXYZ);
-
-                // Convert the adaptation matrix to RGB space
-                float[] result = ColorSpace.mul3x3(mChromaticAdaptationMatrix,
-                        mDisplayColorSpaceRGB.getTransform());
-                result = ColorSpace.mul3x3(mDisplayColorSpaceRGB.getInverseTransform(), result);
-
-                // Normalize the transform matrix to peak white value in RGB space
-                final float adaptedMaxR = result[0] + result[3] + result[6];
-                final float adaptedMaxG = result[1] + result[4] + result[7];
-                final float adaptedMaxB = result[2] + result[5] + result[8];
-                final float denum = Math.max(Math.max(adaptedMaxR, adaptedMaxG), adaptedMaxB);
-                for (int i = 0; i < result.length; i++) {
-                    result[i] /= denum;
-                }
-
-                Matrix.setIdentityM(mMatrixDisplayWhiteBalance, 0);
-                java.lang.System.arraycopy(result, 0, mMatrixDisplayWhiteBalance, 0, 3);
-                java.lang.System.arraycopy(result, 3, mMatrixDisplayWhiteBalance, 4, 3);
-                java.lang.System.arraycopy(result, 6, mMatrixDisplayWhiteBalance, 8, 3);
-            }
-        }
-
-        @Override
-        public int getLevel() {
-            return LEVEL_COLOR_MATRIX_DISPLAY_WHITE_BALANCE;
-        }
-
-        @Override
-        public boolean isAvailable(Context context) {
-            if (mIsAvailable == null) {
-                mIsAvailable = ColorDisplayManager.isDisplayWhiteBalanceAvailable(context);
-            }
-            return mIsAvailable;
-        }
-
-        /**
-         * Format a given matrix into a string.
-         *
-         * @param matrix the matrix to format
-         * @param cols number of columns in the matrix
-         */
-        private String matrixToString(float[] matrix, int cols) {
-            if (matrix == null || cols <= 0) {
-                Slog.e(TAG, "Invalid arguments when formatting matrix to string");
-                return "";
-            }
-
-            StringBuilder sb = new StringBuilder("");
-            for (int i = 0; i < matrix.length; i++) {
-                if (i % cols == 0) {
-                    sb.append("\n    ");
-                }
-                sb.append(String.format("%9.6f ", matrix[i]));
-            }
-            return sb.toString();
-        }
-
-        @Override
-        public void dump(PrintWriter pw) {
-            synchronized (mLock) {
-                pw.println("  mSetUp = " + mSetUp);
-                if (!mSetUp) {
-                    return;
-                }
-
-                pw.println("  isActivated = " + isActivated());
-                pw.println("  mTemperatureMin = " + mTemperatureMin);
-                pw.println("  mTemperatureMax = " + mTemperatureMax);
-                pw.println("  mTemperatureDefault = " + mTemperatureDefault);
-                pw.println("  mCurrentColorTemperature = " + mCurrentColorTemperature);
-                pw.println("  mCurrentColorTemperatureXYZ = " +
-                        matrixToString(mCurrentColorTemperatureXYZ, 3));
-                pw.println("  mDisplayColorSpaceRGB RGB-to-XYZ = " +
-                        matrixToString(mDisplayColorSpaceRGB.getTransform(), 3));
-                pw.println("  mChromaticAdaptationMatrix = " +
-                        matrixToString(mChromaticAdaptationMatrix, 3));
-                pw.println("  mDisplayColorSpaceRGB XYZ-to-RGB = " +
-                        matrixToString(mDisplayColorSpaceRGB.getInverseTransform(), 3));
-                pw.println("  mMatrixDisplayWhiteBalance = " +
-                        matrixToString(mMatrixDisplayWhiteBalance, 4));
-            }
-        }
-
-        private ColorSpace.Rgb makeRgbColorSpaceFromXYZ(float[] redGreenBlueXYZ, float[] whiteXYZ) {
-            return new ColorSpace.Rgb(
-                "Display Color Space",
-                redGreenBlueXYZ,
-                whiteXYZ,
-                2.2f // gamma, unused for display white balance
-            );
-        }
-
-        private ColorSpace.Rgb getDisplayColorSpaceFromSurfaceControl() {
-            final IBinder displayToken = SurfaceControl.getInternalDisplayToken();
-            if (displayToken == null) {
-                return null;
-            }
-
-            DisplayPrimaries primaries = SurfaceControl.getDisplayNativePrimaries(displayToken);
-            if (primaries == null || primaries.red == null || primaries.green == null ||
-                primaries.blue == null || primaries.white == null) {
-                return null;
-            }
-
-            return makeRgbColorSpaceFromXYZ(
-                    new float[] {
-                        primaries.red.X, primaries.red.Y, primaries.red.Z,
-                        primaries.green.X, primaries.green.Y, primaries.green.Z,
-                        primaries.blue.X, primaries.blue.Y, primaries.blue.Z,
-                    },
-                    new float[] { primaries.white.X, primaries.white.Y, primaries.white.Z }
-                    );
-        }
-
-        private ColorSpace.Rgb getDisplayColorSpaceFromResources(Resources res) {
-            final String[] displayPrimariesValues = res.getStringArray(
-                    R.array.config_displayWhiteBalanceDisplayPrimaries);
-            float[] displayRedGreenBlueXYZ =
-                    new float[NUM_DISPLAY_PRIMARIES_VALS - NUM_VALUES_PER_PRIMARY];
-            float[] displayWhiteXYZ = new float[NUM_VALUES_PER_PRIMARY];
-
-            for (int i = 0; i < displayRedGreenBlueXYZ.length; i++) {
-                displayRedGreenBlueXYZ[i] = Float.parseFloat(displayPrimariesValues[i]);
-            }
-
-            for (int i = 0; i < displayWhiteXYZ.length; i++) {
-                displayWhiteXYZ[i] = Float.parseFloat(
-                        displayPrimariesValues[displayRedGreenBlueXYZ.length + i]);
-            }
-
-            return makeRgbColorSpaceFromXYZ(displayRedGreenBlueXYZ, displayWhiteXYZ);
-        }
-    };
+    @VisibleForTesting
+    final DisplayWhiteBalanceTintController mDisplayWhiteBalanceTintController =
+            new DisplayWhiteBalanceTintController();
 
     private final TintController mGlobalSaturationTintController = new TintController() {
 
@@ -860,7 +629,8 @@
         return ldt.isBefore(compareTime) ? ldt.plusDays(1) : ldt;
     }
 
-    private void updateDisplayWhiteBalanceStatus() {
+    @VisibleForTesting
+    void updateDisplayWhiteBalanceStatus() {
         boolean oldActivated = mDisplayWhiteBalanceTintController.isActivated();
         mDisplayWhiteBalanceTintController.setActivated(isDisplayWhiteBalanceSettingEnabled() &&
                 !mNightDisplayTintController.isActivated() &&
@@ -1101,6 +871,7 @@
         pw.println("Display white balance:");
         if (mDisplayWhiteBalanceTintController.isAvailable(getContext())) {
             pw.println("    Activated: " + mDisplayWhiteBalanceTintController.isActivated());
+            mDisplayWhiteBalanceTintController.dump(pw);
         } else {
             pw.println("    Not available");
         }
@@ -1533,6 +1304,244 @@
         }
     }
 
+    final class DisplayWhiteBalanceTintController extends TintController {
+        // Three chromaticity coordinates per color: X, Y, and Z
+        private final int NUM_VALUES_PER_PRIMARY = 3;
+        // Four colors: red, green, blue, and white
+        private final int NUM_DISPLAY_PRIMARIES_VALS = 4 * NUM_VALUES_PER_PRIMARY;
+
+        private final Object mLock = new Object();
+        @VisibleForTesting
+        int mTemperatureMin;
+        @VisibleForTesting
+        int mTemperatureMax;
+        private int mTemperatureDefault;
+        private float[] mDisplayNominalWhiteXYZ = new float[NUM_VALUES_PER_PRIMARY];
+        @VisibleForTesting
+        ColorSpace.Rgb mDisplayColorSpaceRGB;
+        private float[] mChromaticAdaptationMatrix;
+        @VisibleForTesting
+        int mCurrentColorTemperature;
+        private float[] mCurrentColorTemperatureXYZ;
+        private boolean mSetUp = false;
+        private float[] mMatrixDisplayWhiteBalance = new float[16];
+        private Boolean mIsAvailable;
+
+        @Override
+        public void setUp(Context context, boolean needsLinear) {
+            mSetUp = false;
+            final Resources res = context.getResources();
+
+            ColorSpace.Rgb displayColorSpaceRGB = getDisplayColorSpaceFromSurfaceControl();
+            if (displayColorSpaceRGB == null) {
+                Slog.w(TAG, "Failed to get display color space from SurfaceControl, trying res");
+                displayColorSpaceRGB = getDisplayColorSpaceFromResources(res);
+                if (displayColorSpaceRGB == null) {
+                    Slog.e(TAG, "Failed to get display color space from resources");
+                    return;
+                }
+            }
+
+            final String[] nominalWhiteValues = res.getStringArray(
+                    R.array.config_displayWhiteBalanceDisplayNominalWhite);
+            float[] displayNominalWhiteXYZ = new float[NUM_VALUES_PER_PRIMARY];
+            for (int i = 0; i < nominalWhiteValues.length; i++) {
+                displayNominalWhiteXYZ[i] = Float.parseFloat(nominalWhiteValues[i]);
+            }
+
+            final int colorTemperatureMin = res.getInteger(
+                    R.integer.config_displayWhiteBalanceColorTemperatureMin);
+            if (colorTemperatureMin <= 0) {
+                Slog.e(TAG, "Display white balance minimum temperature must be greater than 0");
+                return;
+            }
+
+            final int colorTemperatureMax = res.getInteger(
+                    R.integer.config_displayWhiteBalanceColorTemperatureMax);
+            if (colorTemperatureMax < colorTemperatureMin) {
+                Slog.e(TAG, "Display white balance max temp must be greater or equal to min");
+                return;
+            }
+
+            final int colorTemperature = res.getInteger(
+                    R.integer.config_displayWhiteBalanceColorTemperatureDefault);
+
+            synchronized (mLock) {
+                mDisplayColorSpaceRGB = displayColorSpaceRGB;
+                mDisplayNominalWhiteXYZ = displayNominalWhiteXYZ;
+                mTemperatureMin = colorTemperatureMin;
+                mTemperatureMax = colorTemperatureMax;
+                mTemperatureDefault = colorTemperature;
+                mSetUp = true;
+            }
+
+            setMatrix(mTemperatureDefault);
+        }
+
+        @Override
+        public float[] getMatrix() {
+            return mSetUp && isActivated() ? mMatrixDisplayWhiteBalance : MATRIX_IDENTITY;
+        }
+
+        @Override
+        public void setMatrix(int cct) {
+            if (!mSetUp) {
+                Slog.w(TAG, "Can't set display white balance temperature: uninitialized");
+                return;
+            }
+
+            if (cct < mTemperatureMin) {
+                Slog.w(TAG, "Requested display color temperature is below allowed minimum");
+                cct = mTemperatureMin;
+            } else if (cct > mTemperatureMax) {
+                Slog.w(TAG, "Requested display color temperature is above allowed maximum");
+                cct = mTemperatureMax;
+            }
+
+            Slog.d(TAG, "setDisplayWhiteBalanceTemperatureMatrix: cct = " + cct);
+
+            synchronized (mLock) {
+                mCurrentColorTemperature = cct;
+
+                // Adapt the display's nominal white point to match the requested CCT value
+                mCurrentColorTemperatureXYZ = ColorSpace.cctToIlluminantdXyz(cct);
+
+                mChromaticAdaptationMatrix =
+                        ColorSpace.chromaticAdaptation(ColorSpace.Adaptation.BRADFORD,
+                                mDisplayNominalWhiteXYZ, mCurrentColorTemperatureXYZ);
+
+                // Convert the adaptation matrix to RGB space
+                float[] result = ColorSpace.mul3x3(mChromaticAdaptationMatrix,
+                        mDisplayColorSpaceRGB.getTransform());
+                result = ColorSpace.mul3x3(mDisplayColorSpaceRGB.getInverseTransform(), result);
+
+                // Normalize the transform matrix to peak white value in RGB space
+                final float adaptedMaxR = result[0] + result[3] + result[6];
+                final float adaptedMaxG = result[1] + result[4] + result[7];
+                final float adaptedMaxB = result[2] + result[5] + result[8];
+                final float denum = Math.max(Math.max(adaptedMaxR, adaptedMaxG), adaptedMaxB);
+                for (int i = 0; i < result.length; i++) {
+                    result[i] /= denum;
+                }
+
+                Matrix.setIdentityM(mMatrixDisplayWhiteBalance, 0);
+                java.lang.System.arraycopy(result, 0, mMatrixDisplayWhiteBalance, 0, 3);
+                java.lang.System.arraycopy(result, 3, mMatrixDisplayWhiteBalance, 4, 3);
+                java.lang.System.arraycopy(result, 6, mMatrixDisplayWhiteBalance, 8, 3);
+            }
+        }
+
+        @Override
+        public int getLevel() {
+            return LEVEL_COLOR_MATRIX_DISPLAY_WHITE_BALANCE;
+        }
+
+        @Override
+        public boolean isAvailable(Context context) {
+            if (mIsAvailable == null) {
+                mIsAvailable = ColorDisplayManager.isDisplayWhiteBalanceAvailable(context);
+            }
+            return mIsAvailable;
+        }
+
+        /**
+         * Format a given matrix into a string.
+         *
+         * @param matrix the matrix to format
+         * @param cols number of columns in the matrix
+         */
+        private String matrixToString(float[] matrix, int cols) {
+            if (matrix == null || cols <= 0) {
+                Slog.e(TAG, "Invalid arguments when formatting matrix to string");
+                return "";
+            }
+
+            StringBuilder sb = new StringBuilder("");
+            for (int i = 0; i < matrix.length; i++) {
+                if (i % cols == 0) {
+                    sb.append("\n      ");
+                }
+                sb.append(String.format("%9.6f ", matrix[i]));
+            }
+            return sb.toString();
+        }
+
+        @Override
+        public void dump(PrintWriter pw) {
+            synchronized (mLock) {
+                pw.println("    mSetUp = " + mSetUp);
+                if (!mSetUp) {
+                    return;
+                }
+
+                pw.println("    mTemperatureMin = " + mTemperatureMin);
+                pw.println("    mTemperatureMax = " + mTemperatureMax);
+                pw.println("    mTemperatureDefault = " + mTemperatureDefault);
+                pw.println("    mCurrentColorTemperature = " + mCurrentColorTemperature);
+                pw.println("    mCurrentColorTemperatureXYZ = " +
+                        matrixToString(mCurrentColorTemperatureXYZ, 3));
+                pw.println("    mDisplayColorSpaceRGB RGB-to-XYZ = " +
+                        matrixToString(mDisplayColorSpaceRGB.getTransform(), 3));
+                pw.println("    mChromaticAdaptationMatrix = " +
+                        matrixToString(mChromaticAdaptationMatrix, 3));
+                pw.println("    mDisplayColorSpaceRGB XYZ-to-RGB = " +
+                        matrixToString(mDisplayColorSpaceRGB.getInverseTransform(), 3));
+                pw.println("    mMatrixDisplayWhiteBalance = " +
+                        matrixToString(mMatrixDisplayWhiteBalance, 4));
+            }
+        }
+
+        private ColorSpace.Rgb makeRgbColorSpaceFromXYZ(float[] redGreenBlueXYZ, float[] whiteXYZ) {
+            return new ColorSpace.Rgb(
+                "Display Color Space",
+                redGreenBlueXYZ,
+                whiteXYZ,
+                2.2f // gamma, unused for display white balance
+            );
+        }
+
+        private ColorSpace.Rgb getDisplayColorSpaceFromSurfaceControl() {
+            final IBinder displayToken = SurfaceControl.getInternalDisplayToken();
+            if (displayToken == null) {
+                return null;
+            }
+
+            DisplayPrimaries primaries = SurfaceControl.getDisplayNativePrimaries(displayToken);
+            if (primaries == null || primaries.red == null || primaries.green == null ||
+                primaries.blue == null || primaries.white == null) {
+                return null;
+            }
+
+            return makeRgbColorSpaceFromXYZ(
+                    new float[] {
+                        primaries.red.X, primaries.red.Y, primaries.red.Z,
+                        primaries.green.X, primaries.green.Y, primaries.green.Z,
+                        primaries.blue.X, primaries.blue.Y, primaries.blue.Z,
+                    },
+                    new float[] { primaries.white.X, primaries.white.Y, primaries.white.Z }
+                    );
+        }
+
+        private ColorSpace.Rgb getDisplayColorSpaceFromResources(Resources res) {
+            final String[] displayPrimariesValues = res.getStringArray(
+                    R.array.config_displayWhiteBalanceDisplayPrimaries);
+            float[] displayRedGreenBlueXYZ =
+                    new float[NUM_DISPLAY_PRIMARIES_VALS - NUM_VALUES_PER_PRIMARY];
+            float[] displayWhiteXYZ = new float[NUM_VALUES_PER_PRIMARY];
+
+            for (int i = 0; i < displayRedGreenBlueXYZ.length; i++) {
+                displayRedGreenBlueXYZ[i] = Float.parseFloat(displayPrimariesValues[i]);
+            }
+
+            for (int i = 0; i < displayWhiteXYZ.length; i++) {
+                displayWhiteXYZ[i] = Float.parseFloat(
+                        displayPrimariesValues[displayRedGreenBlueXYZ.length + i]);
+            }
+
+            return makeRgbColorSpaceFromXYZ(displayRedGreenBlueXYZ, displayWhiteXYZ);
+        }
+    };
+
     /**
      * Local service that allows color transforms to be enabled from other system services.
      */
diff --git a/services/core/java/com/android/server/display/DisplayPowerController.java b/services/core/java/com/android/server/display/DisplayPowerController.java
index dc5be6a..15c7ef7 100644
--- a/services/core/java/com/android/server/display/DisplayPowerController.java
+++ b/services/core/java/com/android/server/display/DisplayPowerController.java
@@ -1019,7 +1019,7 @@
         if (mDisplayWhiteBalanceController != null) {
             if (state == Display.STATE_ON && mDisplayWhiteBalanceSettings.isEnabled()) {
                 mDisplayWhiteBalanceController.setEnabled(true);
-                mDisplayWhiteBalanceController.updateScreenColorTemperature();
+                mDisplayWhiteBalanceController.updateDisplayColorTemperature();
             } else {
                 mDisplayWhiteBalanceController.setEnabled(false);
             }
diff --git a/services/core/java/com/android/server/display/whitebalance/DisplayWhiteBalanceController.java b/services/core/java/com/android/server/display/whitebalance/DisplayWhiteBalanceController.java
index b9aa34e8..d95e92b 100644
--- a/services/core/java/com/android/server/display/whitebalance/DisplayWhiteBalanceController.java
+++ b/services/core/java/com/android/server/display/whitebalance/DisplayWhiteBalanceController.java
@@ -29,14 +29,14 @@
 
 /**
  * The DisplayWhiteBalanceController drives display white-balance (automatically correcting the
- * screen color temperature depending on the ambient color temperature).
+ * display color temperature depending on the ambient color temperature).
  *
  * The DisplayWhiteBalanceController:
  * - Uses the AmbientColorTemperatureSensor to detect changes in the ambient color temperature;
  * - Uses the AmbientColorTemperatureFilter to average these changes over time, filter out the
  *   noise, and arrive at an estimate of the actual ambient color temperature;
- * - Uses the DisplayWhiteBalanceThrottler to decide whether the screen color tempearture should be
- *   updated, suppressing changes that are too frequent or too minor.
+ * - Uses the DisplayWhiteBalanceThrottler to decide whether the display color tempearture should
+ *   be updated, suppressing changes that are too frequent or too minor.
  */
 public class DisplayWhiteBalanceController implements
         AmbientSensor.AmbientBrightnessSensor.Callbacks,
@@ -76,8 +76,8 @@
     // Override the ambient color temperature for debugging purposes.
     private float mAmbientColorTemperatureOverride;
 
-    // A piecewise linear relationship between ambient and display color temperatures
-    private Spline.LinearSpline mAmbientToDisplayTemperatureSpline;
+    // A piecewise linear relationship between ambient and display color temperatures.
+    private Spline.LinearSpline mAmbientToDisplayColorTemperatureSpline;
 
     /**
      * @param brightnessSensor
@@ -91,7 +91,7 @@
      *      The filter used to average ambient color temperature changes over time, filter out the
      *      noise and arrive at an estimate of the actual ambient color temperature.
      * @param throttler
-     *      The throttler used to determine whether the new screen color temperature should be
+     *      The throttler used to determine whether the new display color temperature should be
      *      updated or not.
      * @param lowLightAmbientBrightnessThreshold
      *      The ambient brightness threshold beneath which we fall back to a fixed ambient color
@@ -99,6 +99,12 @@
      * @param lowLightAmbientColorTemperature
      *      The ambient color temperature to which we fall back when the ambient brightness drops
      *      beneath a certain threshold.
+     * @param ambientColorTemperatures
+     *      The ambient color tempeartures used to map the ambient color temperature to the display
+     *      color temperature (or null if no mapping is necessary).
+     * @param displayColorTemperatures
+     *      The display color temperatures used to map the ambient color temperature to the display
+     *      color temperature (or null if no mapping is necessary).
      *
      * @throws NullPointerException
      *      - brightnessSensor is null;
@@ -114,7 +120,7 @@
             @NonNull AmbientFilter colorTemperatureFilter,
             @NonNull DisplayWhiteBalanceThrottler throttler,
             float lowLightAmbientBrightnessThreshold, float lowLightAmbientColorTemperature,
-            float[] ambientTemperatures, float[] displayTemperatures) {
+            float[] ambientColorTemperatures, float[] displayColorTemperatures) {
         validateArguments(brightnessSensor, brightnessFilter, colorTemperatureSensor,
                 colorTemperatureFilter, throttler);
         mLoggingEnabled = false;
@@ -134,10 +140,10 @@
         mAmbientColorTemperatureOverride = -1.0f;
 
         try {
-            mAmbientToDisplayTemperatureSpline = new Spline.LinearSpline(ambientTemperatures,
-                    displayTemperatures);
+            mAmbientToDisplayColorTemperatureSpline = new Spline.LinearSpline(
+                    ambientColorTemperatures, displayColorTemperatures);
         } catch (Exception e) {
-            mAmbientToDisplayTemperatureSpline = null;
+            mAmbientToDisplayColorTemperatureSpline = null;
         }
 
         mColorDisplayServiceInternal = LocalServices.getService(ColorDisplayServiceInternal.class);
@@ -160,7 +166,7 @@
     }
 
     /**
-     * Set an object to call back to when the screen color temperature should be updated.
+     * Set an object to call back to when the display color temperature should be updated.
      *
      * @param callbacks
      *      The object to call back to.
@@ -201,7 +207,7 @@
      *
      * This is only applied when the ambient color temperature changes or is updated (in which case
      * it overrides the ambient color temperature estimate); in other words, it doesn't necessarily
-     * change the screen color temperature immediately.
+     * change the display color temperature immediately.
      *
      * @param ambientColorTemperatureOverride
      *      The ambient color temperature override.
@@ -240,9 +246,8 @@
         writer.println("  mLastAmbientColorTemperature=" + mLastAmbientColorTemperature);
         writer.println("  mAmbientColorTemperatureHistory=" + mAmbientColorTemperatureHistory);
         writer.println("  mAmbientColorTemperatureOverride=" + mAmbientColorTemperatureOverride);
-        writer.println("  mAmbientToDisplayTemperatureSpline="
-                + (mAmbientToDisplayTemperatureSpline == null ? "unused" :
-                    mAmbientToDisplayTemperatureSpline));
+        writer.println("  mAmbientToDisplayColorTemperatureSpline="
+                + mAmbientToDisplayColorTemperatureSpline);
     }
 
     @Override // AmbientSensor.AmbientBrightnessSensor.Callbacks
@@ -266,9 +271,9 @@
         final long time = System.currentTimeMillis();
         float ambientColorTemperature = mColorTemperatureFilter.getEstimate(time);
 
-        if (mAmbientToDisplayTemperatureSpline != null) {
+        if (mAmbientToDisplayColorTemperatureSpline != null) {
             ambientColorTemperature =
-                mAmbientToDisplayTemperatureSpline.interpolate(ambientColorTemperature);
+                mAmbientToDisplayColorTemperatureSpline.interpolate(ambientColorTemperature);
         }
 
         final float ambientBrightness = mBrightnessFilter.getEstimate(time);
@@ -290,7 +295,7 @@
             ambientColorTemperature = mAmbientColorTemperatureOverride;
         }
 
-        // When the screen color temperature needs to be updated, we call DisplayPowerController to
+        // When the display color temperature needs to be updated, we call DisplayPowerController to
         // call our updateColorTemperature. The reason we don't call it directly is that we want
         // all changes to the system to happen in a predictable order in DPC's main loop
         // (updatePowerState).
@@ -308,9 +313,9 @@
     }
 
     /**
-     * Updates the screen color temperature.
+     * Updates the display color temperature.
      */
-    public void updateScreenColorTemperature() {
+    public void updateDisplayColorTemperature() {
         float ambientColorTemperature = -1.0f;
 
         // If both the pending and the current ambient color temperatures are -1, it means the DWBC
@@ -353,7 +358,7 @@
          * Called whenever the display white-balance state has changed.
          *
          * Usually, this means the estimated ambient color temperature has changed enough, and the
-         * screen color temperature should be updated; but it is also called by
+         * display color temperature should be updated; but it is also called if settings change.
          */
         void updateWhiteBalance();
     }
diff --git a/services/core/java/com/android/server/display/whitebalance/DisplayWhiteBalanceFactory.java b/services/core/java/com/android/server/display/whitebalance/DisplayWhiteBalanceFactory.java
index 56f4ca3..449f115 100644
--- a/services/core/java/com/android/server/display/whitebalance/DisplayWhiteBalanceFactory.java
+++ b/services/core/java/com/android/server/display/whitebalance/DisplayWhiteBalanceFactory.java
@@ -67,14 +67,14 @@
         final float lowLightAmbientColorTemperature = getFloat(resources,
                 com.android.internal.R.dimen
                 .config_displayWhiteBalanceLowLightAmbientColorTemperature);
-        final float[] ambientTemperatures = getFloatArray(resources,
-                com.android.internal.R.array.config_displayWhiteBalanceAmbientTemperatureValues);
-        final float[] displayTemperatures = getFloatArray(resources,
-                com.android.internal.R.array.config_displayWhiteBalanceDisplayTemperatureValues);
+        final float[] ambientColorTemperatures = getFloatArray(resources,
+                com.android.internal.R.array.config_displayWhiteBalanceAmbientColorTemperatures);
+        final float[] displayColorTempeartures = getFloatArray(resources,
+                com.android.internal.R.array.config_displayWhiteBalanceDisplayColorTemperatures);
         final DisplayWhiteBalanceController controller = new DisplayWhiteBalanceController(
                 brightnessSensor, brightnessFilter, colorTemperatureSensor, colorTemperatureFilter,
                 throttler, lowLightAmbientBrightnessThreshold, lowLightAmbientColorTemperature,
-                ambientTemperatures, displayTemperatures);
+                ambientColorTemperatures, displayColorTempeartures);
         brightnessSensor.setCallbacks(controller);
         colorTemperatureSensor.setCallbacks(controller);
         return controller;
diff --git a/services/core/java/com/android/server/display/whitebalance/DisplayWhiteBalanceThrottler.java b/services/core/java/com/android/server/display/whitebalance/DisplayWhiteBalanceThrottler.java
index c1f0e98..5941bbb 100644
--- a/services/core/java/com/android/server/display/whitebalance/DisplayWhiteBalanceThrottler.java
+++ b/services/core/java/com/android/server/display/whitebalance/DisplayWhiteBalanceThrottler.java
@@ -23,7 +23,7 @@
 
 /**
  * The DisplayWhiteBalanceController uses the DisplayWhiteBalanceThrottler to decide whether the
- * screen color temperature should be updated, suppressing changes that are too frequent or too
+ * display color temperature should be updated, suppressing changes that are too frequent or too
  * minor.
  */
 class DisplayWhiteBalanceThrottler {
diff --git a/services/core/java/com/android/server/location/GnssLocationProvider.java b/services/core/java/com/android/server/location/GnssLocationProvider.java
index e71b156..243b6fe 100644
--- a/services/core/java/com/android/server/location/GnssLocationProvider.java
+++ b/services/core/java/com/android/server/location/GnssLocationProvider.java
@@ -373,6 +373,7 @@
     private final NtpTimeHelper mNtpTimeHelper;
     private final GnssBatchingProvider mGnssBatchingProvider;
     private final GnssGeofenceProvider mGnssGeofenceProvider;
+    // Available only on GNSS HAL 2.0 implementations and later.
     private GnssVisibilityControl mGnssVisibilityControl;
 
     // Handler for processing events
@@ -463,8 +464,8 @@
         }
     };
 
-    // TODO(b/119326010): replace OnSubscriptionsChangedListener with
-    // ACTION_DEFAULT_DATA_SUBSCRIPTION_CHANGED broadcast reseiver.
+    // TODO: replace OnSubscriptionsChangedListener with ACTION_DEFAULT_DATA_SUBSCRIPTION_CHANGED
+    //       broadcast receiver.
     private final OnSubscriptionsChangedListener mOnSubscriptionsChangedListener =
             new OnSubscriptionsChangedListener() {
                 @Override
@@ -676,8 +677,7 @@
         mNtpTimeHelper.onNetworkAvailable();
         if (mDownloadXtraDataPending == STATE_PENDING_NETWORK) {
             if (mSupportsXtra) {
-                // Download only if supported, (prevents an unneccesary on-boot
-                // download)
+                // Download only if supported, (prevents an unnecessary on-boot download)
                 xtraDownloadRequest();
             }
         }
@@ -764,7 +764,7 @@
 
     /** Returns true if the location request is too frequent. */
     private boolean isRequestLocationRateLimited() {
-        // TODO(b/73198123): implement exponential backoff.
+        // TODO: implement exponential backoff.
         return false;
     }
 
diff --git a/services/core/java/com/android/server/location/GnssVisibilityControl.java b/services/core/java/com/android/server/location/GnssVisibilityControl.java
index c3f25bf..20f872a 100644
--- a/services/core/java/com/android/server/location/GnssVisibilityControl.java
+++ b/services/core/java/com/android/server/location/GnssVisibilityControl.java
@@ -24,10 +24,13 @@
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.content.pm.PackageManager;
+import android.database.ContentObserver;
+import android.location.LocationManager;
 import android.os.Handler;
 import android.os.Looper;
 import android.os.PowerManager;
 import android.os.UserHandle;
+import android.provider.Settings;
 import android.text.TextUtils;
 import android.util.Log;
 import android.util.StatsLog;
@@ -70,7 +73,7 @@
     private final Handler mHandler;
     private final Context mContext;
 
-    private boolean mIsMasterLocationSettingsEnabled = true;
+    private boolean mIsDeviceLocationSettingsEnabled;
 
     // Number of non-framework location access proxy apps is expected to be small (< 5).
     private static final int HASH_MAP_INITIAL_CAPACITY_PROXY_APP_TO_LOCATION_PERMISSIONS = 7;
@@ -88,13 +91,9 @@
         mAppOps = mContext.getSystemService(AppOpsManager.class);
         mPackageManager = mContext.getPackageManager();
 
-        // Set to empty proxy app list initially until the configuration properties are loaded.
-        updateNfwLocationAccessProxyAppsInGnssHal();
-
-        // Listen for proxy app package installation, removal events.
-        listenForProxyAppsPackageUpdates();
-
-        // TODO(b/122855984): Handle global location settings on/off.
+        // Complete initialization as the first event to run in mHandler thread. After that,
+        // all object state read/update events run in the mHandler thread.
+        runOnHandler(this::handleInitialize);
     }
 
     void updateProxyApps(List<String> nfwLocationAccessProxyApps) {
@@ -105,10 +104,6 @@
         runOnHandler(() -> handleUpdateProxyApps(nfwLocationAccessProxyApps));
     }
 
-    void masterLocationSettingsUpdated(boolean enabled) {
-        runOnHandler(() -> handleMasterLocationSettingsUpdated(enabled));
-    }
-
     void reportNfwNotification(String proxyAppPackageName, byte protocolStack,
             String otherProtocolStackName, byte requestor, String requestorId, byte responseType,
             boolean inEmergencyMode, boolean isCachedLocation) {
@@ -117,7 +112,19 @@
                         requestor, requestorId, responseType, inEmergencyMode, isCachedLocation)));
     }
 
+    private void handleInitialize() {
+        disableNfwLocationAccess(); // Disable until config properties are loaded.
+        listenForProxyAppsPackageUpdates();
+        listenForDeviceLocationSettingsUpdate();
+        mIsDeviceLocationSettingsEnabled = getDeviceLocationSettings();
+    }
+
+    private boolean getDeviceLocationSettings() {
+        return mContext.getSystemService(LocationManager.class).isLocationEnabled();
+    }
+
     private void listenForProxyAppsPackageUpdates() {
+        // Listen for proxy apps package installation, removal events.
         IntentFilter intentFilter = new IntentFilter();
         intentFilter.addAction(Intent.ACTION_PACKAGE_ADDED);
         intentFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);
@@ -143,11 +150,22 @@
         }, UserHandle.ALL, intentFilter, null, mHandler);
     }
 
+    private void listenForDeviceLocationSettingsUpdate() {
+        mContext.getContentResolver().registerContentObserver(
+                Settings.Secure.getUriFor(Settings.Secure.LOCATION_MODE),
+                true,
+                new ContentObserver(mHandler) {
+                    @Override
+                    public void onChange(boolean selfChange) {
+                        handleDeviceLocationSettingsUpdated();
+                    }
+                }, UserHandle.USER_ALL);
+    }
+
     private void handleProxyAppPackageUpdate(String pkgName, String action) {
         final Boolean locationPermission = mProxyAppToLocationPermissions.get(pkgName);
-        // pkgName is not one of the proxy apps in our list.
         if (locationPermission == null) {
-            return;
+            return; // ignore, pkgName is not one of the proxy apps in our list.
         }
 
         Log.i(TAG, "Proxy app " + pkgName + " package changed: " + action);
@@ -197,15 +215,33 @@
                 return true;
             }
         }
-
         return false;
     }
 
-    private void handleMasterLocationSettingsUpdated(boolean enabled) {
-        mIsMasterLocationSettingsEnabled = enabled;
-        Log.i(TAG, "Master location settings switch changed to "
-                + (enabled ? "enabled" : "disabled"));
-        updateNfwLocationAccessProxyAppsInGnssHal();
+    private void handleDeviceLocationSettingsUpdated() {
+        final boolean enabled = getDeviceLocationSettings();
+        Log.i(TAG, "Device location settings enabled: " + enabled);
+
+        if (mIsDeviceLocationSettingsEnabled == enabled) {
+            return;
+        }
+
+        mIsDeviceLocationSettingsEnabled = enabled;
+        if (!mIsDeviceLocationSettingsEnabled) {
+            disableNfwLocationAccess();
+            return;
+        }
+
+        // When device location settings was disabled, we already set the proxy app list
+        // to empty in GNSS HAL. Update only if the proxy app list is not empty.
+        String[] locationPermissionEnabledProxyApps = getLocationPermissionEnabledProxyApps();
+        if (locationPermissionEnabledProxyApps.length != 0) {
+            setNfwLocationAccessProxyAppsInGnssHal(locationPermissionEnabledProxyApps);
+        }
+    }
+
+    private void disableNfwLocationAccess() {
+        setNfwLocationAccessProxyAppsInGnssHal(NO_LOCATION_ENABLED_PROXY_APPS);
     }
 
     // Represents NfwNotification structure in IGnssVisibilityControlCallback.hal
@@ -316,8 +352,7 @@
             return mPackageManager.getApplicationInfo(pkgName, 0).uid;
         } catch (PackageManager.NameNotFoundException e) {
             if (DEBUG) {
-                Log.d(TAG, "Non-framework location access proxy app "
-                        + pkgName + " is not found.");
+                Log.d(TAG, "Non-framework location access proxy app " + pkgName + " is not found.");
             }
             return null;
         }
@@ -329,8 +364,14 @@
     }
 
     private void updateNfwLocationAccessProxyAppsInGnssHal() {
-        final String[] locationPermissionEnabledProxyApps = shouldDisableNfwLocationAccess()
-                ? NO_LOCATION_ENABLED_PROXY_APPS : getLocationPermissionEnabledProxyApps();
+        if (!mIsDeviceLocationSettingsEnabled) {
+            return; // Keep non-framework location access disabled.
+        }
+        setNfwLocationAccessProxyAppsInGnssHal(getLocationPermissionEnabledProxyApps());
+    }
+
+    private void setNfwLocationAccessProxyAppsInGnssHal(
+            String[] locationPermissionEnabledProxyApps) {
         final String proxyAppsStr = Arrays.toString(locationPermissionEnabledProxyApps);
         Log.i(TAG, "Updating non-framework location access proxy apps in the GNSS HAL to: "
                 + proxyAppsStr);
@@ -341,12 +382,8 @@
         }
     }
 
-    private boolean shouldDisableNfwLocationAccess() {
-        return !mIsMasterLocationSettingsEnabled;
-    }
-
     private String[] getLocationPermissionEnabledProxyApps() {
-        // Get a count of proxy apps with location permission enabled to array creation size.
+        // Get a count of proxy apps with location permission enabled for array creation size.
         int countLocationPermissionEnabledProxyApps = 0;
         for (Boolean hasLocationPermissionEnabled : mProxyAppToLocationPermissions.values()) {
             if (hasLocationPermissionEnabled) {
diff --git a/services/core/java/com/android/server/pm/OWNERS b/services/core/java/com/android/server/pm/OWNERS
index 33b8641..640b155 100644
--- a/services/core/java/com/android/server/pm/OWNERS
+++ b/services/core/java/com/android/server/pm/OWNERS
@@ -19,6 +19,9 @@
 per-file CompilerStats.java = agampe@google.com
 per-file CompilerStats.java = calin@google.com
 per-file CompilerStats.java = ngeoffray@google.com
+per-file DynamicCodeLoggingService.java = agampe@google.com
+per-file DynamicCodeLoggingService.java = calin@google.com
+per-file DynamicCodeLoggingService.java = ngeoffray@google.com
 per-file InstructionSets.java = agampe@google.com
 per-file InstructionSets.java = calin@google.com
 per-file InstructionSets.java = ngeoffray@google.com
diff --git a/services/core/java/com/android/server/pm/PackageInstallerService.java b/services/core/java/com/android/server/pm/PackageInstallerService.java
index 8608349..f5d88e3 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerService.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerService.java
@@ -84,7 +84,7 @@
 import com.android.internal.util.IndentingPrintWriter;
 import com.android.server.IoThread;
 import com.android.server.LocalServices;
-import com.android.server.pm.permission.PermissionManagerInternal;
+import com.android.server.pm.permission.PermissionManagerServiceInternal;
 
 import libcore.io.IoUtils;
 
@@ -131,7 +131,7 @@
     private final Context mContext;
     private final PackageManagerService mPm;
     private final StagingManager mStagingManager;
-    private final PermissionManagerInternal mPermissionManager;
+    private final PermissionManagerServiceInternal mPermissionManager;
 
     private AppOpsManager mAppOps;
 
@@ -189,7 +189,7 @@
     public PackageInstallerService(Context context, PackageManagerService pm, ApexManager am) {
         mContext = context;
         mPm = pm;
-        mPermissionManager = LocalServices.getService(PermissionManagerInternal.class);
+        mPermissionManager = LocalServices.getService(PermissionManagerServiceInternal.class);
 
         mInstallThread = new HandlerThread(TAG);
         mInstallThread.start();
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 61a1a2f..b1c186e 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -238,7 +238,6 @@
 import android.os.storage.StorageManagerInternal;
 import android.os.storage.VolumeInfo;
 import android.os.storage.VolumeRecord;
-import android.permission.PermissionControllerManager;
 import android.provider.DeviceConfig;
 import android.provider.MediaStore;
 import android.provider.Settings.Global;
@@ -291,7 +290,6 @@
 import com.android.internal.util.FastXmlSerializer;
 import com.android.internal.util.IndentingPrintWriter;
 import com.android.internal.util.Preconditions;
-import com.android.internal.util.XmlUtils;
 import com.android.server.AttributeCache;
 import com.android.server.DeviceIdleController;
 import com.android.server.EventLogTags;
@@ -315,9 +313,9 @@
 import com.android.server.pm.permission.BasePermission;
 import com.android.server.pm.permission.DefaultPermissionGrantPolicy;
 import com.android.server.pm.permission.DefaultPermissionGrantPolicy.DefaultPermissionGrantedCallback;
-import com.android.server.pm.permission.PermissionManagerInternal;
-import com.android.server.pm.permission.PermissionManagerInternal.PermissionCallback;
 import com.android.server.pm.permission.PermissionManagerService;
+import com.android.server.pm.permission.PermissionManagerServiceInternal;
+import com.android.server.pm.permission.PermissionManagerServiceInternal.PermissionCallback;
 import com.android.server.pm.permission.PermissionsState;
 import com.android.server.security.VerityUtils;
 import com.android.server.storage.DeviceStorageMonitorInternal;
@@ -371,7 +369,6 @@
 import java.util.concurrent.TimeUnit;
 import java.util.concurrent.atomic.AtomicBoolean;
 import java.util.concurrent.atomic.AtomicInteger;
-import java.util.concurrent.atomic.AtomicReference;
 import java.util.function.BiConsumer;
 import java.util.function.Consumer;
 import java.util.function.Predicate;
@@ -445,8 +442,6 @@
     private static final boolean ENABLE_FREE_CACHE_V2 =
             SystemProperties.getBoolean("fw.free_cache_v2", true);
 
-    private static final long BACKUP_TIMEOUT_MILLIS = TimeUnit.SECONDS.toMillis(60);
-
     private static final String PRECOMPILE_LAYOUTS = "pm.precompile_layouts";
 
     private static final int RADIO_UID = Process.PHONE_UID;
@@ -940,7 +935,7 @@
 
     // TODO remove this and go through mPermissonManager directly
     final DefaultPermissionGrantPolicy mDefaultPermissionPolicy;
-    private final PermissionManagerInternal mPermissionManager;
+    private final PermissionManagerServiceInternal mPermissionManager;
 
     private final ComponentResolver mComponentResolver;
     // List of packages names to keep cached, even if they are uninstalled for all users
@@ -1940,9 +1935,10 @@
                             }
                         }
 
-                        // We may also need to apply pending (restored) runtime
-                        // permission grants within these users.
-                        mSettings.applyPendingPermissionGrantsLPw(packageName, userId);
+                        // We may also need to apply pending (restored) runtime permission grants
+                        // within these users.
+                        mPermissionManager.restoreDelayedRuntimePermissions(packageName,
+                                UserHandle.of(userId));
 
                         // Persistent preferred activity might have came into effect due to this
                         // install.
@@ -18825,7 +18821,7 @@
             // permission as requiring a review as this is the initial state.
             int flags = 0;
             if (ps.pkg.applicationInfo.targetSdkVersion < Build.VERSION_CODES.M && bp.isRuntime()) {
-                flags |= FLAG_PERMISSION_REVIEW_REQUIRED;
+                flags |= FLAG_PERMISSION_REVIEW_REQUIRED | FLAG_PERMISSION_REVOKE_ON_UPGRADE;
             }
             if (permissionsState.updatePermissionFlags(bp, userId, userSettableMask, flags)) {
                 if (hasInstallState) {
@@ -19665,139 +19661,6 @@
     }
 
     @Override
-    public byte[] getPermissionGrantBackup(int userId) {
-        if (Binder.getCallingUid() != Process.SYSTEM_UID) {
-            throw new SecurityException("Only the system may call getPermissionGrantBackup()");
-        }
-
-        AtomicReference<byte[]> backup = new AtomicReference<>();
-        mContext.getSystemService(PermissionControllerManager.class).getRuntimePermissionBackup(
-                UserHandle.of(userId), mContext.getMainExecutor(), (b) -> {
-                    synchronized (backup) {
-                        backup.set(b);
-                        backup.notifyAll();
-                    }
-                });
-
-        long start = System.currentTimeMillis();
-        synchronized (backup) {
-            while (backup.get() == null) {
-                long timeLeft = start + BACKUP_TIMEOUT_MILLIS - System.currentTimeMillis();
-                if (timeLeft <= 0) {
-                    return null;
-                }
-
-                try {
-                    backup.wait(timeLeft);
-                } catch (InterruptedException ignored) {
-                    return null;
-                }
-            }
-        }
-
-        return backup.get();
-    }
-
-    @Override
-    public void restorePermissionGrants(byte[] backup, int userId) {
-        if (Binder.getCallingUid() != Process.SYSTEM_UID) {
-            throw new SecurityException("Only the system may call restorePermissionGrants()");
-        }
-
-        try {
-            final XmlPullParser parser = Xml.newPullParser();
-            parser.setInput(new ByteArrayInputStream(backup), StandardCharsets.UTF_8.name());
-            restoreFromXml(parser, userId, TAG_PERMISSION_BACKUP,
-                    (parser1, userId1) -> {
-                        synchronized (mPackages) {
-                            processRestoredPermissionGrantsLPr(parser1, userId1);
-                        }
-                    });
-        } catch (Exception e) {
-            if (DEBUG_BACKUP) {
-                Slog.e(TAG, "Exception restoring preferred activities: " + e.getMessage());
-            }
-        }
-    }
-
-    @GuardedBy("mPackages")
-    private void processRestoredPermissionGrantsLPr(XmlPullParser parser, int userId)
-            throws XmlPullParserException, IOException {
-        String pkgName = null;
-        int outerDepth = parser.getDepth();
-        int type;
-        while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
-                && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
-            if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
-                continue;
-            }
-
-            final String tagName = parser.getName();
-            if (tagName.equals(TAG_GRANT)) {
-                pkgName = parser.getAttributeValue(null, ATTR_PACKAGE_NAME);
-                if (DEBUG_BACKUP) {
-                    Slog.v(TAG, "+++ Restoring grants for package " + pkgName);
-                }
-            } else if (tagName.equals(TAG_PERMISSION)) {
-
-                final boolean isGranted = "true".equals(parser.getAttributeValue(null, ATTR_IS_GRANTED));
-                final String permName = parser.getAttributeValue(null, ATTR_PERMISSION_NAME);
-
-                int newFlagSet = 0;
-                if ("true".equals(parser.getAttributeValue(null, ATTR_USER_SET))) {
-                    newFlagSet |= FLAG_PERMISSION_USER_SET;
-                }
-                if ("true".equals(parser.getAttributeValue(null, ATTR_USER_FIXED))) {
-                    newFlagSet |= FLAG_PERMISSION_USER_FIXED;
-                }
-                if ("true".equals(parser.getAttributeValue(null, ATTR_REVOKE_ON_UPGRADE))) {
-                    newFlagSet |= FLAG_PERMISSION_REVOKE_ON_UPGRADE;
-                }
-                if (DEBUG_BACKUP) {
-                    Slog.v(TAG, "  + Restoring grant:"
-                            + " pkg=" + pkgName
-                            + " perm=" + permName
-                            + " granted=" + isGranted
-                            + " bits=0x" + Integer.toHexString(newFlagSet));
-                }
-                final PackageSetting ps = mSettings.mPackages.get(pkgName);
-                if (ps != null) {
-                    // Already installed so we apply the grant immediately
-                    if (DEBUG_BACKUP) {
-                        Slog.v(TAG, "        + already installed; applying");
-                    }
-                    PermissionsState perms = ps.getPermissionsState();
-                    BasePermission bp =
-                            (BasePermission) mPermissionManager.getPermissionTEMP(permName);
-                    if (bp != null) {
-                        if (isGranted) {
-                            perms.grantRuntimePermission(bp, userId);
-                        }
-                        if (newFlagSet != 0) {
-                            perms.updatePermissionFlags(
-                                    bp, userId, USER_RUNTIME_GRANT_MASK, newFlagSet);
-                        }
-                    }
-                } else {
-                    // Need to wait for post-restore install to apply the grant
-                    if (DEBUG_BACKUP) {
-                        Slog.v(TAG, "        - not yet installed; saving for later");
-                    }
-                    mSettings.processRestoredPermissionGrantLPr(pkgName, permName,
-                            isGranted, newFlagSet, userId);
-                }
-            } else {
-                PackageManagerService.reportSettingsProblem(Log.WARN,
-                        "Unknown element under <" + TAG_PERMISSION_BACKUP + ">: " + tagName);
-                XmlUtils.skipCurrentTag(parser);
-            }
-        }
-
-        scheduleWriteSettingsLocked();
-        mSettings.writeRuntimePermissionsForUserLPr(userId, false);
-    }
-
-    @Override
     public void addCrossProfileIntentFilter(IntentFilter intentFilter, String ownerPackage,
             int sourceUserId, int targetUserId, int flags) {
         mContext.enforceCallingOrSelfPermission(
@@ -21285,10 +21148,6 @@
                 }
             }
 
-            if (!checkin && dumpState.isDumping(DumpState.DUMP_PERMISSIONS) && packageName == null) {
-                mSettings.dumpRestoredPermissionGrantsLPr(pw, dumpState);
-            }
-
             if (!checkin && dumpState.isDumping(DumpState.DUMP_FROZEN) && packageName == null) {
                 // XXX should handle packageName != null by dumping only install data that
                 // the given package is involved with.
diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java
index 975ffb2..92fe377 100644
--- a/services/core/java/com/android/server/pm/Settings.java
+++ b/services/core/java/com/android/server/pm/Settings.java
@@ -19,10 +19,6 @@
 import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DEFAULT;
 import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DISABLED;
 import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_ENABLED;
-import static android.content.pm.PackageManager.FLAG_PERMISSION_REVOKE_ON_UPGRADE;
-import static android.content.pm.PackageManager.FLAG_PERMISSION_REVOKE_WHEN_REQUESTED;
-import static android.content.pm.PackageManager.FLAG_PERMISSION_USER_FIXED;
-import static android.content.pm.PackageManager.FLAG_PERMISSION_USER_SET;
 import static android.content.pm.PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE;
 import static android.content.pm.PackageManager.INSTALL_FAILED_SHARED_USER_INCOMPATIBLE;
 import static android.content.pm.PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS;
@@ -249,23 +245,6 @@
     private static final String ATTR_SDK_VERSION = "sdkVersion";
     private static final String ATTR_DATABASE_VERSION = "databaseVersion";
 
-    // Bookkeeping for restored permission grants
-    private static final String TAG_RESTORED_RUNTIME_PERMISSIONS = "restored-perms";
-    // package name: ATTR_PACKAGE_NAME
-    private static final String TAG_PERMISSION_ENTRY = "perm";
-    // permission name: ATTR_NAME
-    // permission granted (boolean): ATTR_GRANTED
-    private static final String ATTR_USER_SET = "set";
-    private static final String ATTR_USER_FIXED = "fixed";
-    private static final String ATTR_REVOKE_ON_UPGRADE = "rou";
-    private static final String ATTR_REVOKE_WHEN_REQUESTED = "rwr";
-
-    // Flag mask of restored permission grants that are applied at install time
-    private static final int USER_RUNTIME_GRANT_MASK =
-            FLAG_PERMISSION_USER_SET
-            | FLAG_PERMISSION_USER_FIXED
-            | FLAG_PERMISSION_REVOKE_ON_UPGRADE;
-
     private final Object mLock;
 
     private final RuntimePermissionPersistence mRuntimePermissionsPersistence;
@@ -303,26 +282,6 @@
         int[] excludedUserIds;
     }
 
-    // Bookkeeping for restored user permission grants
-    final class RestoredPermissionGrant {
-        String permissionName;
-        boolean granted;
-        int grantBits;
-
-        RestoredPermissionGrant(String name, boolean isGranted, int theGrantBits) {
-            permissionName = name;
-            granted = isGranted;
-            grantBits = theGrantBits;
-        }
-    }
-
-    // This would be more compact as a flat array of restored grants or something, but we
-    // may have quite a few, especially during early device lifetime, and avoiding all those
-    // linear lookups will be important.
-    private final SparseArray<ArrayMap<String, ArraySet<RestoredPermissionGrant>>>
-            mRestoredUserGrants =
-                new SparseArray<ArrayMap<String, ArraySet<RestoredPermissionGrant>>>();
-
     private static int mFirstAvailableUid = 0;
 
     /** Map from volume UUID to {@link VersionInfo} */
@@ -461,43 +420,6 @@
         return mRenamedPackages.put(pkgName, origPkgName);
     }
 
-    void applyPendingPermissionGrantsLPw(String packageName, int userId) {
-        ArrayMap<String, ArraySet<RestoredPermissionGrant>> grantsByPackage =
-                mRestoredUserGrants.get(userId);
-        if (grantsByPackage == null || grantsByPackage.size() == 0) {
-            return;
-        }
-
-        ArraySet<RestoredPermissionGrant> grants = grantsByPackage.get(packageName);
-        if (grants == null || grants.size() == 0) {
-            return;
-        }
-
-        final PackageSetting ps = mPackages.get(packageName);
-        if (ps == null) {
-            Slog.e(TAG, "Can't find supposedly installed package " + packageName);
-            return;
-        }
-        final PermissionsState perms = ps.getPermissionsState();
-
-        for (RestoredPermissionGrant grant : grants) {
-            BasePermission bp = mPermissions.getPermission(grant.permissionName);
-            if (bp != null) {
-                if (grant.granted) {
-                    perms.grantRuntimePermission(bp, userId);
-                }
-                perms.updatePermissionFlags(bp, userId, USER_RUNTIME_GRANT_MASK, grant.grantBits);
-            }
-        }
-
-        // And remove it from the pending-grant bookkeeping
-        grantsByPackage.remove(packageName);
-        if (grantsByPackage.size() < 1) {
-            mRestoredUserGrants.remove(userId);
-        }
-        writeRuntimePermissionsForUserLPr(userId, false);
-    }
-
     public boolean canPropagatePermissionToInstantApp(String permName) {
         return mPermissions.canPropagatePermissionToInstantApp(permName);
     }
@@ -1982,13 +1904,6 @@
         }
     }
 
-    // Specifically for backup/restore
-    public void processRestoredPermissionGrantLPr(String pkgName, String permission,
-            boolean isGranted, int restoredFlagSet, int userId) {
-        mRuntimePermissionsPersistence.rememberRestoredUserGrantLPr(
-                pkgName, permission, isGranted, restoredFlagSet, userId);
-    }
-
     void writeDefaultAppsLPr(XmlSerializer serializer, int userId)
             throws IllegalArgumentException, IllegalStateException, IOException {
         serializer.startTag(null, TAG_DEFAULT_APPS);
@@ -5014,51 +4929,6 @@
         pw.print(mReadMessages.toString());
     }
 
-    void dumpRestoredPermissionGrantsLPr(PrintWriter pw, DumpState dumpState) {
-        if (mRestoredUserGrants.size() > 0) {
-            pw.println();
-            pw.println("Restored (pending) permission grants:");
-            for (int userIndex = 0; userIndex < mRestoredUserGrants.size(); userIndex++) {
-                ArrayMap<String, ArraySet<RestoredPermissionGrant>> grantsByPackage =
-                        mRestoredUserGrants.valueAt(userIndex);
-                if (grantsByPackage != null && grantsByPackage.size() > 0) {
-                    final int userId = mRestoredUserGrants.keyAt(userIndex);
-                    pw.print("  User "); pw.println(userId);
-
-                    for (int pkgIndex = 0; pkgIndex < grantsByPackage.size(); pkgIndex++) {
-                        ArraySet<RestoredPermissionGrant> grants = grantsByPackage.valueAt(pkgIndex);
-                        if (grants != null && grants.size() > 0) {
-                            final String pkgName = grantsByPackage.keyAt(pkgIndex);
-                            pw.print("    "); pw.print(pkgName); pw.println(" :");
-
-                            for (RestoredPermissionGrant g : grants) {
-                                pw.print("      ");
-                                pw.print(g.permissionName);
-                                if (g.granted) {
-                                    pw.print(" GRANTED");
-                                }
-                                if ((g.grantBits&FLAG_PERMISSION_USER_SET) != 0) {
-                                    pw.print(" user_set");
-                                }
-                                if ((g.grantBits&FLAG_PERMISSION_USER_FIXED) != 0) {
-                                    pw.print(" user_fixed");
-                                }
-                                if ((g.grantBits&FLAG_PERMISSION_REVOKE_ON_UPGRADE) != 0) {
-                                    pw.print(" revoke_on_upgrade");
-                                }
-                                if ((g.grantBits & FLAG_PERMISSION_REVOKE_WHEN_REQUESTED) != 0) {
-                                    pw.print(" revoke_when_requested");
-                                }
-                                pw.println();
-                            }
-                        }
-                    }
-                }
-            }
-            pw.println();
-        }
-    }
-
     private static void dumpSplitNames(PrintWriter pw, PackageParser.Package pkg) {
         if (pkg == null) {
             pw.print("unknown");
@@ -5328,55 +5198,6 @@
 
                 serializer.endTag(null, TAG_RUNTIME_PERMISSIONS);
 
-                // Now any restored permission grants that are waiting for the apps
-                // in question to be installed.  These are stored as per-package
-                // TAG_RESTORED_RUNTIME_PERMISSIONS blocks, each containing some
-                // number of individual permission grant entities.
-                if (mRestoredUserGrants.get(userId) != null) {
-                    ArrayMap<String, ArraySet<RestoredPermissionGrant>> restoredGrants =
-                            mRestoredUserGrants.get(userId);
-                    if (restoredGrants != null) {
-                        final int pkgCount = restoredGrants.size();
-                        for (int i = 0; i < pkgCount; i++) {
-                            final ArraySet<RestoredPermissionGrant> pkgGrants =
-                                    restoredGrants.valueAt(i);
-                            if (pkgGrants != null && pkgGrants.size() > 0) {
-                                final String pkgName = restoredGrants.keyAt(i);
-                                serializer.startTag(null, TAG_RESTORED_RUNTIME_PERMISSIONS);
-                                serializer.attribute(null, ATTR_PACKAGE_NAME, pkgName);
-
-                                final int N = pkgGrants.size();
-                                for (int z = 0; z < N; z++) {
-                                    RestoredPermissionGrant g = pkgGrants.valueAt(z);
-                                    serializer.startTag(null, TAG_PERMISSION_ENTRY);
-                                    serializer.attribute(null, ATTR_NAME, g.permissionName);
-
-                                    if (g.granted) {
-                                        serializer.attribute(null, ATTR_GRANTED, "true");
-                                    }
-
-                                    if ((g.grantBits&FLAG_PERMISSION_USER_SET) != 0) {
-                                        serializer.attribute(null, ATTR_USER_SET, "true");
-                                    }
-                                    if ((g.grantBits&FLAG_PERMISSION_USER_FIXED) != 0) {
-                                        serializer.attribute(null, ATTR_USER_FIXED, "true");
-                                    }
-                                    if ((g.grantBits&FLAG_PERMISSION_REVOKE_ON_UPGRADE) != 0) {
-                                        serializer.attribute(null, ATTR_REVOKE_ON_UPGRADE, "true");
-                                    }
-                                    if ((g.grantBits & FLAG_PERMISSION_REVOKE_WHEN_REQUESTED)
-                                            != 0) {
-                                        serializer.attribute(null, ATTR_REVOKE_WHEN_REQUESTED,
-                                                "true");
-                                    }
-                                    serializer.endTag(null, TAG_PERMISSION_ENTRY);
-                                }
-                                serializer.endTag(null, TAG_RESTORED_RUNTIME_PERMISSIONS);
-                            }
-                        }
-                    }
-                }
-
                 serializer.endDocument();
                 destination.finishWrite(out);
 
@@ -5455,29 +5276,6 @@
             }
         }
 
-        // Backup/restore support
-
-        public void rememberRestoredUserGrantLPr(String pkgName, String permission,
-                boolean isGranted, int restoredFlagSet, int userId) {
-            // This change will be remembered at write-settings time
-            ArrayMap<String, ArraySet<RestoredPermissionGrant>> grantsByPackage =
-                    mRestoredUserGrants.get(userId);
-            if (grantsByPackage == null) {
-                grantsByPackage = new ArrayMap<String, ArraySet<RestoredPermissionGrant>>();
-                mRestoredUserGrants.put(userId, grantsByPackage);
-            }
-
-            ArraySet<RestoredPermissionGrant> grants = grantsByPackage.get(pkgName);
-            if (grants == null) {
-                grants = new ArraySet<RestoredPermissionGrant>();
-                grantsByPackage.put(pkgName, grants);
-            }
-
-            RestoredPermissionGrant grant = new RestoredPermissionGrant(permission,
-                    isGranted, restoredFlagSet);
-            grants.add(grant);
-        }
-
         // Private internals
 
         @GuardedBy("Settings.this.mLock")
@@ -5520,50 +5318,6 @@
                         }
                         parsePermissionsLPr(parser, sus.getPermissionsState(), userId);
                     } break;
-
-                    case TAG_RESTORED_RUNTIME_PERMISSIONS: {
-                        final String pkgName = parser.getAttributeValue(null, ATTR_PACKAGE_NAME);
-                        parseRestoredRuntimePermissionsLPr(parser, pkgName, userId);
-                    } break;
-                }
-            }
-        }
-
-        private void parseRestoredRuntimePermissionsLPr(XmlPullParser parser,
-                final String pkgName, final int userId) throws IOException, XmlPullParserException {
-            final int outerDepth = parser.getDepth();
-            int type;
-            while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
-                    && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
-                if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
-                    continue;
-                }
-
-                switch (parser.getName()) {
-                    case TAG_PERMISSION_ENTRY: {
-                        final String permName = parser.getAttributeValue(null, ATTR_NAME);
-                        final boolean isGranted = "true".equals(
-                                parser.getAttributeValue(null, ATTR_GRANTED));
-
-                        int permBits = 0;
-                        if ("true".equals(parser.getAttributeValue(null, ATTR_USER_SET))) {
-                            permBits |= FLAG_PERMISSION_USER_SET;
-                        }
-                        if ("true".equals(parser.getAttributeValue(null, ATTR_USER_FIXED))) {
-                            permBits |= FLAG_PERMISSION_USER_FIXED;
-                        }
-                        if ("true".equals(parser.getAttributeValue(null, ATTR_REVOKE_ON_UPGRADE))) {
-                            permBits |= FLAG_PERMISSION_REVOKE_ON_UPGRADE;
-                        }
-                        if ("true".equals(parser.getAttributeValue(null,
-                                ATTR_REVOKE_WHEN_REQUESTED))) {
-                            permBits |= FLAG_PERMISSION_REVOKE_WHEN_REQUESTED;
-                        }
-
-                        if (isGranted || permBits != 0) {
-                            rememberRestoredUserGrantLPr(pkgName, permName, isGranted, permBits, userId);
-                        }
-                    } break;
                 }
             }
         }
diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
index 38940d6..f56b984 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
@@ -30,6 +30,7 @@
 import static android.content.pm.PackageManager.FLAG_PERMISSION_GRANTED_BY_DEFAULT;
 import static android.content.pm.PackageManager.FLAG_PERMISSION_POLICY_FIXED;
 import static android.content.pm.PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED;
+import static android.content.pm.PackageManager.FLAG_PERMISSION_REVOKE_ON_UPGRADE;
 import static android.content.pm.PackageManager.FLAG_PERMISSION_REVOKE_WHEN_REQUESTED;
 import static android.content.pm.PackageManager.FLAG_PERMISSION_SYSTEM_FIXED;
 import static android.content.pm.PackageManager.FLAG_PERMISSION_USER_FIXED;
@@ -43,6 +44,9 @@
 import static com.android.server.pm.PackageManagerService.DEBUG_PERMISSIONS;
 import static com.android.server.pm.PackageManagerService.DEBUG_REMOVE;
 import static com.android.server.pm.PackageManagerService.PLATFORM_PACKAGE_NAME;
+import static com.android.server.pm.permission.PermissionsState.PERMISSION_OPERATION_FAILURE;
+
+import static java.util.concurrent.TimeUnit.SECONDS;
 
 import android.Manifest;
 import android.annotation.NonNull;
@@ -69,7 +73,9 @@
 import android.os.UserManagerInternal;
 import android.os.storage.StorageManager;
 import android.os.storage.StorageManagerInternal;
+import android.permission.PermissionControllerManager;
 import android.permission.PermissionManager;
+import android.permission.PermissionManagerInternal;
 import android.text.TextUtils;
 import android.util.ArrayMap;
 import android.util.ArraySet;
@@ -77,6 +83,7 @@
 import android.util.Log;
 import android.util.Slog;
 import android.util.SparseArray;
+import android.util.SparseBooleanArray;
 
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.logging.MetricsLogger;
@@ -91,9 +98,8 @@
 import com.android.server.pm.PackageSetting;
 import com.android.server.pm.SharedUserSetting;
 import com.android.server.pm.UserManagerService;
-import com.android.server.pm.permission.DefaultPermissionGrantPolicy
-        .DefaultPermissionGrantedCallback;
-import com.android.server.pm.permission.PermissionManagerInternal.PermissionCallback;
+import com.android.server.pm.permission.DefaultPermissionGrantPolicy.DefaultPermissionGrantedCallback;
+import com.android.server.pm.permission.PermissionManagerServiceInternal.PermissionCallback;
 import com.android.server.pm.permission.PermissionsState.PermissionState;
 
 import libcore.util.EmptyArray;
@@ -106,6 +112,10 @@
 import java.util.Map;
 import java.util.Objects;
 import java.util.Set;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
 
 /**
  * Manages all permissions and handles permissions related tasks.
@@ -122,6 +132,8 @@
     /** Permission grant: grant as runtime a permission that was granted as an install time one. */
     private static final int GRANT_UPGRADE = 4;
 
+    private static final long BACKUP_TIMEOUT_MILLIS = SECONDS.toMillis(60);
+
     /** Cap the size of permission trees that 3rd party apps can define; in characters of text */
     private static final int MAX_PERMISSION_TREE_FOOTPRINT = 32768;
     /** Empty array to avoid allocations */
@@ -146,6 +158,9 @@
     /** Internal connection to the user manager */
     private final UserManagerInternal mUserManagerInt;
 
+    /** Permission controller: User space permission management */
+    private PermissionControllerManager mPermissionControllerManager;
+
     /** Default permission policy to provide proper behaviour out-of-the-box */
     private final DefaultPermissionGrantPolicy mDefaultPermissionGrantPolicy;
 
@@ -180,6 +195,16 @@
     @GuardedBy("mLock")
     private ArrayMap<String, List<String>> mBackgroundPermissions;
 
+    /**
+     * A permission backup might contain apps that are not installed. In this case we delay the
+     * restoration until the app is installed.
+     *
+     * <p>This array ({@code userId -> noDelayedBackupLeft}) is {@code true} for all the users where
+     * there is <u>no more</u> delayed backup left.
+     */
+    @GuardedBy("mLock")
+    private final SparseBooleanArray mHasNoDelayedPermBackup = new SparseBooleanArray();
+
     PermissionManagerService(Context context,
             @Nullable DefaultPermissionGrantedCallback defaultGrantCallback,
             @NonNull Object externalLock) {
@@ -218,29 +243,31 @@
             }
         }
 
-        LocalServices.addService(
-                PermissionManagerInternal.class, new PermissionManagerInternalImpl());
+        PermissionManagerServiceInternalImpl localService =
+                new PermissionManagerServiceInternalImpl();
+        LocalServices.addService(PermissionManagerServiceInternal.class, localService);
+        LocalServices.addService(PermissionManagerInternal.class, localService);
     }
 
     /**
      * Creates and returns an initialized, internal service for use by other components.
      * <p>
      * The object returned is identical to the one returned by the LocalServices class using:
-     * {@code LocalServices.getService(PermissionManagerInternal.class);}
+     * {@code LocalServices.getService(PermissionManagerServiceInternal.class);}
      * <p>
      * NOTE: The external lock is temporary and should be removed. This needs to be a
      * lock created by the permission manager itself.
      */
-    public static PermissionManagerInternal create(Context context,
+    public static PermissionManagerServiceInternal create(Context context,
             @Nullable DefaultPermissionGrantedCallback defaultGrantCallback,
             @NonNull Object externalLock) {
-        final PermissionManagerInternal permMgrInt =
-                LocalServices.getService(PermissionManagerInternal.class);
+        final PermissionManagerServiceInternal permMgrInt =
+                LocalServices.getService(PermissionManagerServiceInternal.class);
         if (permMgrInt != null) {
             return permMgrInt;
         }
         new PermissionManagerService(context, defaultGrantCallback, externalLock);
-        return LocalServices.getService(PermissionManagerInternal.class);
+        return LocalServices.getService(PermissionManagerServiceInternal.class);
     }
 
     @Nullable BasePermission getPermission(String permName) {
@@ -332,6 +359,74 @@
     }
 
     /**
+     * Get the state of the runtime permissions as xml file.
+     *
+     * <p>Can not be called on main thread.
+     *
+     * @param user The user the data should be extracted for
+     *
+     * @return The state as a xml file
+     */
+    private @Nullable byte[] backupRuntimePermissions(@NonNull UserHandle user) {
+        CompletableFuture<byte[]> backup = new CompletableFuture<>();
+        mPermissionControllerManager.getRuntimePermissionBackup(user, mContext.getMainExecutor(),
+                backup::complete);
+
+        try {
+            return backup.get(BACKUP_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS);
+        } catch (InterruptedException | ExecutionException  | TimeoutException e) {
+            Slog.e(TAG, "Cannot create permission backup for " + user, e);
+            return null;
+        }
+    }
+
+    /**
+     * Restore a permission state previously backed up via {@link #backupRuntimePermissions}.
+     *
+     * <p>If not all state can be restored, the un-appliable state will be delayed and can be
+     * applied via {@link #restoreDelayedRuntimePermissions}.
+     *
+     * @param backup The state as an xml file
+     * @param user The user the data should be restored for
+     */
+    private void restoreRuntimePermissions(@NonNull byte[] backup, @NonNull UserHandle user) {
+        synchronized (mLock) {
+            mHasNoDelayedPermBackup.delete(user.getIdentifier());
+            mPermissionControllerManager.restoreRuntimePermissionBackup(backup, user);
+        }
+    }
+
+    /**
+     * Try to apply permission backup that was previously not applied.
+     *
+     * <p>Can not be called on main thread.
+     *
+     * @param packageName The package that is newly installed
+     * @param user The user the package is installed for
+     *
+     * @see #restoreRuntimePermissions
+     */
+    private void restoreDelayedRuntimePermissions(@NonNull String packageName,
+            @NonNull UserHandle user) {
+        synchronized (mLock) {
+            if (mHasNoDelayedPermBackup.get(user.getIdentifier(), false)) {
+                return;
+            }
+
+            mPermissionControllerManager.restoreDelayedRuntimePermissionBackup(packageName, user,
+                    mContext.getMainExecutor(), (hasMoreBackup) -> {
+                        if (hasMoreBackup) {
+                            return;
+                        }
+
+                        synchronized (mLock) {
+                            mHasNoDelayedPermBackup.put(user.getIdentifier(), true);
+                        }
+                    });
+        }
+    }
+
+    /**
      * Returns {@code true} if the permission can be implied from another granted permission.
      * <p>Some permissions, such as ACCESS_FINE_LOCATION, imply other permissions,
      * such as ACCESS_COURSE_LOCATION. If the caller holds an umbrella permission, give
@@ -741,7 +836,6 @@
         if (ps == null) {
             return;
         }
-        final boolean isLegacySystemApp = mPackageManagerInt.isLegacySystemApp(pkg);
 
         final PermissionsState permissionsState = ps.getPermissionsState();
         PermissionsState origPermissions = permissionsState;
@@ -828,17 +922,9 @@
                     // For all apps normal permissions are install time ones.
                     grant = GRANT_INSTALL;
                 } else if (bp.isRuntime()) {
-                    // If a permission review is required for legacy apps we represent
-                    // their permissions as always granted runtime ones since we need
-                    // to keep the review required permission flag per user while an
-                    // install permission's state is shared across all users.
                     if (origPermissions.hasInstallPermission(bp.getName())) {
-                        // For legacy apps that became modern, install becomes runtime.
-                        grant = GRANT_UPGRADE;
-                    } else if (isLegacySystemApp) {
-                        // For legacy system apps, install becomes runtime.
-                        // We cannot check hasInstallPermission() for system apps since those
-                        // permissions were granted implicitly and not persisted pre-M.
+                        // Before Q we represented some runtime permissions as install permissions,
+                        // in Q we cannot do this anymore. Hence upgrade them all.
                         grant = GRANT_UPGRADE;
                     } else {
                         // For modern apps keep runtime permissions unchanged.
@@ -891,110 +977,111 @@
                             }
                             // Grant an install permission.
                             if (permissionsState.grantInstallPermission(bp) !=
-                                    PermissionsState.PERMISSION_OPERATION_FAILURE) {
+                                    PERMISSION_OPERATION_FAILURE) {
                                 changedInstallPermission = true;
                             }
                         } break;
 
                         case GRANT_RUNTIME: {
-                            // Grant previously granted runtime permissions.
-                            for (int userId : UserManagerService.getInstance().getUserIds()) {
-                                final PermissionState permissionState = origPermissions
+                            for (int userId : currentUserIds) {
+                                PermissionState permState = origPermissions
                                         .getRuntimePermissionState(perm, userId);
-                                int flags = permissionState != null
-                                        ? permissionState.getFlags() : 0;
-                                if (origPermissions.hasRuntimePermission(perm, userId)) {
-                                    // Don't propagate the permission in a permission review
-                                    // mode if the former was revoked, i.e. marked to not
-                                    // propagate on upgrade. Note that in a permission review
-                                    // mode install permissions are represented as constantly
-                                    // granted runtime ones since we need to keep a per user
-                                    // state associated with the permission. Also the revoke
-                                    // on upgrade flag is no longer applicable and is reset.
-                                    final boolean revokeOnUpgrade = (flags & PackageManager
-                                            .FLAG_PERMISSION_REVOKE_ON_UPGRADE) != 0;
-                                    if (revokeOnUpgrade) {
-                                        flags &= ~PackageManager.FLAG_PERMISSION_REVOKE_ON_UPGRADE;
-                                        // Since we changed the flags, we have to write.
-                                        updatedUserIds = ArrayUtils.appendInt(
-                                                updatedUserIds, userId);
+                                int flags = permState != null ? permState.getFlags() : 0;
+
+                                boolean wasChanged = false;
+
+                                if (appSupportsRuntimePermissions) {
+                                    // Remove review flag as it is not necessary anymore
+                                    if ((flags & FLAG_PERMISSION_REVIEW_REQUIRED) != 0) {
+                                        flags &= ~FLAG_PERMISSION_REVIEW_REQUIRED;
+                                        wasChanged = true;
                                     }
-                                    if (!revokeOnUpgrade) {
-                                        if (permissionsState.grantRuntimePermission(bp, userId) ==
-                                                PermissionsState.PERMISSION_OPERATION_FAILURE) {
-                                            // If we cannot put the permission as it was,
-                                            // we have to write.
-                                            updatedUserIds = ArrayUtils.appendInt(
-                                                    updatedUserIds, userId);
+
+                                    if ((flags & FLAG_PERMISSION_REVOKE_ON_UPGRADE) != 0) {
+                                        flags &= ~FLAG_PERMISSION_REVOKE_ON_UPGRADE;
+                                        wasChanged = true;
+                                    } else {
+                                        if (permState != null && permState.isGranted()) {
+                                            if (permissionsState.grantRuntimePermission(bp, userId)
+                                                    == PERMISSION_OPERATION_FAILURE) {
+                                                wasChanged = true;
+                                            }
+                                        }
+                                    }
+                                } else {
+                                    if (permState == null) {
+                                        // New permission
+                                        if (PLATFORM_PACKAGE_NAME.equals(
+                                                bp.getSourcePackageName())) {
+                                            if (!bp.isRemoved()) {
+                                                flags |= FLAG_PERMISSION_REVIEW_REQUIRED
+                                                        | FLAG_PERMISSION_REVOKE_ON_UPGRADE;
+                                                wasChanged = true;
+                                            }
                                         }
                                     }
 
-                                    // If the app supports runtime permissions no need for a review.
-                                    if (appSupportsRuntimePermissions
-                                            && (flags & PackageManager
-                                                    .FLAG_PERMISSION_REVIEW_REQUIRED) != 0) {
-                                        flags &= ~PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED;
-                                        // Since we changed the flags, we have to write.
-                                        updatedUserIds = ArrayUtils.appendInt(
-                                                updatedUserIds, userId);
-                                    }
-                                } else if (!appSupportsRuntimePermissions) {
-                                    // For legacy apps that need a permission review, every new
-                                    // runtime permission is granted but it is pending a review.
-                                    // We also need to review only platform defined runtime
-                                    // permissions as these are the only ones the platform knows
-                                    // how to disable the API to simulate revocation as legacy
-                                    // apps don't expect to run with revoked permissions.
-                                    if (PLATFORM_PACKAGE_NAME.equals(bp.getSourcePackageName())) {
-                                        if ((flags & FLAG_PERMISSION_REVIEW_REQUIRED) == 0
-                                                && !bp.isRemoved()) {
-                                            flags |= FLAG_PERMISSION_REVIEW_REQUIRED;
-                                            // We changed the flags, hence have to write.
-                                            updatedUserIds = ArrayUtils.appendInt(
-                                                    updatedUserIds, userId);
-                                        }
-                                    }
                                     if (permissionsState.grantRuntimePermission(bp, userId)
-                                            != PermissionsState.PERMISSION_OPERATION_FAILURE) {
-                                        // We changed the permission, hence have to write.
-                                        updatedUserIds = ArrayUtils.appendInt(
-                                                updatedUserIds, userId);
+                                            != PERMISSION_OPERATION_FAILURE) {
+                                        wasChanged = true;
                                     }
                                 }
-                                // Propagate the permission flags.
+
+                                if (wasChanged) {
+                                    updatedUserIds = ArrayUtils.appendInt(updatedUserIds, userId);
+                                }
+
                                 permissionsState.updatePermissionFlags(bp, userId, flags, flags);
                             }
                         } break;
 
                         case GRANT_UPGRADE: {
-                            // Grant runtime permissions for a previously held install permission.
-                            final PermissionState permissionState = origPermissions
+                            // Upgrade from Pre-Q to Q permission model. Make all permissions
+                            // runtime
+                            PermissionState permState = origPermissions
                                     .getInstallPermissionState(perm);
-                            final int flags =
-                                    (permissionState != null) ? permissionState.getFlags() : 0;
+                            int flags = (permState != null) ? permState.getFlags() : 0;
 
+                            // Remove install permission
                             if (origPermissions.revokeInstallPermission(bp)
-                                    != PermissionsState.PERMISSION_OPERATION_FAILURE) {
-                                // We will be transferring the permission flags, so clear them.
+                                    != PERMISSION_OPERATION_FAILURE) {
                                 origPermissions.updatePermissionFlags(bp, UserHandle.USER_ALL,
                                         PackageManager.MASK_PERMISSION_FLAGS, 0);
                                 changedInstallPermission = true;
                             }
 
-                            // If the permission is not to be promoted to runtime we ignore it and
-                            // also its other flags as they are not applicable to install permissions.
-                            if ((flags & PackageManager.FLAG_PERMISSION_REVOKE_ON_UPGRADE) == 0) {
-                                for (int userId : currentUserIds) {
+                            for (int userId : currentUserIds) {
+                                boolean wasChanged = false;
+
+                                if (appSupportsRuntimePermissions) {
+                                    // Remove review flag as it is not necessary anymore
+                                    if ((flags & FLAG_PERMISSION_REVIEW_REQUIRED) != 0) {
+                                        flags &= ~FLAG_PERMISSION_REVIEW_REQUIRED;
+                                        wasChanged = true;
+                                    }
+
+                                    if ((flags & FLAG_PERMISSION_REVOKE_ON_UPGRADE) != 0) {
+                                        flags &= ~FLAG_PERMISSION_REVOKE_ON_UPGRADE;
+                                        wasChanged = true;
+                                    } else {
+                                        if (permissionsState.grantRuntimePermission(bp, userId) !=
+                                                PERMISSION_OPERATION_FAILURE) {
+                                             wasChanged = true;
+                                        }
+                                    }
+                                } else {
                                     if (permissionsState.grantRuntimePermission(bp, userId) !=
-                                            PermissionsState.PERMISSION_OPERATION_FAILURE) {
-                                        // Transfer the permission flags.
-                                        permissionsState.updatePermissionFlags(bp, userId,
-                                                flags, flags);
-                                        // If we granted the permission, we have to write.
-                                        updatedUserIds = ArrayUtils.appendInt(
-                                                updatedUserIds, userId);
+                                            PERMISSION_OPERATION_FAILURE) {
+                                        flags |= FLAG_PERMISSION_REVIEW_REQUIRED;
+                                        wasChanged = true;
                                     }
                                 }
+
+                                if (wasChanged) {
+                                    updatedUserIds = ArrayUtils.appendInt(updatedUserIds, userId);
+                                }
+
+                                permissionsState.updatePermissionFlags(bp, userId, flags, flags);
                             }
                         } break;
 
@@ -1011,7 +1098,7 @@
                     }
                 } else {
                     if (permissionsState.revokeInstallPermission(bp) !=
-                            PermissionsState.PERMISSION_OPERATION_FAILURE) {
+                            PERMISSION_OPERATION_FAILURE) {
                         // Also drop the permission flags.
                         permissionsState.updatePermissionFlags(bp, UserHandle.USER_ALL,
                                 PackageManager.MASK_PERMISSION_FLAGS, 0);
@@ -1094,6 +1181,10 @@
             @NonNull int[] updatedUserIds) {
         AppOpsManager appOpsManager = mContext.getSystemService(AppOpsManager.class);
 
+        if (pkg.applicationInfo.targetSdkVersion < Build.VERSION_CODES.M) {
+            return updatedUserIds;
+        }
+
         String pkgName = pkg.packageName;
 
         int[] users = UserManagerService.getInstance().getUserIds();
@@ -1119,26 +1210,14 @@
                             if ((flags & (FLAG_PERMISSION_GRANTED_BY_DEFAULT
                                     | FLAG_PERMISSION_POLICY_FIXED | FLAG_PERMISSION_SYSTEM_FIXED))
                                     == 0) {
-                                if (pkg.applicationInfo.targetSdkVersion < Build.VERSION_CODES.M) {
-                                    if (permissionToOpCode(permission) != OP_NONE) {
-                                        setAppOpMode(permission, pkg, userId, MODE_IGNORED);
+                                int revokeResult = ps.revokeRuntimePermission(bp, userId);
+                                if (revokeResult
+                                        != PERMISSION_OPERATION_FAILURE) {
 
-                                        if (DEBUG_PERMISSIONS) {
-                                            Slog.i(TAG, "Revoking app-op "
-                                                    + permissionToOp(permission) + " for " + pkgName
-                                                    + " as it is now requested");
-                                        }
-                                    }
-                                } else {
-                                    int revokeResult = ps.revokeRuntimePermission(bp, userId);
-                                    if (revokeResult
-                                            != PermissionsState.PERMISSION_OPERATION_FAILURE) {
-
-                                        if (DEBUG_PERMISSIONS) {
-                                            Slog.i(TAG, "Revoking runtime permission " + permission
-                                                    + " for " + pkgName
-                                                    + " as it is now requested");
-                                        }
+                                    if (DEBUG_PERMISSIONS) {
+                                        Slog.i(TAG, "Revoking runtime permission " + permission
+                                                + " for " + pkgName
+                                                + " as it is now requested");
                                     }
                                 }
 
@@ -1925,7 +2004,7 @@
             // Development permissions must be handled specially, since they are not
             // normal runtime permissions.  For now they apply to all users.
             if (permissionsState.grantInstallPermission(bp) !=
-                    PermissionsState.PERMISSION_OPERATION_FAILURE) {
+                    PERMISSION_OPERATION_FAILURE) {
                 if (callback != null) {
                     callback.onInstallPermissionGranted();
                 }
@@ -1945,7 +2024,7 @@
 
         final int result = permissionsState.grantRuntimePermission(bp, userId);
         switch (result) {
-            case PermissionsState.PERMISSION_OPERATION_FAILURE: {
+            case PERMISSION_OPERATION_FAILURE: {
                 return;
             }
 
@@ -2045,7 +2124,7 @@
             // Development permissions must be handled specially, since they are not
             // normal runtime permissions.  For now they apply to all users.
             if (permissionsState.revokeInstallPermission(bp) !=
-                    PermissionsState.PERMISSION_OPERATION_FAILURE) {
+                    PERMISSION_OPERATION_FAILURE) {
                 if (callback != null) {
                     callback.onInstallPermissionRevoked();
                 }
@@ -2054,7 +2133,7 @@
         }
 
         if (permissionsState.revokeRuntimePermission(bp, userId) ==
-                PermissionsState.PERMISSION_OPERATION_FAILURE) {
+                PERMISSION_OPERATION_FAILURE) {
             return;
         }
 
@@ -2522,6 +2601,8 @@
             throw new IllegalStateException("Signature|privileged permissions not in "
                     + "privapp-permissions whitelist: " + mPrivappPermissionsViolations);
         }
+
+        mPermissionControllerManager = mContext.getSystemService(PermissionControllerManager.class);
     }
 
     private static String getVolumeUuidForPackage(PackageParser.Package pkg) {
@@ -2574,7 +2655,7 @@
         return mBackgroundPermissions;
     }
 
-    private class PermissionManagerInternalImpl extends PermissionManagerInternal {
+    private class PermissionManagerServiceInternalImpl extends PermissionManagerServiceInternal {
         @Override
         public void systemReady() {
             PermissionManagerService.this.systemReady();
@@ -2737,5 +2818,21 @@
                 return mSettings.getPermissionLocked(permName);
             }
         }
+
+        @Override
+        public @Nullable byte[] backupRuntimePermissions(@NonNull UserHandle user) {
+            return PermissionManagerService.this.backupRuntimePermissions(user);
+        }
+
+        @Override
+        public void restoreRuntimePermissions(@NonNull byte[] backup, @NonNull UserHandle user) {
+            PermissionManagerService.this.restoreRuntimePermissions(backup, user);
+        }
+
+        @Override
+        public void restoreDelayedRuntimePermissions(@NonNull String packageName,
+                @NonNull UserHandle user) {
+            PermissionManagerService.this.restoreDelayedRuntimePermissions(packageName, user);
+        }
     }
 }
diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerInternal.java b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInternal.java
similarity index 96%
rename from services/core/java/com/android/server/pm/permission/PermissionManagerInternal.java
rename to services/core/java/com/android/server/pm/permission/PermissionManagerServiceInternal.java
index f4979746..1dd2408 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerInternal.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInternal.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2017 The Android Open Source Project
+ * Copyright (C) 2019 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.
@@ -18,19 +18,22 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.content.pm.PackageManager.PermissionInfoFlags;
 import android.content.pm.PackageParser;
 import android.content.pm.PermissionGroupInfo;
 import android.content.pm.PermissionInfo;
-import android.content.pm.PackageManager.PermissionInfoFlags;
+import android.permission.PermissionManagerInternal;
 
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.List;
 
 /**
- * Internal interfaces to be used by other components within the system server.
+ * Internal interfaces services.
+ *
+ * TODO: Should be merged into PermissionManagerInternal, but currently uses internal classes.
  */
-public abstract class PermissionManagerInternal {
+public abstract class PermissionManagerServiceInternal extends PermissionManagerInternal {
     /**
      * Callbacks invoked when interesting actions have been taken on a permission.
      * <p>
diff --git a/services/core/java/com/android/server/pm/permission/TEST_MAPPING b/services/core/java/com/android/server/pm/permission/TEST_MAPPING
index 0892b32..2280d3f 100644
--- a/services/core/java/com/android/server/pm/permission/TEST_MAPPING
+++ b/services/core/java/com/android/server/pm/permission/TEST_MAPPING
@@ -16,6 +16,9 @@
                 },
                 {
                     "include-filter": "android.permission.cts.SplitPermissionTest"
+                },
+                {
+                    "include-filter": "android.permission.cts.PermissionFlagsTest"
                 }
             ]
         },
diff --git a/services/core/java/com/android/server/stats/StatsCompanionService.java b/services/core/java/com/android/server/stats/StatsCompanionService.java
index f3393e2..4815e5c 100644
--- a/services/core/java/com/android/server/stats/StatsCompanionService.java
+++ b/services/core/java/com/android/server/stats/StatsCompanionService.java
@@ -85,7 +85,9 @@
 import android.os.Temperature;
 import android.os.UserHandle;
 import android.os.UserManager;
+import android.os.storage.DiskInfo;
 import android.os.storage.StorageManager;
+import android.os.storage.VolumeInfo;
 import android.telephony.ModemActivityInfo;
 import android.telephony.TelephonyManager;
 import android.util.ArrayMap;
@@ -1942,6 +1944,41 @@
         }
     }
 
+    private void pullTimeZoneDataInfo(int tagId,
+            long elapsedNanos, long wallClockNanos, List<StatsLogEventWrapper> pulledData) {
+        String tzDbVersion = "Unknown";
+        try {
+            tzDbVersion = android.icu.util.TimeZone.getTZDataVersion();
+        } catch (Exception e) {
+            Log.e(TAG, "Getting tzdb version failed: ", e);
+        }
+
+        StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos);
+        e.writeString(tzDbVersion);
+        pulledData.add(e);
+    }
+
+    private void pullSDCardInfo(int tagId, long elapsedNanos, long wallClockNanos,
+            List<StatsLogEventWrapper> pulledData) {
+        StorageManager storageManager = mContext.getSystemService(StorageManager.class);
+        if (storageManager != null) {
+            List<VolumeInfo> volumes = storageManager.getVolumes();
+            for (VolumeInfo vol : volumes) {
+                final String envState = VolumeInfo.getEnvironmentForState(vol.getState());
+                final DiskInfo diskInfo = vol.getDisk();
+                if (diskInfo != null && diskInfo.isSd()) {
+                    if (envState.equals(Environment.MEDIA_MOUNTED)) {
+                        StatsLogEventWrapper e =
+                                new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos);
+                        e.writeInt(vol.getType() + 1);
+                        e.writeLong(diskInfo.size);
+                        pulledData.add(e);
+                    }
+                }
+            }
+        }
+    }
+
     /**
      * Pulls various data.
      */
@@ -2130,6 +2167,14 @@
                 pullDangerousPermissionState(elapsedNanos, wallClockNanos, ret);
                 break;
             }
+            case StatsLog.TIME_ZONE_DATA_INFO: {
+                pullTimeZoneDataInfo(tagId, elapsedNanos, wallClockNanos, ret);
+                break;
+            }
+            case StatsLog.SDCARD_INFO: {
+                pullSDCardInfo(tagId, elapsedNanos, wallClockNanos, ret);
+                break;
+            }
             default:
                 Slog.w(TAG, "No such tagId data as " + tagId);
                 return null;
diff --git a/services/core/java/com/android/server/telecom/TelecomLoaderService.java b/services/core/java/com/android/server/telecom/TelecomLoaderService.java
index 7a3f030..f581bc0 100644
--- a/services/core/java/com/android/server/telecom/TelecomLoaderService.java
+++ b/services/core/java/com/android/server/telecom/TelecomLoaderService.java
@@ -44,7 +44,7 @@
 import com.android.server.SystemService;
 import com.android.server.pm.UserManagerService;
 import com.android.server.pm.permission.DefaultPermissionGrantPolicy;
-import com.android.server.pm.permission.PermissionManagerInternal;
+import com.android.server.pm.permission.PermissionManagerServiceInternal;
 
 /**
  * Starts the telecom component by binding to its ITelecomService implementation. Telecom is setup
@@ -133,7 +133,7 @@
     }
 
     private DefaultPermissionGrantPolicy getDefaultPermissionGrantPolicy() {
-        return LocalServices.getService(PermissionManagerInternal.class)
+        return LocalServices.getService(PermissionManagerServiceInternal.class)
                 .getDefaultPermissionGrantPolicy();
     }
 
diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java
index c33a2c1..d40948b 100644
--- a/services/core/java/com/android/server/wm/ActivityStarter.java
+++ b/services/core/java/com/android/server/wm/ActivityStarter.java
@@ -830,12 +830,25 @@
                         new String[]{resolvedType}, PendingIntent.FLAG_CANCEL_CURRENT
                                 | PendingIntent.FLAG_ONE_SHOT, null);
 
-                final int flags = intent.getFlags();
                 Intent newIntent = new Intent(Intent.ACTION_REVIEW_PERMISSIONS);
-                newIntent.setFlags(flags
-                        | FLAG_ACTIVITY_NEW_TASK
-                        | Intent.FLAG_ACTIVITY_MULTIPLE_TASK
-                        | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
+
+                int flags = intent.getFlags();
+                flags |= Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS;
+
+                /*
+                 * Prevent reuse of review activity: Each app needs their own review activity. By
+                 * default activities launched with NEW_TASK or NEW_DOCUMENT try to reuse activities
+                 * with the same launch parameters (extras are ignored). Hence to avoid possible
+                 * reuse force a new activity via the MULTIPLE_TASK flag.
+                 *
+                 * Activities that are not launched with NEW_TASK or NEW_DOCUMENT are not re-used,
+                 * hence no need to add the flag in this case.
+                 */
+                if ((flags & (FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_NEW_DOCUMENT)) != 0) {
+                    flags |= Intent.FLAG_ACTIVITY_MULTIPLE_TASK;
+                }
+                newIntent.setFlags(flags);
+
                 newIntent.putExtra(Intent.EXTRA_PACKAGE_NAME, aInfo.packageName);
                 newIntent.putExtra(Intent.EXTRA_INTENT, new IntentSender(target));
                 if (resultRecord != null) {
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 168c9ad..899bf7c 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -5090,11 +5090,7 @@
 
     @Override
     public void startWindowTrace(){
-        try {
-            mWindowTracing.startTrace(null /* printwriter */);
-        } catch (IOException e) {
-            throw new RuntimeException(e);
-        }
+        mWindowTracing.startTrace(null /* printwriter */);
     }
 
     @Override
diff --git a/services/core/java/com/android/server/wm/WindowTraceBuffer.java b/services/core/java/com/android/server/wm/WindowTraceBuffer.java
index e4461ea..2ce6e6c 100644
--- a/services/core/java/com/android/server/wm/WindowTraceBuffer.java
+++ b/services/core/java/com/android/server/wm/WindowTraceBuffer.java
@@ -20,7 +20,6 @@
 import static com.android.server.wm.WindowManagerTraceFileProto.MAGIC_NUMBER_H;
 import static com.android.server.wm.WindowManagerTraceFileProto.MAGIC_NUMBER_L;
 
-import android.os.Trace;
 import android.util.proto.ProtoOutputStream;
 
 import com.android.internal.annotations.VisibleForTesting;
@@ -36,24 +35,30 @@
 /**
  * Buffer used for window tracing.
  */
-abstract class WindowTraceBuffer {
+class WindowTraceBuffer {
     private static final long MAGIC_NUMBER_VALUE = ((long) MAGIC_NUMBER_H << 32) | MAGIC_NUMBER_L;
 
-    final Object mBufferLock = new Object();
-    final Queue<ProtoOutputStream> mBuffer = new ArrayDeque<>();
-    final File mTraceFile;
-    int mBufferSize;
-    private final int mBufferCapacity;
+    private final Object mBufferLock = new Object();
 
-    WindowTraceBuffer(int size, File traceFile) throws IOException {
-        mBufferCapacity = size;
-        mTraceFile = traceFile;
+    private final Queue<ProtoOutputStream> mBuffer = new ArrayDeque<>();
+    private int mBufferUsedSize;
+    private int mBufferCapacity;
 
-        initTraceFile();
+    WindowTraceBuffer(int bufferCapacity) {
+        mBufferCapacity = bufferCapacity;
+        resetBuffer();
     }
 
     int getAvailableSpace() {
-        return mBufferCapacity - mBufferSize;
+        return mBufferCapacity - mBufferUsedSize;
+    }
+
+    int size() {
+        return mBuffer.size();
+    }
+
+    void setCapacity(int capacity) {
+        mBufferCapacity = capacity;
     }
 
     /**
@@ -70,42 +75,37 @@
                     + mBufferCapacity + " Object size: " + protoLength);
         }
         synchronized (mBufferLock) {
-            boolean canAdd = canAdd(protoLength);
-            if (canAdd) {
-                mBuffer.add(proto);
-                mBufferSize += protoLength;
-            }
+            discardOldest(protoLength);
+            mBuffer.add(proto);
+            mBufferUsedSize += protoLength;
             mBufferLock.notify();
         }
     }
 
-    /**
-     * Stops the buffer execution and flush all buffer content to the disk.
-     *
-     * @throws IOException if the buffer cannot write its contents to the {@link #mTraceFile}
-     */
-    void dump() throws IOException, InterruptedException {
-        try {
-            Trace.traceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "writeTraceToFile");
-            writeTraceToFile();
-        } finally {
-            Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER);
-        }
-    }
-
-    @VisibleForTesting
     boolean contains(byte[] other) {
         return mBuffer.stream()
                 .anyMatch(p -> Arrays.equals(p.getBytes(), other));
     }
 
-    private void initTraceFile() throws IOException {
-        mTraceFile.delete();
-        try (OutputStream os = new FileOutputStream(mTraceFile)) {
-            mTraceFile.setReadable(true, false);
-            ProtoOutputStream proto = new ProtoOutputStream(os);
-            proto.write(MAGIC_NUMBER, MAGIC_NUMBER_VALUE);
-            proto.flush();
+    /**
+     * Writes the trace buffer to disk.
+     */
+    void writeTraceToFile(File traceFile) throws IOException {
+        synchronized (mBufferLock) {
+            traceFile.delete();
+            traceFile.setReadable(true, false);
+            try (OutputStream os = new FileOutputStream(traceFile)) {
+                ProtoOutputStream proto = new ProtoOutputStream();
+                proto.write(MAGIC_NUMBER, MAGIC_NUMBER_VALUE);
+                os.write(proto.getBytes());
+                while (!mBuffer.isEmpty()) {
+                    proto = mBuffer.poll();
+                    mBufferUsedSize -= proto.getRawSize();
+                    byte[] protoBytes = proto.getBytes();
+                    os.write(protoBytes);
+                }
+                os.flush();
+            }
         }
     }
 
@@ -114,59 +114,48 @@
      * smaller than the overall buffer size.
      *
      * @param protoLength byte array representation of the Proto object to add
-     * @return {@code true} if the element can be added to the buffer or not
      */
-    abstract boolean canAdd(int protoLength);
+    private void discardOldest(int protoLength) {
+        long availableSpace = getAvailableSpace();
+
+        while (availableSpace < protoLength) {
+
+            ProtoOutputStream item = mBuffer.poll();
+            if (item == null) {
+                throw new IllegalStateException("No element to discard from buffer");
+            }
+            mBufferUsedSize -= item.getRawSize();
+            availableSpace = getAvailableSpace();
+        }
+    }
 
     /**
-     * Flush all buffer content to the disk.
-     *
-     * @throws IOException if the buffer cannot write its contents to the {@link #mTraceFile}
+     * Removes all elements form the buffer
      */
-    abstract void writeTraceToFile() throws IOException, InterruptedException;
-
-    /**
-     * Builder for a {@code WindowTraceBuffer} which creates a {@link WindowTraceRingBuffer} for
-     * continuous mode or a {@link WindowTraceQueueBuffer} otherwise
-     */
-    static class Builder {
-        private boolean mContinuous;
-        private File mTraceFile;
-        private int mBufferCapacity;
-
-        Builder setContinuousMode(boolean continuous) {
-            mContinuous = continuous;
-            return this;
+    void resetBuffer() {
+        synchronized (mBufferLock) {
+            mBuffer.clear();
+            mBufferUsedSize = 0;
         }
+    }
 
-        Builder setTraceFile(File traceFile) {
-            mTraceFile = traceFile;
-            return this;
-        }
+    @VisibleForTesting
+    int getBufferSize() {
+        return mBufferUsedSize;
+    }
 
-        Builder setBufferCapacity(int size) {
-            mBufferCapacity = size;
-            return this;
-        }
-
-        File getFile() {
-            return mTraceFile;
-        }
-
-        WindowTraceBuffer build() throws IOException {
-            if (mBufferCapacity <= 0) {
-                throw new IllegalStateException("Buffer capacity must be greater than 0.");
-            }
-
-            if (mTraceFile == null) {
-                throw new IllegalArgumentException("A valid trace file must be specified.");
-            }
-
-            if (mContinuous) {
-                return new WindowTraceRingBuffer(mBufferCapacity, mTraceFile);
-            } else {
-                return new WindowTraceQueueBuffer(mBufferCapacity, mTraceFile);
-            }
+    String getStatus() {
+        synchronized (mBufferLock) {
+            return "Buffer size: "
+                    + mBufferCapacity
+                    + " bytes"
+                    + "\n"
+                    + "Buffer usage: "
+                    + mBufferUsedSize
+                    + " bytes"
+                    + "\n"
+                    + "Elements in the buffer: "
+                    + mBuffer.size();
         }
     }
 }
diff --git a/services/core/java/com/android/server/wm/WindowTraceQueueBuffer.java b/services/core/java/com/android/server/wm/WindowTraceQueueBuffer.java
deleted file mode 100644
index 5888b7a..0000000
--- a/services/core/java/com/android/server/wm/WindowTraceQueueBuffer.java
+++ /dev/null
@@ -1,105 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.wm;
-
-import static android.os.Build.IS_USER;
-
-import android.util.Log;
-import android.util.proto.ProtoOutputStream;
-
-import com.android.internal.annotations.VisibleForTesting;
-
-import java.io.File;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.io.OutputStream;
-
-/**
- * A buffer structure backed by a {@link java.util.concurrent.BlockingQueue} to store the first
- * {@code #size size} bytes of window trace elements.
- * Once the buffer is full it will no longer accepts new elements.
- */
-class WindowTraceQueueBuffer extends WindowTraceBuffer {
-    private static final String TAG = "WindowTracing";
-
-    private Thread mConsumerThread;
-    private boolean mCancel;
-
-    @VisibleForTesting
-    WindowTraceQueueBuffer(int size, File traceFile, boolean startConsumerThread)
-            throws IOException {
-        super(size, traceFile);
-        if (startConsumerThread) {
-            initializeConsumerThread();
-        }
-    }
-
-    WindowTraceQueueBuffer(int size, File traceFile) throws IOException {
-        this(size, traceFile, !IS_USER);
-    }
-
-    private void initializeConsumerThread() {
-        mCancel = false;
-        mConsumerThread = new Thread(() -> {
-            try {
-                loop();
-            } catch (InterruptedException e) {
-                Log.i(TAG, "Interrupting trace consumer thread");
-            } catch (IOException e) {
-                Log.e(TAG, "Failed to execute trace consumer thread", e);
-            }
-        }, "window_tracing");
-        mConsumerThread.start();
-    }
-
-    private void loop() throws IOException, InterruptedException {
-        while (!mCancel) {
-            ProtoOutputStream proto;
-            synchronized (mBufferLock) {
-                mBufferLock.wait();
-                proto = mBuffer.poll();
-                if (proto != null) {
-                    mBufferSize -= proto.getRawSize();
-                }
-            }
-            if (proto != null) {
-                try (OutputStream os = new FileOutputStream(mTraceFile, true)) {
-                    byte[] protoBytes = proto.getBytes();
-                    os.write(protoBytes);
-                }
-            }
-        }
-    }
-
-    @Override
-    boolean canAdd(int protoLength) {
-        long availableSpace = getAvailableSpace();
-        return availableSpace >= protoLength;
-    }
-
-    @Override
-    void writeTraceToFile() throws InterruptedException {
-        synchronized (mBufferLock) {
-            mCancel = true;
-            mBufferLock.notify();
-        }
-        if (mConsumerThread != null) {
-            mConsumerThread.join();
-            mConsumerThread = null;
-        }
-    }
-}
diff --git a/services/core/java/com/android/server/wm/WindowTraceRingBuffer.java b/services/core/java/com/android/server/wm/WindowTraceRingBuffer.java
deleted file mode 100644
index 77d30be..0000000
--- a/services/core/java/com/android/server/wm/WindowTraceRingBuffer.java
+++ /dev/null
@@ -1,70 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.wm;
-
-import android.util.proto.ProtoOutputStream;
-
-import java.io.File;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.io.OutputStream;
-
-/**
- * A ring buffer to store the {@code #size size} bytes of window trace data.
- * The buffer operates on a trace entry level, that is, if the new trace data is larger than the
- * available buffer space, the buffer will discard as many full trace entries as necessary to fit
- * the new trace.
- */
-class WindowTraceRingBuffer extends WindowTraceBuffer {
-    WindowTraceRingBuffer(int size, File traceFile) throws IOException {
-        super(size, traceFile);
-    }
-
-    @Override
-    boolean canAdd(int protoLength) {
-        long availableSpace = getAvailableSpace();
-
-        while (availableSpace < protoLength) {
-            discardOldest();
-            availableSpace = getAvailableSpace();
-        }
-
-        return true;
-    }
-
-    @Override
-    void writeTraceToFile() throws IOException {
-        synchronized (mBufferLock) {
-            try (OutputStream os = new FileOutputStream(mTraceFile, true)) {
-                while (!mBuffer.isEmpty()) {
-                    ProtoOutputStream proto = mBuffer.poll();
-                    mBufferSize -= proto.getRawSize();
-                    byte[] protoBytes = proto.getBytes();
-                    os.write(protoBytes);
-                }
-            }
-        }
-    }
-
-    private void discardOldest() {
-        ProtoOutputStream item = mBuffer.poll();
-        if (item == null) {
-            throw new IllegalStateException("No element to discard from buffer");
-        }
-        mBufferSize -= item.getRawSize();
-    }
-}
diff --git a/services/core/java/com/android/server/wm/WindowTracing.java b/services/core/java/com/android/server/wm/WindowTracing.java
index abc474d..0ce215c 100644
--- a/services/core/java/com/android/server/wm/WindowTracing.java
+++ b/services/core/java/com/android/server/wm/WindowTracing.java
@@ -31,8 +31,6 @@
 import android.util.proto.ProtoOutputStream;
 import android.view.Choreographer;
 
-import com.android.internal.annotations.VisibleForTesting;
-
 import java.io.File;
 import java.io.IOException;
 import java.io.PrintWriter;
@@ -47,139 +45,191 @@
      * Maximum buffer size, currently defined as 512 KB
      * Size was experimentally defined to fit between 100 to 150 elements.
      */
-    private static final int WINDOW_TRACE_BUFFER_SIZE = 512 * 1024;
+    private static final int BUFFER_CAPACITY_CRITICAL = 512 * 1024;
+    private static final int BUFFER_CAPACITY_TRIM = 2048 * 1024;
+    private static final int BUFFER_CAPACITY_ALL = 4096 * 1024;
+    private static final String TRACE_FILENAME = "/data/misc/wmtrace/wm_trace.pb";
     private static final String TAG = "WindowTracing";
 
     private final WindowManagerService mService;
     private final Choreographer mChoreographer;
     private final WindowManagerGlobalLock mGlobalLock;
 
-    private final Object mLock = new Object();
-    private final WindowTraceBuffer.Builder mBufferBuilder;
+    private final Object mEnabledLock = new Object();
+    private final File mTraceFile;
+    private final WindowTraceBuffer mBuffer;
+    private final Choreographer.FrameCallback mFrameCallback = (frameTimeNanos) ->
+            log("onFrame" /* where */);
 
-    private WindowTraceBuffer mTraceBuffer;
-
-    private @WindowTraceLogLevel int mWindowTraceLogLevel = WindowTraceLogLevel.TRIM;
-    private boolean mContinuousMode;
+    private @WindowTraceLogLevel int mLogLevel = WindowTraceLogLevel.TRIM;
+    private boolean mLogOnFrame = false;
     private boolean mEnabled;
     private volatile boolean mEnabledLockFree;
     private boolean mScheduled;
-    private Choreographer.FrameCallback mFrameCallback = (frameTimeNanos) ->
-            log("onFrame" /* where */);
 
-    private WindowTracing(File file, WindowManagerService service, Choreographer choreographer) {
-        this(file, service, choreographer, service.mGlobalLock);
+    static WindowTracing createDefaultAndStartLooper(WindowManagerService service,
+            Choreographer choreographer) {
+        File file = new File(TRACE_FILENAME);
+        return new WindowTracing(file, service, choreographer, BUFFER_CAPACITY_TRIM);
     }
 
-    @VisibleForTesting
-    WindowTracing(File file, WindowManagerService service, Choreographer choreographer,
-            WindowManagerGlobalLock globalLock) {
-        mBufferBuilder = new WindowTraceBuffer.Builder()
-                .setTraceFile(file)
-                .setBufferCapacity(WINDOW_TRACE_BUFFER_SIZE);
+    private WindowTracing(File file, WindowManagerService service, Choreographer choreographer,
+            int bufferCapacity) {
+        this(file, service, choreographer, service.mGlobalLock, bufferCapacity);
+    }
 
+    WindowTracing(File file, WindowManagerService service, Choreographer choreographer,
+            WindowManagerGlobalLock globalLock, int bufferCapacity) {
         mChoreographer = choreographer;
         mService = service;
         mGlobalLock = globalLock;
+        mTraceFile = file;
+        mBuffer = new WindowTraceBuffer(bufferCapacity);
+        setLogLevel(WindowTraceLogLevel.TRIM, null /* pw */);
     }
 
-    void startTrace(@Nullable PrintWriter pw) throws IOException {
+    void startTrace(@Nullable PrintWriter pw) {
         if (IS_USER) {
             logAndPrintln(pw, "Error: Tracing is not supported on user builds.");
             return;
         }
-        synchronized (mLock) {
-            logAndPrintln(pw, "Start tracing to " + mBufferBuilder.getFile() + ".");
-            if (mTraceBuffer != null) {
-                writeTraceToFileLocked();
-            }
-            mTraceBuffer = mBufferBuilder
-                    .setContinuousMode(mContinuousMode)
-                    .build();
+        synchronized (mEnabledLock) {
+            logAndPrintln(pw, "Start tracing to " + mTraceFile + ".");
+            mBuffer.resetBuffer();
             mEnabled = mEnabledLockFree = true;
         }
     }
 
-    private void logAndPrintln(@Nullable PrintWriter pw, String msg) {
-        Log.i(TAG, msg);
-        if (pw != null) {
-            pw.println(msg);
-            pw.flush();
-        }
-    }
-
     void stopTrace(@Nullable PrintWriter pw) {
         if (IS_USER) {
             logAndPrintln(pw, "Error: Tracing is not supported on user builds.");
             return;
         }
-        synchronized (mLock) {
-            logAndPrintln(pw, "Stop tracing to " + mBufferBuilder.getFile()
-                    + ". Waiting for traces to flush.");
+        synchronized (mEnabledLock) {
+            logAndPrintln(pw, "Stop tracing to " + mTraceFile + ". Waiting for traces to flush.");
             mEnabled = mEnabledLockFree = false;
 
-            synchronized (mLock) {
-                if (mEnabled) {
-                    logAndPrintln(pw, "ERROR: tracing was re-enabled while waiting for flush.");
-                    throw new IllegalStateException("tracing enabled while waiting for flush.");
-                }
-                writeTraceToFileLocked();
-                mTraceBuffer = null;
+            if (mEnabled) {
+                logAndPrintln(pw, "ERROR: tracing was re-enabled while waiting for flush.");
+                throw new IllegalStateException("tracing enabled while waiting for flush.");
             }
-            logAndPrintln(pw, "Trace written to " + mBufferBuilder.getFile() + ".");
+            writeTraceToFileLocked();
+            logAndPrintln(pw, "Trace written to " + mTraceFile + ".");
         }
     }
 
-    @VisibleForTesting
-    void setContinuousMode(boolean continuous, PrintWriter pw) {
-        logAndPrintln(pw, "Setting window tracing continuous mode to " + continuous);
+    private void setLogLevel(@WindowTraceLogLevel int logLevel, PrintWriter pw) {
+        logAndPrintln(pw, "Setting window tracing log level to " + logLevel);
+        mLogLevel = logLevel;
 
-        if (mEnabled) {
-            logAndPrintln(pw, "Trace is currently active, change will take effect once the "
-                    + "trace is restarted.");
+        switch (logLevel) {
+            case WindowTraceLogLevel.ALL: {
+                setBufferCapacity(BUFFER_CAPACITY_ALL, pw);
+                break;
+            }
+            case WindowTraceLogLevel.TRIM: {
+                setBufferCapacity(BUFFER_CAPACITY_TRIM, pw);
+                break;
+            }
+            case WindowTraceLogLevel.CRITICAL: {
+                setBufferCapacity(BUFFER_CAPACITY_CRITICAL, pw);
+                break;
+            }
         }
-        mContinuousMode = continuous;
-        mWindowTraceLogLevel = (continuous) ? WindowTraceLogLevel.CRITICAL :
-                WindowTraceLogLevel.TRIM;
+    }
+
+    private void setLogFrequency(boolean onFrame, PrintWriter pw) {
+        logAndPrintln(pw, "Setting window tracing log frequency to "
+                + ((onFrame) ? "frame" : "transaction"));
+        mLogOnFrame = onFrame;
+    }
+
+    private void setBufferCapacity(int capacity, PrintWriter pw) {
+        logAndPrintln(pw, "Setting window tracing buffer capacity to " + capacity + "bytes");
+        mBuffer.setCapacity(capacity);
     }
 
     boolean isEnabled() {
         return mEnabledLockFree;
     }
 
-    static WindowTracing createDefaultAndStartLooper(WindowManagerService service,
-            Choreographer choreographer) {
-        File file = new File("/data/misc/wmtrace/wm_trace.pb");
-        return new WindowTracing(file, service, choreographer);
-    }
-
     int onShellCommand(ShellCommand shell) {
         PrintWriter pw = shell.getOutPrintWriter();
-        try {
-            String cmd = shell.getNextArgRequired();
-            switch (cmd) {
-                case "start":
-                    startTrace(pw);
-                    return 0;
-                case "stop":
-                    stopTrace(pw);
-                    return 0;
-                case "continuous":
-                    setContinuousMode(Boolean.valueOf(shell.getNextArgRequired()), pw);
-                    return 0;
-                default:
-                    pw.println("Unknown command: " + cmd);
-                    return -1;
-            }
-        } catch (IOException e) {
-            logAndPrintln(pw, e.toString());
-            throw new RuntimeException(e);
+        String cmd = shell.getNextArgRequired();
+        switch (cmd) {
+            case "start":
+                startTrace(pw);
+                return 0;
+            case "stop":
+                stopTrace(pw);
+                return 0;
+            case "status":
+                logAndPrintln(pw, getStatus());
+                return 0;
+            case "frame":
+                setLogFrequency(true /* onFrame */, pw);
+                mBuffer.resetBuffer();
+                return 0;
+            case "transaction":
+                setLogFrequency(false /* onFrame */, pw);
+                mBuffer.resetBuffer();
+                return 0;
+            case "level":
+                String logLevelStr = shell.getNextArgRequired().toLowerCase();
+                switch (logLevelStr) {
+                    case "all": {
+                        setLogLevel(WindowTraceLogLevel.ALL, pw);
+                        break;
+                    }
+                    case "trim": {
+                        setLogLevel(WindowTraceLogLevel.TRIM, pw);
+                        break;
+                    }
+                    case "critical": {
+                        setLogLevel(WindowTraceLogLevel.CRITICAL, pw);
+                        break;
+                    }
+                    default: {
+                        setLogLevel(WindowTraceLogLevel.TRIM, pw);
+                        break;
+                    }
+                }
+                mBuffer.resetBuffer();
+                return 0;
+            case "size":
+                setBufferCapacity(Integer.parseInt(shell.getNextArgRequired()) * 1024, pw);
+                mBuffer.resetBuffer();
+                return 0;
+            default:
+                pw.println("Unknown command: " + cmd);
+                pw.println("Window manager trace options:");
+                pw.println("  start: Start logging");
+                pw.println("  stop: Stop logging");
+                pw.println("  frame: Log trace once per frame");
+                pw.println("  transaction: Log each transaction");
+                pw.println("  size: Set the maximum log size (in KB)");
+                pw.println("  level [lvl]: Set the log level between");
+                pw.println("    lvl may be one of:");
+                pw.println("      critical: Only visible windows with reduced information");
+                pw.println("      trim: All windows with reduced");
+                pw.println("      all: All window and information");
+                return -1;
         }
     }
 
+    private String getStatus() {
+        return "Status: "
+                + ((isEnabled()) ? "Enabled" : "Disabled")
+                + "\n"
+                + "Log level: "
+                + mLogLevel
+                + "\n"
+                + mBuffer.getStatus();
+    }
+
     /**
      * If tracing is enabled, log the current state or schedule the next frame to be logged,
-     * according to {@link #mContinuousMode}.
+     * according to {@link #mLogOnFrame}.
      *
      * @param where Logging point descriptor
      */
@@ -188,7 +238,7 @@
             return;
         }
 
-        if (mContinuousMode) {
+        if (mLogOnFrame) {
             schedule();
         } else {
             log(where);
@@ -215,25 +265,24 @@
     private void log(String where) {
         Trace.traceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "traceStateLocked");
         try {
-            synchronized (mGlobalLock) {
-                ProtoOutputStream os = new ProtoOutputStream();
-                long tokenOuter = os.start(ENTRY);
-                os.write(ELAPSED_REALTIME_NANOS, SystemClock.elapsedRealtimeNanos());
-                os.write(WHERE, where);
+            ProtoOutputStream os = new ProtoOutputStream();
+            long tokenOuter = os.start(ENTRY);
+            os.write(ELAPSED_REALTIME_NANOS, SystemClock.elapsedRealtimeNanos());
+            os.write(WHERE, where);
 
+            long tokenInner = os.start(WINDOW_MANAGER_SERVICE);
+            synchronized (mGlobalLock) {
                 Trace.traceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "writeToProtoLocked");
                 try {
-                    long tokenInner = os.start(WINDOW_MANAGER_SERVICE);
-                    mService.writeToProtoLocked(os, mWindowTraceLogLevel);
-                    os.end(tokenInner);
+                    mService.writeToProtoLocked(os, mLogLevel);
                 } finally {
                     Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER);
                 }
-                os.end(tokenOuter);
-                mTraceBuffer.add(os);
-
-                mScheduled = false;
             }
+            os.end(tokenInner);
+            os.end(tokenOuter);
+            mBuffer.add(os);
+            mScheduled = false;
         } catch (Exception e) {
             Log.wtf(TAG, "Exception while tracing state", e);
         } finally {
@@ -242,31 +291,37 @@
     }
 
     /**
-     * Writes the trace buffer to disk. This method has no internal synchronization and should be
-     * externally synchronized
-     */
-    private void writeTraceToFileLocked() {
-        if (mTraceBuffer == null) {
-            return;
-        }
-
-        try {
-            mTraceBuffer.dump();
-        } catch (IOException e) {
-            Log.e(TAG, "Unable to write buffer to file", e);
-        } catch (InterruptedException e) {
-            Log.e(TAG, "Unable to interrupt window tracing file write thread", e);
-        }
-    }
-
-    /**
-     * Writes the trace buffer to disk and clones it into a new file for the bugreport.
+     * Writes the trace buffer to new file for the bugreport.
+     *
      * This method is synchronized with {@code #startTrace(PrintWriter)} and
      * {@link #stopTrace(PrintWriter)}.
      */
     void writeTraceToFile() {
-        synchronized (mLock) {
+        synchronized (mEnabledLock) {
             writeTraceToFileLocked();
         }
     }
-}
+
+    private void logAndPrintln(@Nullable PrintWriter pw, String msg) {
+        Log.i(TAG, msg);
+        if (pw != null) {
+            pw.println(msg);
+            pw.flush();
+        }
+    }
+
+    /**
+     * Writes the trace buffer to disk. This method has no internal synchronization and should be
+     * externally synchronized
+     */
+    private void writeTraceToFileLocked() {
+        try {
+            Trace.traceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "writeTraceToFileLocked");
+            mBuffer.writeTraceToFile(mTraceFile);
+        } catch (IOException e) {
+            Log.e(TAG, "Unable to write buffer to file", e);
+        } finally {
+            Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER);
+        }
+    }
+}
\ No newline at end of file
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index a6017f2..aae159c 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -36,6 +36,7 @@
 import android.content.res.Resources.Theme;
 import android.database.sqlite.SQLiteCompatibilityWalFlags;
 import android.database.sqlite.SQLiteGlobal;
+import android.net.NetworkStackClient;
 import android.os.BaseBundle;
 import android.os.Binder;
 import android.os.Build;
@@ -1350,9 +1351,7 @@
 
             traceBeginAndSlog("StartNetworkStack");
             try {
-                final android.net.NetworkStack networkStack =
-                        context.getSystemService(android.net.NetworkStack.class);
-                networkStack.start(context);
+                NetworkStackClient.getInstance().start(context);
             } catch (Throwable e) {
                 reportWtf("starting Network Stack", e);
             }
diff --git a/services/net/java/android/net/NetworkStackClient.java b/services/net/java/android/net/NetworkStackClient.java
new file mode 100644
index 0000000..1eb7b98
--- /dev/null
+++ b/services/net/java/android/net/NetworkStackClient.java
@@ -0,0 +1,289 @@
+/*
+ * Copyright (C) 2019 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.net;
+
+import static android.content.pm.PackageManager.PERMISSION_GRANTED;
+import static android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK;
+import static android.os.IServiceManager.DUMP_FLAG_PRIORITY_HIGH;
+import static android.os.IServiceManager.DUMP_FLAG_PRIORITY_NORMAL;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.content.pm.PackageManager;
+import android.net.dhcp.DhcpServingParamsParcel;
+import android.net.dhcp.IDhcpServerCallbacks;
+import android.net.ip.IIpClientCallbacks;
+import android.os.Binder;
+import android.os.IBinder;
+import android.os.Process;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.os.UserHandle;
+import android.util.Slog;
+
+import com.android.internal.annotations.GuardedBy;
+
+import java.lang.reflect.InvocationTargetException;
+import java.util.ArrayList;
+
+/**
+ * Service used to communicate with the network stack, which is running in a separate module.
+ * @hide
+ */
+public class NetworkStackClient {
+    private static final String TAG = NetworkStackClient.class.getSimpleName();
+
+    private static final int NETWORKSTACK_TIMEOUT_MS = 10_000;
+
+    private static NetworkStackClient sInstance;
+
+    @NonNull
+    @GuardedBy("mPendingNetStackRequests")
+    private final ArrayList<NetworkStackCallback> mPendingNetStackRequests = new ArrayList<>();
+    @Nullable
+    @GuardedBy("mPendingNetStackRequests")
+    private INetworkStackConnector mConnector;
+
+    private volatile boolean mNetworkStackStartRequested = false;
+
+    private interface NetworkStackCallback {
+        void onNetworkStackConnected(INetworkStackConnector connector);
+    }
+
+    private NetworkStackClient() { }
+
+    /**
+     * Get the NetworkStackClient singleton instance.
+     */
+    public static synchronized NetworkStackClient getInstance() {
+        if (sInstance == null) {
+            sInstance = new NetworkStackClient();
+        }
+        return sInstance;
+    }
+
+    /**
+     * Create a DHCP server according to the specified parameters.
+     *
+     * <p>The server will be returned asynchronously through the provided callbacks.
+     */
+    public void makeDhcpServer(final String ifName, final DhcpServingParamsParcel params,
+            final IDhcpServerCallbacks cb) {
+        requestConnector(connector -> {
+            try {
+                connector.makeDhcpServer(ifName, params, cb);
+            } catch (RemoteException e) {
+                e.rethrowFromSystemServer();
+            }
+        });
+    }
+
+    /**
+     * Create an IpClient on the specified interface.
+     *
+     * <p>The IpClient will be returned asynchronously through the provided callbacks.
+     */
+    public void makeIpClient(String ifName, IIpClientCallbacks cb) {
+        requestConnector(connector -> {
+            try {
+                connector.makeIpClient(ifName, cb);
+            } catch (RemoteException e) {
+                e.rethrowFromSystemServer();
+            }
+        });
+    }
+
+    /**
+     * Create a NetworkMonitor.
+     *
+     * <p>The INetworkMonitor will be returned asynchronously through the provided callbacks.
+     */
+    public void makeNetworkMonitor(
+            NetworkParcelable network, String name, INetworkMonitorCallbacks cb) {
+        requestConnector(connector -> {
+            try {
+                connector.makeNetworkMonitor(network, name, cb);
+            } catch (RemoteException e) {
+                e.rethrowFromSystemServer();
+            }
+        });
+    }
+
+    private class NetworkStackConnection implements ServiceConnection {
+        @Override
+        public void onServiceConnected(ComponentName name, IBinder service) {
+            registerNetworkStackService(service);
+        }
+
+        @Override
+        public void onServiceDisconnected(ComponentName name) {
+            // TODO: crash/reboot the system ?
+            Slog.wtf(TAG, "Lost network stack connector");
+        }
+    };
+
+    private void registerNetworkStackService(@NonNull IBinder service) {
+        final INetworkStackConnector connector = INetworkStackConnector.Stub.asInterface(service);
+
+        ServiceManager.addService(Context.NETWORK_STACK_SERVICE, service, false /* allowIsolated */,
+                DUMP_FLAG_PRIORITY_HIGH | DUMP_FLAG_PRIORITY_NORMAL);
+
+        final ArrayList<NetworkStackCallback> requests;
+        synchronized (mPendingNetStackRequests) {
+            requests = new ArrayList<>(mPendingNetStackRequests);
+            mPendingNetStackRequests.clear();
+            mConnector = connector;
+        }
+
+        for (NetworkStackCallback r : requests) {
+            r.onNetworkStackConnected(connector);
+        }
+    }
+
+    /**
+     * Start the network stack. Should be called only once on device startup.
+     *
+     * <p>This method will start the network stack either in the network stack process, or inside
+     * the system server on devices that do not support the network stack module. The network stack
+     * connector will then be delivered asynchronously to clients that requested it before it was
+     * started.
+     */
+    public void start(Context context) {
+        mNetworkStackStartRequested = true;
+        // Try to bind in-process if the library is available
+        IBinder connector = null;
+        try {
+            final Class service = Class.forName(
+                    "com.android.server.NetworkStackService",
+                    true /* initialize */,
+                    context.getClassLoader());
+            connector = (IBinder) service.getMethod("makeConnector", Context.class)
+                    .invoke(null, context);
+        } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
+            Slog.wtf(TAG, "Could not create network stack connector from NetworkStackService");
+            // TODO: crash/reboot system here ?
+            return;
+        } catch (ClassNotFoundException e) {
+            // Normal behavior if stack is provided by the app: fall through
+        }
+
+        // In-process network stack. Add the service to the service manager here.
+        if (connector != null) {
+            registerNetworkStackService(connector);
+            return;
+        }
+        // Start the network stack process. The service will be added to the service manager in
+        // NetworkStackConnection.onServiceConnected().
+        final Intent intent = new Intent(INetworkStackConnector.class.getName());
+        final ComponentName comp = intent.resolveSystemService(context.getPackageManager(), 0);
+        intent.setComponent(comp);
+
+        if (comp == null) {
+            Slog.wtf(TAG, "Could not resolve the network stack with " + intent);
+            // TODO: crash/reboot system server ?
+            return;
+        }
+        final PackageManager pm = context.getPackageManager();
+        int uid = -1;
+        try {
+            uid = pm.getPackageUid(comp.getPackageName(), UserHandle.USER_SYSTEM);
+        } catch (PackageManager.NameNotFoundException e) {
+            Slog.wtf("Network stack package not found", e);
+            // Fall through
+        }
+        if (uid != Process.NETWORK_STACK_UID) {
+            throw new SecurityException("Invalid network stack UID: " + uid);
+        }
+
+        final int hasPermission =
+                pm.checkPermission(PERMISSION_MAINLINE_NETWORK_STACK, comp.getPackageName());
+        if (hasPermission != PERMISSION_GRANTED) {
+            throw new SecurityException(
+                    "Network stack does not have permission " + PERMISSION_MAINLINE_NETWORK_STACK);
+        }
+
+        if (!context.bindServiceAsUser(intent, new NetworkStackConnection(),
+                Context.BIND_AUTO_CREATE | Context.BIND_IMPORTANT, UserHandle.SYSTEM)) {
+            Slog.wtf(TAG,
+                    "Could not bind to network stack in-process, or in app with " + intent);
+            // TODO: crash/reboot system server if no network stack after a timeout ?
+        }
+    }
+
+    /**
+     * For non-system server clients, get the connector registered by the system server.
+     */
+    private INetworkStackConnector getRemoteConnector() {
+        // Block until the NetworkStack connector is registered in ServiceManager.
+        // <p>This is only useful for non-system processes that do not have a way to be notified of
+        // registration completion. Adding a callback system would be too heavy weight considering
+        // that the connector is registered on boot, so it is unlikely that a client would request
+        // it before it is registered.
+        // TODO: consider blocking boot on registration and simplify much of the logic in this class
+        IBinder connector;
+        try {
+            final long before = System.currentTimeMillis();
+            while ((connector = ServiceManager.getService(Context.NETWORK_STACK_SERVICE)) == null) {
+                Thread.sleep(20);
+                if (System.currentTimeMillis() - before > NETWORKSTACK_TIMEOUT_MS) {
+                    Slog.e(TAG, "Timeout waiting for NetworkStack connector");
+                    return null;
+                }
+            }
+        } catch (InterruptedException e) {
+            Slog.e(TAG, "Error waiting for NetworkStack connector", e);
+            return null;
+        }
+
+        return INetworkStackConnector.Stub.asInterface(connector);
+    }
+
+    private void requestConnector(@NonNull NetworkStackCallback request) {
+        // TODO: PID check.
+        final int caller = Binder.getCallingUid();
+        if (caller != Process.SYSTEM_UID && !UserHandle.isSameApp(caller, Process.BLUETOOTH_UID)) {
+            // Don't even attempt to obtain the connector and give a nice error message
+            throw new SecurityException(
+                    "Only the system server should try to bind to the network stack.");
+        }
+
+        if (!mNetworkStackStartRequested) {
+            // The network stack is not being started in this process, e.g. this process is not
+            // the system server. Get a remote connector registered by the system server.
+            final INetworkStackConnector connector = getRemoteConnector();
+            synchronized (mPendingNetStackRequests) {
+                mConnector = connector;
+            }
+            request.onNetworkStackConnected(connector);
+            return;
+        }
+
+        final INetworkStackConnector connector;
+        synchronized (mPendingNetStackRequests) {
+            connector = mConnector;
+            if (connector == null) {
+                mPendingNetStackRequests.add(request);
+                return;
+            }
+        }
+
+        request.onNetworkStackConnected(connector);
+    }
+}
diff --git a/services/net/java/android/net/ip/IpClientUtil.java b/services/net/java/android/net/ip/IpClientUtil.java
index 2a2a67a..bf917bf 100644
--- a/services/net/java/android/net/ip/IpClientUtil.java
+++ b/services/net/java/android/net/ip/IpClientUtil.java
@@ -23,8 +23,7 @@
 import android.net.DhcpResultsParcelable;
 import android.net.LinkProperties;
 import android.net.LinkPropertiesParcelable;
-import android.net.NetworkStack;
-import android.net.ip.IIpClientCallbacks;
+import android.net.NetworkStackClient;
 import android.os.ConditionVariable;
 
 import java.io.FileDescriptor;
@@ -76,30 +75,17 @@
      *
      * <p>This is a convenience method to allow clients to use {@link IpClientCallbacks} instead of
      * {@link IIpClientCallbacks}.
-     * @see {@link NetworkStack#makeIpClient(String, IIpClientCallbacks)}
+     * @see {@link NetworkStackClient#makeIpClient(String, IIpClientCallbacks)}
      */
     public static void makeIpClient(Context context, String ifName, IpClientCallbacks callback) {
-        context.getSystemService(NetworkStack.class)
-                .makeIpClient(ifName, new IpClientCallbacksProxy(callback));
-    }
-
-    /**
-     * Create a new IpClient.
-     *
-     * <p>This is a convenience method to allow clients to use {@link IpClientCallbacksProxy}
-     * instead of {@link IIpClientCallbacks}.
-     * @see {@link NetworkStack#makeIpClient(String, IIpClientCallbacks)}
-     */
-    public static void makeIpClient(
-            Context context, String ifName, IpClientCallbacksProxy callback) {
-        context.getSystemService(NetworkStack.class)
-                .makeIpClient(ifName, callback);
+        // TODO: migrate clients and remove context argument
+        NetworkStackClient.getInstance().makeIpClient(ifName, new IpClientCallbacksProxy(callback));
     }
 
     /**
      * Wrapper to relay calls from {@link IIpClientCallbacks} to {@link IpClientCallbacks}.
      */
-    public static class IpClientCallbacksProxy extends IIpClientCallbacks.Stub {
+    private static class IpClientCallbacksProxy extends IIpClientCallbacks.Stub {
         protected final IpClientCallbacks mCb;
 
         /**
diff --git a/services/net/java/android/net/ip/IpServer.java b/services/net/java/android/net/ip/IpServer.java
index 7910c9a..34fc735 100644
--- a/services/net/java/android/net/ip/IpServer.java
+++ b/services/net/java/android/net/ip/IpServer.java
@@ -22,7 +22,6 @@
 import static android.net.util.NetworkConstants.RFC7421_PREFIX_LENGTH;
 import static android.net.util.NetworkConstants.asByte;
 
-import android.content.Context;
 import android.net.ConnectivityManager;
 import android.net.INetd;
 import android.net.INetworkStackStatusCallback;
@@ -31,7 +30,7 @@
 import android.net.IpPrefix;
 import android.net.LinkAddress;
 import android.net.LinkProperties;
-import android.net.NetworkStack;
+import android.net.NetworkStackClient;
 import android.net.RouteInfo;
 import android.net.dhcp.DhcpServerCallbacks;
 import android.net.dhcp.DhcpServingParamsParcel;
@@ -132,10 +131,6 @@
     }
 
     public static class Dependencies {
-        private final Context mContext;
-        public Dependencies(Context context) {
-            mContext = context;
-        }
         public RouterAdvertisementDaemon getRouterAdvertisementDaemon(InterfaceParams ifParams) {
             return new RouterAdvertisementDaemon(ifParams);
         }
@@ -153,7 +148,7 @@
          */
         public void makeDhcpServer(String ifName, DhcpServingParamsParcel params,
                 DhcpServerCallbacks cb) {
-            mContext.getSystemService(NetworkStack.class).makeDhcpServer(ifName, params, cb);
+            NetworkStackClient.getInstance().makeDhcpServer(ifName, params, cb);
         }
     }
 
diff --git a/services/tests/servicestests/src/com/android/server/backup/testutils/IPackageManagerStub.java b/services/tests/servicestests/src/com/android/server/backup/testutils/IPackageManagerStub.java
index 0355e84..5cb6cbb 100644
--- a/services/tests/servicestests/src/com/android/server/backup/testutils/IPackageManagerStub.java
+++ b/services/tests/servicestests/src/com/android/server/backup/testutils/IPackageManagerStub.java
@@ -556,16 +556,6 @@
     }
 
     @Override
-    public byte[] getPermissionGrantBackup(int userId) throws RemoteException {
-        return new byte[0];
-    }
-
-    @Override
-    public void restorePermissionGrants(byte[] backup, int userId) throws RemoteException {
-
-    }
-
-    @Override
     public ComponentName getHomeActivities(List<ResolveInfo> outHomeCandidates)
         throws RemoteException {
         return null;
diff --git a/services/tests/servicestests/src/com/android/server/display/ColorDisplayServiceTest.java b/services/tests/servicestests/src/com/android/server/display/ColorDisplayServiceTest.java
index 5900fc5..01759d2 100644
--- a/services/tests/servicestests/src/com/android/server/display/ColorDisplayServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/display/ColorDisplayServiceTest.java
@@ -98,6 +98,8 @@
 
         mColorDisplayService = new ColorDisplayService(mContext);
         mBinderService = mColorDisplayService.new BinderService();
+        LocalServices.addService(ColorDisplayService.ColorDisplayServiceInternal.class,
+                        mColorDisplayService.new ColorDisplayServiceInternal());
     }
 
     @After
@@ -110,6 +112,8 @@
 
         mUserId = UserHandle.USER_NULL;
         mContext = null;
+
+        LocalServices.removeServiceForTest(ColorDisplayService.ColorDisplayServiceInternal.class);
     }
 
     @AfterClass
@@ -979,6 +983,99 @@
         assertActiveColorMode(ColorDisplayManager.COLOR_MODE_NATURAL);
     }
 
+    @Test
+    public void displayWhiteBalance_enable() {
+        setWhiteBalance(true /* Enable DWB Setting */);
+        setActivated(false /* activated */, -30 /* lastActivatedTimeOffset */);
+        mBinderService.setColorMode(ColorDisplayManager.COLOR_MODE_NATURAL);
+        startService();
+        assertDwbActive(true);
+    }
+
+    @Test
+    public void displayWhiteBalance_disableAfterNightDisplayEnable() {
+        setWhiteBalance(true /* Enable DWB Setting */);
+
+        startService();
+        /* Enable nightlight */
+        setAutoModeTwilight(-120 /* sunsetOffset */, -60 /* sunriseOffset */);
+        setActivated(true /* activated */, -30 /* lastActivatedTimeOffset */);
+
+        /* Since we are using FakeSettingsProvider which could not trigger observer change,
+         * force an update here.*/
+        mColorDisplayService.updateDisplayWhiteBalanceStatus();
+        assertDwbActive(false);
+    }
+
+    @Test
+    public void displayWhiteBalance_enableAfterNightDisplayDisable() {
+        setWhiteBalance(true /* Enable DWB Setting */);
+        startService();
+        /* Enable nightlight */
+        setAutoModeTwilight(-120 /* sunsetOffset */, -60 /* sunriseOffset */);
+        setActivated(true /* activated */, -30 /* lastActivatedTimeOffset */);
+
+        mColorDisplayService.updateDisplayWhiteBalanceStatus();
+        assertDwbActive(false);
+
+        /* Disable nightlight */
+        setActivated(false /* activated */, -30 /* lastActivatedTimeOffset */);
+        mColorDisplayService.updateDisplayWhiteBalanceStatus();
+        assertDwbActive(true);
+    }
+
+    @Test
+    public void displayWhiteBalance_enableAfterLinearColorMode() {
+        setWhiteBalance(true /* Enable DWB Setting */);
+        setActivated(false /* activated */, -30 /* lastActivatedTimeOffset */);
+        startService();
+        mBinderService.setColorMode(ColorDisplayManager.COLOR_MODE_NATURAL);
+
+        mColorDisplayService.updateDisplayWhiteBalanceStatus();
+        assertDwbActive(true);
+    }
+
+    @Test
+    public void displayWhiteBalance_setTemperatureOverMax() {
+        int max = mColorDisplayService.mDisplayWhiteBalanceTintController.mTemperatureMax;
+
+        ColorDisplayService.ColorDisplayServiceInternal cdsInternal = LocalServices.getService(
+                        ColorDisplayService.ColorDisplayServiceInternal.class);
+        cdsInternal.setDisplayWhiteBalanceColorTemperature(max+1);
+
+        assertWithMessage("Unexpected temperature set")
+                .that(mColorDisplayService.mDisplayWhiteBalanceTintController.mCurrentColorTemperature)
+                .isEqualTo(max);
+    }
+
+    @Test
+    public void displayWhiteBalance_setTemperatureBelowMin() {
+        int min = mColorDisplayService.mDisplayWhiteBalanceTintController.mTemperatureMin;
+
+        ColorDisplayService.ColorDisplayServiceInternal cdsInternal = LocalServices.getService(
+                        ColorDisplayService.ColorDisplayServiceInternal.class);
+        cdsInternal.setDisplayWhiteBalanceColorTemperature(min - 1);
+
+        assertWithMessage("Unexpected temperature set")
+                .that(mColorDisplayService.mDisplayWhiteBalanceTintController.mCurrentColorTemperature)
+                .isEqualTo(min);
+    }
+
+    @Test
+    public void displayWhiteBalance_setValidTemperature() {
+        int min = mColorDisplayService.mDisplayWhiteBalanceTintController.mTemperatureMin;
+        int max = mColorDisplayService.mDisplayWhiteBalanceTintController.mTemperatureMax;
+        int valToSet = (min + max) / 2;
+
+        ColorDisplayService.ColorDisplayServiceInternal cdsInternal = LocalServices.getService(
+                        ColorDisplayService.ColorDisplayServiceInternal.class);
+        cdsInternal.setDisplayWhiteBalanceColorTemperature(valToSet);
+
+        assertWithMessage("Unexpected temperature set")
+                .that(mColorDisplayService.mDisplayWhiteBalanceTintController.mCurrentColorTemperature)
+                .isEqualTo(valToSet);
+    }
+
     /**
      * Configures Night display to use a custom schedule.
      *
@@ -1041,6 +1138,16 @@
     }
 
     /**
+     * Configures the Display White Balance setting state.
+     *
+     * @param state {@code true} if display white balance should be enabled
+     */
+    private void setWhiteBalance(boolean state) {
+        Secure.putIntForUser(mContext.getContentResolver(),
+                Secure.DISPLAY_WHITE_BALANCE_ENABLED, state ? 1 : 0, mUserId);
+    }
+
+    /**
      * Configures color mode.
      */
     private void setColorMode(int colorMode) {
@@ -1111,6 +1218,17 @@
     }
 
     /**
+     * Convenience method for asserting that the DWB active status matches expectation.
+     *
+     * @param enabled the expected active status.
+     */
+    private void assertDwbActive(boolean enabled) {
+        assertWithMessage("Incorrect Display White Balance state")
+                .that(mColorDisplayService.mDisplayWhiteBalanceTintController.isActivated())
+                .isEqualTo(enabled);
+    }
+
+    /**
      * Convenience for making a {@link LocalTime} instance with an offset relative to now.
      *
      * @param offsetMinutes the offset relative to now (in minutes)
diff --git a/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java b/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java
index 6d28ed1..50734ef 100644
--- a/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java
+++ b/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java
@@ -55,8 +55,8 @@
 
 import com.android.internal.os.AtomicFile;
 import com.android.server.LocalServices;
-import com.android.server.pm.permission.PermissionManagerInternal;
 import com.android.server.pm.permission.PermissionManagerService;
+import com.android.server.pm.permission.PermissionManagerServiceInternal;
 
 import org.junit.After;
 import org.junit.Before;
@@ -88,7 +88,8 @@
         writeOldFiles();
         final Context context = InstrumentationRegistry.getContext();
         final Object lock = new Object();
-        PermissionManagerInternal pmInt = PermissionManagerService.create(context, null, lock);
+        PermissionManagerServiceInternal pmInt = PermissionManagerService.create(context, null,
+                lock);
         Settings settings =
                 new Settings(context.getFilesDir(), pmInt.getPermissionSettings(), lock);
         assertThat(settings.readLPw(createFakeUsers()), is(true));
@@ -103,7 +104,8 @@
         writeOldFiles();
         final Context context = InstrumentationRegistry.getContext();
         final Object lock = new Object();
-        PermissionManagerInternal pmInt = PermissionManagerService.create(context, null, lock);
+        PermissionManagerServiceInternal pmInt = PermissionManagerService.create(context, null,
+                lock);
         Settings settings =
                 new Settings(context.getFilesDir(), pmInt.getPermissionSettings(), lock);
         assertThat(settings.readLPw(createFakeUsers()), is(true));
@@ -120,7 +122,8 @@
         writeOldFiles();
         final Context context = InstrumentationRegistry.getContext();
         final Object lock = new Object();
-        PermissionManagerInternal pmInt = PermissionManagerService.create(context, null, lock);
+        PermissionManagerServiceInternal pmInt = PermissionManagerService.create(context, null,
+                lock);
         Settings settings =
                 new Settings(context.getFilesDir(), pmInt.getPermissionSettings(), lock);
         assertThat(settings.readLPw(createFakeUsers()), is(true));
@@ -143,7 +146,8 @@
         writeOldFiles();
         final Context context = InstrumentationRegistry.getContext();
         final Object lock = new Object();
-        PermissionManagerInternal pmInt = PermissionManagerService.create(context, null, lock);
+        PermissionManagerServiceInternal pmInt = PermissionManagerService.create(context, null,
+                lock);
         Settings settings =
                 new Settings(context.getFilesDir(), pmInt.getPermissionSettings(), lock);
         assertThat(settings.readLPw(createFakeUsers()), is(true));
@@ -313,7 +317,8 @@
         writeOldFiles();
         final Context context = InstrumentationRegistry.getContext();
         final Object lock = new Object();
-        PermissionManagerInternal pmInt = PermissionManagerService.create(context, null, lock);
+        PermissionManagerServiceInternal pmInt = PermissionManagerService.create(context, null,
+                lock);
         Settings settings =
                 new Settings(context.getFilesDir(), pmInt.getPermissionSettings(), lock);
         assertThat(settings.readLPw(createFakeUsers()), is(true));
@@ -507,7 +512,8 @@
     public void testUpdatePackageSetting03() {
         final Context context = InstrumentationRegistry.getContext();
         final Object lock = new Object();
-        PermissionManagerInternal pmInt = PermissionManagerService.create(context, null, lock);
+        PermissionManagerServiceInternal pmInt = PermissionManagerService.create(context, null,
+                lock);
         final Settings testSettings01 =
                 new Settings(context.getFilesDir(), pmInt.getPermissionSettings(), lock);
         final SharedUserSetting testUserSetting01 = createSharedUserSetting(
@@ -625,7 +631,8 @@
     public void testCreateNewSetting03() {
         final Context context = InstrumentationRegistry.getContext();
         final Object lock = new Object();
-        PermissionManagerInternal pmInt = PermissionManagerService.create(context, null, lock);
+        PermissionManagerServiceInternal pmInt = PermissionManagerService.create(context, null,
+                lock);
         final Settings testSettings01 =
                 new Settings(context.getFilesDir(), pmInt.getPermissionSettings(), lock);
         final SharedUserSetting testUserSetting01 = createSharedUserSetting(
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowTraceBufferTest.java b/services/tests/wmtests/src/com/android/server/wm/WindowTraceBufferTest.java
index 2b8e307..b299f0d 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowTraceBufferTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowTraceBufferTest.java
@@ -36,11 +36,10 @@
 import org.junit.Test;
 
 import java.io.File;
-import java.io.IOException;
 
 
 /**
- * Test class for {@link WindowTraceBuffer} and {@link WindowTraceQueueBuffer}.
+ * Test class for {@link WindowTraceBuffer}.
  *
  * Build/Install/Run:
  *  atest WmTests:WindowTraceBufferTest
@@ -49,12 +48,15 @@
 @Presubmit
 public class WindowTraceBufferTest {
     private File mFile;
+    private WindowTraceBuffer mBuffer;
 
     @Before
     public void setUp() throws Exception {
         final Context testContext = getInstrumentation().getContext();
         mFile = testContext.getFileStreamPath("tracing_test.dat");
         mFile.delete();
+
+        mBuffer = new WindowTraceBuffer(10);
     }
 
     @After
@@ -63,145 +65,112 @@
     }
 
     @Test
-    public void testTraceQueueBuffer_addItem() throws Exception {
-        ProtoOutputStream toWrite1 = getDummy(1);
-        ProtoOutputStream toWrite2 = getDummy(2);
-        ProtoOutputStream toWrite3 = getDummy(3);
-        final int objectSize = toWrite1.getRawSize();
-        final int bufferCapacity = objectSize * 2;
-
-        final WindowTraceBuffer buffer = buildQueueBuffer(bufferCapacity);
-
-        buffer.add(toWrite1);
-        byte[] toWrite1Bytes = toWrite1.getBytes();
-        assertTrue("First element should be in the list",
-                buffer.contains(toWrite1Bytes));
-
-        buffer.add(toWrite2);
-        byte[] toWrite2Bytes = toWrite2.getBytes();
-        assertTrue("First element should be in the list",
-                buffer.contains(toWrite1Bytes));
-        assertTrue("Second element should be in the list",
-                buffer.contains(toWrite2Bytes));
-
-        buffer.add(toWrite3);
-        byte[] toWrite3Bytes = toWrite3.getBytes();
-        assertTrue("First element should be in the list",
-                buffer.contains(toWrite1Bytes));
-        assertTrue("Second element should be in the list",
-                buffer.contains(toWrite2Bytes));
-        assertTrue("Third element should not be in the list",
-                !buffer.contains(toWrite3Bytes));
-
-        assertEquals("Buffer should have 2 elements", buffer.mBuffer.size(), 2);
-        assertEquals(String.format("Buffer is full, used space should be %d", bufferCapacity),
-                buffer.mBufferSize, bufferCapacity);
-        assertEquals("Buffer is full, available space should be 0",
-                buffer.getAvailableSpace(), 0);
-    }
-
-    @Test
-    public void testTraceRingBuffer_addItem() throws Exception {
+    public void test_addItem() {
         ProtoOutputStream toWrite = getDummy(1);
         final int objectSize = toWrite.getRawSize();
+        mBuffer.setCapacity(objectSize);
+        mBuffer.resetBuffer();
 
-        final WindowTraceBuffer buffer = buildRingBuffer(objectSize);
+        Preconditions.checkArgument(mBuffer.size() == 0);
 
-        Preconditions.checkArgument(buffer.mBuffer.isEmpty());
+        mBuffer.add(toWrite);
 
-        buffer.add(toWrite);
-
-        assertEquals("Item was not added to the buffer", buffer.mBuffer.size(), 1);
+        assertEquals("Item was not added to the buffer", 1, mBuffer.size());
         assertEquals("Total buffer getSize differs from inserted object",
-                buffer.mBufferSize, objectSize);
-        assertEquals("Available buffer space does not match used one",
-                buffer.getAvailableSpace(), 0);
+                mBuffer.getBufferSize(), objectSize);
+        assertEquals("Available buffer space does not match used one", 0,
+                mBuffer.getAvailableSpace());
     }
 
     @Test
-    public void testTraceRingBuffer_addItemMustOverwriteOne() throws Exception {
+    public void test_addItemMustOverwriteOne() {
         ProtoOutputStream toWrite1 = getDummy(1);
         ProtoOutputStream toWrite2 = getDummy(2);
         ProtoOutputStream toWrite3 = getDummy(3);
         final int objectSize = toWrite1.getRawSize();
-
         final int bufferCapacity = objectSize * 2 + 1;
-        final WindowTraceBuffer buffer = buildRingBuffer(bufferCapacity);
+        mBuffer.setCapacity(bufferCapacity);
+        mBuffer.resetBuffer();
 
-        buffer.add(toWrite1);
+        mBuffer.add(toWrite1);
         byte[] toWrite1Bytes = toWrite1.getBytes();
         assertTrue("First element should be in the list",
-                buffer.contains(toWrite1Bytes));
+                mBuffer.contains(toWrite1Bytes));
 
-        buffer.add(toWrite2);
+        mBuffer.add(toWrite2);
         byte[] toWrite2Bytes = toWrite2.getBytes();
         assertTrue("First element should be in the list",
-                buffer.contains(toWrite1Bytes));
+                mBuffer.contains(toWrite1Bytes));
         assertTrue("Second element should be in the list",
-                buffer.contains(toWrite2Bytes));
+                mBuffer.contains(toWrite2Bytes));
 
-        buffer.add(toWrite3);
+        mBuffer.add(toWrite3);
         byte[] toWrite3Bytes = toWrite3.getBytes();
         assertTrue("First element should not be in the list",
-                !buffer.contains(toWrite1Bytes));
+                !mBuffer.contains(toWrite1Bytes));
         assertTrue("Second element should be in the list",
-                buffer.contains(toWrite2Bytes));
+                mBuffer.contains(toWrite2Bytes));
         assertTrue("Third element should be in the list",
-                buffer.contains(toWrite3Bytes));
-        assertEquals("Buffer should have 2 elements", buffer.mBuffer.size(), 2);
+                mBuffer.contains(toWrite3Bytes));
+        assertEquals("Buffer should have 2 elements", 2, mBuffer.size());
         assertEquals(String.format("Buffer is full, used space should be %d", bufferCapacity),
-                buffer.mBufferSize, bufferCapacity - 1);
-        assertEquals(" Buffer is full, available space should be 0",
-                buffer.getAvailableSpace(), 1);
+                mBuffer.getBufferSize(), bufferCapacity - 1);
+        assertEquals(" Buffer is full, available space should be 0", 1,
+                mBuffer.getAvailableSpace());
     }
 
     @Test
-    public void testTraceRingBuffer_addItemMustOverwriteMultiple() throws Exception {
+    public void test_addItemMustOverwriteMultiple() {
         ProtoOutputStream toWriteSmall1 = getDummy(1);
         ProtoOutputStream toWriteSmall2 = getDummy(2);
         final int objectSize = toWriteSmall1.getRawSize();
-
         final int bufferCapacity = objectSize * 2;
-        final WindowTraceBuffer buffer = buildRingBuffer(bufferCapacity);
+        mBuffer.setCapacity(bufferCapacity);
+        mBuffer.resetBuffer();
 
         ProtoOutputStream toWriteBig = new ProtoOutputStream();
         toWriteBig.write(MAGIC_NUMBER, 1);
         toWriteBig.write(MAGIC_NUMBER, 2);
 
-        buffer.add(toWriteSmall1);
+        mBuffer.add(toWriteSmall1);
         byte[] toWriteSmall1Bytes = toWriteSmall1.getBytes();
         assertTrue("First element should be in the list",
-                buffer.contains(toWriteSmall1Bytes));
+                mBuffer.contains(toWriteSmall1Bytes));
 
-        buffer.add(toWriteSmall2);
+        mBuffer.add(toWriteSmall2);
         byte[] toWriteSmall2Bytes = toWriteSmall2.getBytes();
         assertTrue("First element should be in the list",
-                buffer.contains(toWriteSmall1Bytes));
+                mBuffer.contains(toWriteSmall1Bytes));
         assertTrue("Second element should be in the list",
-                buffer.contains(toWriteSmall2Bytes));
+                mBuffer.contains(toWriteSmall2Bytes));
 
-        buffer.add(toWriteBig);
+        mBuffer.add(toWriteBig);
         byte[] toWriteBigBytes = toWriteBig.getBytes();
         assertTrue("Third element should overwrite all others",
-                !buffer.contains(toWriteSmall1Bytes));
+                !mBuffer.contains(toWriteSmall1Bytes));
         assertTrue("Third element should overwrite all others",
-                !buffer.contains(toWriteSmall2Bytes));
+                !mBuffer.contains(toWriteSmall2Bytes));
         assertTrue("Third element should overwrite all others",
-                buffer.contains(toWriteBigBytes));
+                mBuffer.contains(toWriteBigBytes));
 
-        assertEquals(" Buffer should have only 1 big element", buffer.mBuffer.size(), 1);
+        assertEquals(" Buffer should have only 1 big element", 1, mBuffer.size());
         assertEquals(String.format(" Buffer is full, used space should be %d", bufferCapacity),
-                buffer.mBufferSize, bufferCapacity);
-        assertEquals(" Buffer is full, available space should be 0",
-                buffer.getAvailableSpace(), 0);
+                mBuffer.getBufferSize(), bufferCapacity);
+        assertEquals(" Buffer is full, available space should be 0", 0,
+                mBuffer.getAvailableSpace());
     }
 
-    private WindowTraceBuffer buildRingBuffer(int capacity) throws IOException {
-        return new WindowTraceBuffer.Builder()
-                .setContinuousMode(true)
-                .setBufferCapacity(capacity)
-                .setTraceFile(mFile)
-                .build();
+    @Test
+    public void test_startResetsBuffer() {
+        ProtoOutputStream toWrite = getDummy(1);
+        mBuffer.resetBuffer();
+        Preconditions.checkArgument(mBuffer.size() == 0);
+
+        mBuffer.add(toWrite);
+        assertEquals("Item was not added to the buffer", 1, mBuffer.size());
+        mBuffer.resetBuffer();
+        assertEquals("Buffer should be empty after reset", 0, mBuffer.size());
+        assertEquals("Buffer size should be 0 after reset", 0, mBuffer.getBufferSize());
     }
 
     private ProtoOutputStream getDummy(int value) {
@@ -212,7 +181,4 @@
         return toWrite;
     }
 
-    private WindowTraceBuffer buildQueueBuffer(int size) throws IOException {
-        return new WindowTraceQueueBuffer(size, mFile, false);
-    }
 }
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowTracingTest.java b/services/tests/wmtests/src/com/android/server/wm/WindowTracingTest.java
index 3c6e240..8358fdd 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowTracingTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowTracingTest.java
@@ -88,8 +88,7 @@
         mFile.delete();
 
         mWindowTracing = new WindowTracing(mFile, mWmMock, mChoreographer,
-                new WindowManagerGlobalLock());
-        mWindowTracing.setContinuousMode(false /* continuous */, null /* pw */);
+                new WindowManagerGlobalLock(), 1024);
     }
 
     @After
@@ -103,13 +102,13 @@
     }
 
     @Test
-    public void isEnabled_returnsTrueAfterStart() throws Exception {
+    public void isEnabled_returnsTrueAfterStart() {
         mWindowTracing.startTrace(mock(PrintWriter.class));
         assertTrue(mWindowTracing.isEnabled());
     }
 
     @Test
-    public void isEnabled_returnsFalseAfterStop() throws Exception {
+    public void isEnabled_returnsFalseAfterStop() {
         mWindowTracing.startTrace(mock(PrintWriter.class));
         mWindowTracing.stopTrace(mock(PrintWriter.class));
         assertFalse(mWindowTracing.isEnabled());
@@ -133,6 +132,8 @@
         mWindowTracing.startTrace(mock(PrintWriter.class));
         mWindowTracing.stopTrace(mock(PrintWriter.class));
 
+        assertTrue("Trace file should exist", mFile.exists());
+
         byte[] header = new byte[MAGIC_HEADER.length];
         try (InputStream is = new FileInputStream(mFile)) {
             assertEquals(MAGIC_HEADER.length, is.read(header));
diff --git a/telecomm/java/android/telecom/Connection.java b/telecomm/java/android/telecom/Connection.java
index bd0d4ae..ae12a17 100644
--- a/telecomm/java/android/telecom/Connection.java
+++ b/telecomm/java/android/telecom/Connection.java
@@ -623,7 +623,7 @@
             "android.telecom.event.HANDOVER_FAILED";
 
     /**
-     * Connection extra key used to store SIP invite fields for an incoming call for IMS calls
+     * String Connection extra key used to store SIP invite fields for an incoming call for IMS call
      */
     public static final String EXTRA_SIP_INVITE = "android.telecom.extra.SIP_INVITE";
 
diff --git a/telephony/java/android/provider/Telephony.java b/telephony/java/android/provider/Telephony.java
index a1c32b5..2462bee 100644
--- a/telephony/java/android/provider/Telephony.java
+++ b/telephony/java/android/provider/Telephony.java
@@ -2123,6 +2123,11 @@
      * @hide - not meant for public use
      */
     public interface RcsColumns {
+        // TODO(sahinc): Turn this to true once the schema finalizes, so that people can update
+        //  their messaging databases. NOTE: move the switch/case update in MmsSmsDatabaseHelper to
+        //  the latest version of the database before turning this flag to true.
+        boolean IS_RCS_TABLE_SCHEMA_CODE_COMPLETE = false;
+
         /**
          * The authority for the content provider
          */
diff --git a/telephony/java/android/telephony/SmsMessage.java b/telephony/java/android/telephony/SmsMessage.java
index 099015f..2aa4768 100644
--- a/telephony/java/android/telephony/SmsMessage.java
+++ b/telephony/java/android/telephony/SmsMessage.java
@@ -981,4 +981,13 @@
 
         return false;
     }
+
+    /**
+     * {@hide}
+     * Returns the recipient address(receiver) of this SMS message in String form or null if
+     * unavailable.
+     */
+    public String getRecipientAddress() {
+        return mWrappedSmsMessage.getRecipientAddress();
+    }
 }
diff --git a/telephony/java/android/telephony/SubscriptionManager.java b/telephony/java/android/telephony/SubscriptionManager.java
index 836a50b..94f26a8 100644
--- a/telephony/java/android/telephony/SubscriptionManager.java
+++ b/telephony/java/android/telephony/SubscriptionManager.java
@@ -1236,7 +1236,7 @@
     @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges
     @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
     public List<SubscriptionInfo> getActiveSubscriptionInfoList() {
-        return getActiveSubscriptionInfoList(false);
+        return getActiveSubscriptionInfoList(/* userVisibleonly */true);
     }
 
     /**
@@ -2858,15 +2858,24 @@
     /**
      * Whether system UI should hide a subscription. If it's a bundled opportunistic
      * subscription, it shouldn't show up in anywhere in Settings app, dialer app,
-     * or status bar.
+     * or status bar. Exception is if caller is carrier app, in which case they will
+     * want to see their own hidden subscriptions.
      *
      * @param info the subscriptionInfo to check against.
      * @return true if this subscription should be hidden.
      *
      * @hide
      */
-    public static boolean shouldHideSubscription(SubscriptionInfo info) {
-        return (info != null && !TextUtils.isEmpty(info.getGroupUuid()) && info.isOpportunistic());
+    private boolean shouldHideSubscription(SubscriptionInfo info) {
+        if (info == null) return false;
+
+        // If hasCarrierPrivileges or canManageSubscription returns true, it means caller
+        // has carrier privilege.
+        boolean hasCarrierPrivilegePermission = (info.isEmbedded() && canManageSubscription(info))
+                || TelephonyManager.from(mContext).hasCarrierPrivileges(info.getSubscriptionId());
+
+        return (!TextUtils.isEmpty(info.getGroupUuid()) && info.isOpportunistic()
+                && !hasCarrierPrivilegePermission);
     }
 
     /**
diff --git a/telephony/java/android/telephony/ims/ImsCallProfile.java b/telephony/java/android/telephony/ims/ImsCallProfile.java
index 73f0556..d5c7079 100644
--- a/telephony/java/android/telephony/ims/ImsCallProfile.java
+++ b/telephony/java/android/telephony/ims/ImsCallProfile.java
@@ -266,6 +266,11 @@
     public static final String EXTRA_DISPLAY_TEXT = "DisplayText";
     public static final String EXTRA_ADDITIONAL_CALL_INFO = "AdditionalCallInfo";
     public static final String EXTRA_IS_CALL_PULL = "CallPull";
+
+    /**
+     * String extra property
+     *  Containing fields from the SIP INVITE message for an IMS call
+     */
     public static final String EXTRA_ADDITIONAL_SIP_INVITE_FIELDS =
                                   "android.telephony.ims.extra.ADDITIONAL_SIP_INVITE_FIELDS";
 
diff --git a/telephony/java/com/android/internal/telephony/SmsMessageBase.java b/telephony/java/com/android/internal/telephony/SmsMessageBase.java
index 190eac4..ffdc4b6 100644
--- a/telephony/java/com/android/internal/telephony/SmsMessageBase.java
+++ b/telephony/java/com/android/internal/telephony/SmsMessageBase.java
@@ -41,6 +41,9 @@
     @UnsupportedAppUsage
     protected SmsAddress mOriginatingAddress;
 
+    /** {@hide} The address of the receiver */
+    protected SmsAddress mRecipientAddress;
+
     /** {@hide} The message body as a string. May be null if the message isn't text */
     @UnsupportedAppUsage
     protected String mMessageBody;
@@ -457,4 +460,17 @@
 
         return ted;
     }
+
+    /**
+     * {@hide}
+     * Returns the receiver address of this SMS message in String
+     * form or null if unavailable
+     */
+    public String getRecipientAddress() {
+        if (mRecipientAddress == null) {
+            return null;
+        }
+
+        return mRecipientAddress.getAddressString();
+    }
 }
diff --git a/telephony/java/com/android/internal/telephony/cdma/SmsMessage.java b/telephony/java/com/android/internal/telephony/cdma/SmsMessage.java
index 1da5eac..a31fa0b 100644
--- a/telephony/java/com/android/internal/telephony/cdma/SmsMessage.java
+++ b/telephony/java/com/android/internal/telephony/cdma/SmsMessage.java
@@ -601,18 +601,24 @@
 
                             } else if (addr.numberMode == CdmaSmsAddress.NUMBER_MODE_DATA_NETWORK) {
                                 if (numberType == 2)
-                                    Rlog.e(LOG_TAG, "TODO: Originating Addr is email id");
+                                    Rlog.e(LOG_TAG, "TODO: Addr is email id");
                                 else
                                     Rlog.e(LOG_TAG,
-                                          "TODO: Originating Addr is data network address");
+                                          "TODO: Addr is data network address");
                             } else {
-                                Rlog.e(LOG_TAG, "Originating Addr is of incorrect type");
+                                Rlog.e(LOG_TAG, "Addr is of incorrect type");
                             }
                         } else {
                             Rlog.e(LOG_TAG, "Incorrect Digit mode");
                         }
                         addr.origBytes = data;
-                        Rlog.i(LOG_TAG, "Originating Addr=" + addr.toString());
+                        Rlog.pii(LOG_TAG, "Addr=" + addr.toString());
+                        mOriginatingAddress = addr;
+                        if (parameterId == DESTINATION_ADDRESS) {
+                            // Original address awlays indicates one sender's address for 3GPP2
+                            // Here add recipient address support along with 3GPP
+                            mRecipientAddress = addr;
+                        }
                         break;
                     case ORIGINATING_SUB_ADDRESS:
                     case DESTINATION_SUB_ADDRESS:
@@ -667,7 +673,7 @@
     }
 
     /**
-     * Parses a SMS message from its BearerData stream. (mobile-terminated only)
+     * Parses a SMS message from its BearerData stream.
      */
     public void parseSms() {
         // Message Waiting Info Record defined in 3GPP2 C.S-0005, 3.7.5.6
@@ -697,16 +703,15 @@
         }
 
         if (mOriginatingAddress != null) {
-            mOriginatingAddress.address = new String(mOriginatingAddress.origBytes);
-            if (mOriginatingAddress.ton == CdmaSmsAddress.TON_INTERNATIONAL_OR_IP) {
-                if (mOriginatingAddress.address.charAt(0) != '+') {
-                    mOriginatingAddress.address = "+" + mOriginatingAddress.address;
-                }
-            }
+            decodeSmsDisplayAddress(mOriginatingAddress);
             if (VDBG) Rlog.v(LOG_TAG, "SMS originating address: "
                     + mOriginatingAddress.address);
         }
 
+        if (mRecipientAddress != null) {
+            decodeSmsDisplayAddress(mRecipientAddress);
+        }
+
         if (mBearerData.msgCenterTimeStamp != null) {
             mScTimeMillis = mBearerData.msgCenterTimeStamp.toMillis(true);
         }
@@ -731,7 +736,8 @@
                 status = mBearerData.errorClass << 8;
                 status |= mBearerData.messageStatus;
             }
-        } else if (mBearerData.messageType != BearerData.MESSAGE_TYPE_DELIVER) {
+        } else if (mBearerData.messageType != BearerData.MESSAGE_TYPE_DELIVER
+                && mBearerData.messageType != BearerData.MESSAGE_TYPE_SUBMIT) {
             throw new RuntimeException("Unsupported message type: " + mBearerData.messageType);
         }
 
@@ -743,6 +749,16 @@
         }
     }
 
+    private void decodeSmsDisplayAddress(SmsAddress addr) {
+        addr.address = new String(addr.origBytes);
+        if (addr.ton == CdmaSmsAddress.TON_INTERNATIONAL_OR_IP) {
+            if (addr.address.charAt(0) != '+') {
+                addr.address = "+" + addr.address;
+            }
+        }
+        Rlog.pii(LOG_TAG, " decodeSmsDisplayAddress = " + addr.address);
+    }
+
     /**
      * Parses a broadcast SMS, possibly containing a CMAS alert.
      *
diff --git a/telephony/java/com/android/internal/telephony/gsm/SmsMessage.java b/telephony/java/com/android/internal/telephony/gsm/SmsMessage.java
index 015efa6..19465a4 100644
--- a/telephony/java/com/android/internal/telephony/gsm/SmsMessage.java
+++ b/telephony/java/com/android/internal/telephony/gsm/SmsMessage.java
@@ -71,9 +71,6 @@
     // e.g. 23.040 9.2.2.1
     private boolean mReplyPathPresent = false;
 
-    /** The address of the receiver. */
-    private GsmSmsAddress mRecipientAddress;
-
     /**
      *  TP-Status - status of a previously submitted SMS.
      *  This field applies to SMS-STATUS-REPORT messages.  0 indicates success;
diff --git a/test-mock/api/test-current.txt b/test-mock/api/test-current.txt
index ab10800..0cb8f22 100644
--- a/test-mock/api/test-current.txt
+++ b/test-mock/api/test-current.txt
@@ -9,10 +9,12 @@
     method public java.util.List<android.content.pm.PackageInfo> getInstalledPackagesAsUser(int, int);
     method public String[] getNamesForUids(int[]);
     method public String getPermissionControllerPackageName();
+    method public int getPermissionFlags(String, String, android.os.UserHandle);
     method @NonNull public String getServicesSystemSharedLibraryPackageName();
     method @NonNull public String getSharedSystemSharedLibraryPackageName();
     method public void grantRuntimePermission(String, String, android.os.UserHandle);
     method public void revokeRuntimePermission(String, String, android.os.UserHandle);
+    method public void updatePermissionFlags(String, String, int, int, android.os.UserHandle);
   }
 
 }
diff --git a/tests/net/java/com/android/server/ConnectivityServiceTest.java b/tests/net/java/com/android/server/ConnectivityServiceTest.java
index e57d838..a10fb4e 100644
--- a/tests/net/java/com/android/server/ConnectivityServiceTest.java
+++ b/tests/net/java/com/android/server/ConnectivityServiceTest.java
@@ -20,6 +20,7 @@
 import static android.net.ConnectivityManager.PRIVATE_DNS_MODE_OFF;
 import static android.net.ConnectivityManager.PRIVATE_DNS_MODE_OPPORTUNISTIC;
 import static android.net.ConnectivityManager.PRIVATE_DNS_MODE_PROVIDER_HOSTNAME;
+import static android.net.ConnectivityManager.NETID_UNSET;
 import static android.net.ConnectivityManager.TYPE_ETHERNET;
 import static android.net.ConnectivityManager.TYPE_MOBILE;
 import static android.net.ConnectivityManager.TYPE_MOBILE_FOTA;
@@ -123,7 +124,7 @@
 import android.net.NetworkParcelable;
 import android.net.NetworkRequest;
 import android.net.NetworkSpecifier;
-import android.net.NetworkStack;
+import android.net.NetworkStackClient;
 import android.net.NetworkUtils;
 import android.net.ProxyInfo;
 import android.net.RouteInfo;
@@ -245,7 +246,7 @@
     @Mock INetworkStatsService mStatsService;
     @Mock INetworkPolicyManager mNpm;
     @Mock INetd mMockNetd;
-    @Mock NetworkStack mNetworkStack;
+    @Mock NetworkStackClient mNetworkStack;
 
     private ArgumentCaptor<String[]> mStringArrayCaptor = ArgumentCaptor.forClass(String[].class);
 
@@ -901,11 +902,14 @@
 
         public void setUids(Set<UidRange> uids) {
             mNetworkCapabilities.setUids(uids);
-            updateCapabilities();
+            updateCapabilities(null /* defaultNetwork */);
         }
 
         @Override
         public int getNetId() {
+            if (mMockNetworkAgent == null) {
+                return NETID_UNSET;
+            }
             return mMockNetworkAgent.getNetwork().netId;
         }
 
@@ -927,12 +931,13 @@
         }
 
         @Override
-        public void updateCapabilities() {
-            if (!mConnected) return;
-            super.updateCapabilities();
-            // Because super.updateCapabilities will update the capabilities of the agent but not
-            // the mock agent, the mock agent needs to know about them.
+        public NetworkCapabilities updateCapabilities(Network defaultNetwork) {
+            if (!mConnected) return null;
+            super.updateCapabilities(defaultNetwork);
+            // Because super.updateCapabilities will update the capabilities of the agent but
+            // not the mock agent, the mock agent needs to know about them.
             copyCapabilitiesToNetworkAgent();
+            return new NetworkCapabilities(mNetworkCapabilities);
         }
 
         private void copyCapabilitiesToNetworkAgent() {
@@ -1077,6 +1082,11 @@
         }
 
         @Override
+        protected NetworkStackClient getNetworkStack() {
+            return mNetworkStack;
+        }
+
+        @Override
         public WakeupMessage makeWakeupMessage(
                 Context context, Handler handler, String cmdName, int cmd, Object obj) {
             return new FakeWakeupMessage(context, handler, cmdName, cmd, 0, 0, obj);
@@ -4690,6 +4700,7 @@
 
         vpnNetworkAgent.connect(false);
         mMockVpn.connect();
+        mMockVpn.setUnderlyingNetworks(new Network[0]);
 
         genericNetworkCallback.expectAvailableCallbacksUnvalidated(vpnNetworkAgent);
         genericNotVpnNetworkCallback.assertNoCallback();
@@ -4722,6 +4733,7 @@
 
         ranges.add(new UidRange(uid, uid));
         mMockVpn.setUids(ranges);
+        vpnNetworkAgent.setUids(ranges);
 
         genericNetworkCallback.expectAvailableCallbacksValidated(vpnNetworkAgent);
         genericNotVpnNetworkCallback.assertNoCallback();
@@ -4755,12 +4767,11 @@
     }
 
     @Test
-    public void testVpnWithAndWithoutInternet() {
+    public void testVpnWithoutInternet() {
         final int uid = Process.myUid();
 
         final TestNetworkCallback defaultCallback = new TestNetworkCallback();
         mCm.registerDefaultNetworkCallback(defaultCallback);
-        defaultCallback.assertNoCallback();
 
         mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
         mWiFiNetworkAgent.connect(true);
@@ -4782,11 +4793,30 @@
         vpnNetworkAgent.disconnect();
         defaultCallback.assertNoCallback();
 
-        vpnNetworkAgent = new MockNetworkAgent(TRANSPORT_VPN);
+        mCm.unregisterNetworkCallback(defaultCallback);
+    }
+
+    @Test
+    public void testVpnWithInternet() {
+        final int uid = Process.myUid();
+
+        final TestNetworkCallback defaultCallback = new TestNetworkCallback();
+        mCm.registerDefaultNetworkCallback(defaultCallback);
+
+        mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
+        mWiFiNetworkAgent.connect(true);
+
+        defaultCallback.expectAvailableThenValidatedCallbacks(mWiFiNetworkAgent);
+        assertEquals(defaultCallback.getLastAvailableNetwork(), mCm.getActiveNetwork());
+
+        MockNetworkAgent vpnNetworkAgent = new MockNetworkAgent(TRANSPORT_VPN);
+        final ArraySet<UidRange> ranges = new ArraySet<>();
+        ranges.add(new UidRange(uid, uid));
         mMockVpn.setNetworkAgent(vpnNetworkAgent);
         mMockVpn.setUids(ranges);
         vpnNetworkAgent.connect(true /* validated */, true /* hasInternet */);
         mMockVpn.connect();
+
         defaultCallback.expectAvailableThenValidatedCallbacks(vpnNetworkAgent);
         assertEquals(defaultCallback.getLastAvailableNetwork(), mCm.getActiveNetwork());
 
@@ -4794,14 +4824,6 @@
         defaultCallback.expectCallback(CallbackState.LOST, vpnNetworkAgent);
         defaultCallback.expectAvailableCallbacksValidated(mWiFiNetworkAgent);
 
-        vpnNetworkAgent = new MockNetworkAgent(TRANSPORT_VPN);
-        ranges.clear();
-        mMockVpn.setNetworkAgent(vpnNetworkAgent);
-        mMockVpn.setUids(ranges);
-        vpnNetworkAgent.connect(false /* validated */, true /* hasInternet */);
-        mMockVpn.connect();
-        defaultCallback.assertNoCallback();
-
         mCm.unregisterNetworkCallback(defaultCallback);
     }
 
@@ -4904,6 +4926,70 @@
     }
 
     @Test
+    public void testNullUnderlyingNetworks() {
+        final int uid = Process.myUid();
+
+        final TestNetworkCallback vpnNetworkCallback = new TestNetworkCallback();
+        final NetworkRequest vpnNetworkRequest = new NetworkRequest.Builder()
+                .removeCapability(NET_CAPABILITY_NOT_VPN)
+                .addTransportType(TRANSPORT_VPN)
+                .build();
+        NetworkCapabilities nc;
+        mCm.registerNetworkCallback(vpnNetworkRequest, vpnNetworkCallback);
+        vpnNetworkCallback.assertNoCallback();
+
+        final MockNetworkAgent vpnNetworkAgent = new MockNetworkAgent(TRANSPORT_VPN);
+        final ArraySet<UidRange> ranges = new ArraySet<>();
+        ranges.add(new UidRange(uid, uid));
+        mMockVpn.setNetworkAgent(vpnNetworkAgent);
+        mMockVpn.connect();
+        mMockVpn.setUids(ranges);
+        vpnNetworkAgent.connect(true /* validated */, false /* hasInternet */);
+
+        vpnNetworkCallback.expectAvailableThenValidatedCallbacks(vpnNetworkAgent);
+        nc = mCm.getNetworkCapabilities(vpnNetworkAgent.getNetwork());
+        assertTrue(nc.hasTransport(TRANSPORT_VPN));
+        assertFalse(nc.hasTransport(TRANSPORT_CELLULAR));
+        assertFalse(nc.hasTransport(TRANSPORT_WIFI));
+        // By default, VPN is set to track default network (i.e. its underlying networks is null).
+        // In case of no default network, VPN is considered metered.
+        assertFalse(nc.hasCapability(NET_CAPABILITY_NOT_METERED));
+
+        // Connect to Cell; Cell is the default network.
+        mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR);
+        mCellNetworkAgent.connect(true);
+
+        vpnNetworkCallback.expectCapabilitiesLike((caps) -> caps.hasTransport(TRANSPORT_VPN)
+                && caps.hasTransport(TRANSPORT_CELLULAR) && !caps.hasTransport(TRANSPORT_WIFI)
+                && !caps.hasCapability(NET_CAPABILITY_NOT_METERED),
+                vpnNetworkAgent);
+
+        // Connect to WiFi; WiFi is the new default.
+        mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
+        mWiFiNetworkAgent.addCapability(NET_CAPABILITY_NOT_METERED);
+        mWiFiNetworkAgent.connect(true);
+
+        vpnNetworkCallback.expectCapabilitiesLike((caps) -> caps.hasTransport(TRANSPORT_VPN)
+                && !caps.hasTransport(TRANSPORT_CELLULAR) && caps.hasTransport(TRANSPORT_WIFI)
+                && caps.hasCapability(NET_CAPABILITY_NOT_METERED),
+                vpnNetworkAgent);
+
+        // Disconnect Cell. The default network did not change, so there shouldn't be any changes in
+        // the capabilities.
+        mCellNetworkAgent.disconnect();
+
+        // Disconnect wifi too. Now we have no default network.
+        mWiFiNetworkAgent.disconnect();
+
+        vpnNetworkCallback.expectCapabilitiesLike((caps) -> caps.hasTransport(TRANSPORT_VPN)
+                && !caps.hasTransport(TRANSPORT_CELLULAR) && !caps.hasTransport(TRANSPORT_WIFI)
+                && !caps.hasCapability(NET_CAPABILITY_NOT_METERED),
+                vpnNetworkAgent);
+
+        mMockVpn.disconnect();
+    }
+
+    @Test
     public void testNetworkBlockedStatus() {
         final TestNetworkCallback cellNetworkCallback = new TestNetworkCallback();
         final NetworkRequest cellRequest = new NetworkRequest.Builder()
diff --git a/tests/net/java/com/android/server/connectivity/LingerMonitorTest.java b/tests/net/java/com/android/server/connectivity/LingerMonitorTest.java
index e877a8f..5057443 100644
--- a/tests/net/java/com/android/server/connectivity/LingerMonitorTest.java
+++ b/tests/net/java/com/android/server/connectivity/LingerMonitorTest.java
@@ -38,7 +38,6 @@
 import android.net.NetworkFactory;
 import android.net.NetworkInfo;
 import android.net.NetworkMisc;
-import android.net.NetworkStack;
 import android.os.INetworkManagementService;
 import android.support.test.filters.SmallTest;
 import android.support.test.runner.AndroidJUnit4;
@@ -75,16 +74,12 @@
     @Mock NetworkMisc mMisc;
     @Mock NetworkNotificationManager mNotifier;
     @Mock Resources mResources;
-    @Mock NetworkStack mNetworkStack;
 
     @Before
     public void setUp() {
         MockitoAnnotations.initMocks(this);
         when(mCtx.getResources()).thenReturn(mResources);
         when(mCtx.getPackageName()).thenReturn("com.android.server.connectivity");
-        when(mCtx.getSystemServiceName(NetworkStack.class))
-                .thenReturn(Context.NETWORK_STACK_SERVICE);
-        when(mCtx.getSystemService(Context.NETWORK_STACK_SERVICE)).thenReturn(mNetworkStack);
 
         mMonitor = new TestableLingerMonitor(mCtx, mNotifier, HIGH_DAILY_LIMIT, HIGH_RATE_LIMIT);
     }
diff --git a/tests/net/java/com/android/server/connectivity/TetheringTest.java b/tests/net/java/com/android/server/connectivity/TetheringTest.java
index a4a735d..533d7ad 100644
--- a/tests/net/java/com/android/server/connectivity/TetheringTest.java
+++ b/tests/net/java/com/android/server/connectivity/TetheringTest.java
@@ -195,10 +195,6 @@
     }
 
     public class MockIpServerDependencies extends IpServer.Dependencies {
-        MockIpServerDependencies() {
-            super(null);
-        }
-
         @Override
         public RouterAdvertisementDaemon getRouterAdvertisementDaemon(
                 InterfaceParams ifParams) {
@@ -266,7 +262,7 @@
         }
 
         @Override
-        public IpServer.Dependencies getIpServerDependencies(Context context) {
+        public IpServer.Dependencies getIpServerDependencies() {
             return mIpServerDependencies;
         }
 
diff --git a/tests/net/java/com/android/server/connectivity/VpnTest.java b/tests/net/java/com/android/server/connectivity/VpnTest.java
index 46de3d0..f169d6b 100644
--- a/tests/net/java/com/android/server/connectivity/VpnTest.java
+++ b/tests/net/java/com/android/server/connectivity/VpnTest.java
@@ -566,7 +566,7 @@
 
         final NetworkCapabilities caps = new NetworkCapabilities();
 
-        Vpn.updateCapabilities(
+        Vpn.applyUnderlyingCapabilities(
                 mConnectivityManager, new Network[] {}, caps, false /* isAlwaysMetered */);
         assertTrue(caps.hasTransport(TRANSPORT_VPN));
         assertFalse(caps.hasTransport(TRANSPORT_CELLULAR));
@@ -577,7 +577,7 @@
         assertTrue(caps.hasCapability(NET_CAPABILITY_NOT_ROAMING));
         assertTrue(caps.hasCapability(NET_CAPABILITY_NOT_CONGESTED));
 
-        Vpn.updateCapabilities(
+        Vpn.applyUnderlyingCapabilities(
                 mConnectivityManager,
                 new Network[] {mobile},
                 caps,
@@ -591,7 +591,7 @@
         assertFalse(caps.hasCapability(NET_CAPABILITY_NOT_ROAMING));
         assertTrue(caps.hasCapability(NET_CAPABILITY_NOT_CONGESTED));
 
-        Vpn.updateCapabilities(
+        Vpn.applyUnderlyingCapabilities(
                 mConnectivityManager, new Network[] {wifi}, caps, false /* isAlwaysMetered */);
         assertTrue(caps.hasTransport(TRANSPORT_VPN));
         assertFalse(caps.hasTransport(TRANSPORT_CELLULAR));
@@ -602,7 +602,7 @@
         assertTrue(caps.hasCapability(NET_CAPABILITY_NOT_ROAMING));
         assertTrue(caps.hasCapability(NET_CAPABILITY_NOT_CONGESTED));
 
-        Vpn.updateCapabilities(
+        Vpn.applyUnderlyingCapabilities(
                 mConnectivityManager, new Network[] {wifi}, caps, true /* isAlwaysMetered */);
         assertTrue(caps.hasTransport(TRANSPORT_VPN));
         assertFalse(caps.hasTransport(TRANSPORT_CELLULAR));
@@ -613,7 +613,7 @@
         assertTrue(caps.hasCapability(NET_CAPABILITY_NOT_ROAMING));
         assertTrue(caps.hasCapability(NET_CAPABILITY_NOT_CONGESTED));
 
-        Vpn.updateCapabilities(
+        Vpn.applyUnderlyingCapabilities(
                 mConnectivityManager,
                 new Network[] {mobile, wifi},
                 caps,
diff --git a/tools/aapt2/Debug.cpp b/tools/aapt2/Debug.cpp
index 5f664f5..9832485 100644
--- a/tools/aapt2/Debug.cpp
+++ b/tools/aapt2/Debug.cpp
@@ -435,7 +435,7 @@
   const size_t NS = pool->size();
   for (size_t s=0; s<NS; s++) {
     String8 str = pool->string8ObjectAt(s);
-    printer->Print(StringPrintf("String #%zd: %s\n", s, str.string()));
+    printer->Print(StringPrintf("String #%zd : %s\n", s, str.string()));
   }
 }
 
diff --git a/tools/aapt2/ResourceParser.cpp b/tools/aapt2/ResourceParser.cpp
index 2f8ca2d..c188782 100644
--- a/tools/aapt2/ResourceParser.cpp
+++ b/tools/aapt2/ResourceParser.cpp
@@ -1084,7 +1084,7 @@
   // Create a overlayable entry grouping that represents this <overlayable>
   auto overlayable = std::make_shared<Overlayable>(
       overlayable_name.value(), (overlayable_actor) ? overlayable_actor.value() : "",
-      out_resource->source);
+      source_);
 
   bool error = false;
   std::string comment;
@@ -1113,6 +1113,13 @@
     const std::string& element_name = parser->element_name();
     const std::string& element_namespace = parser->element_namespace();
     if (element_namespace.empty() && element_name == "item") {
+      if (current_policies == OverlayableItem::Policy::kNone) {
+        diag_->Error(DiagMessage(element_source)
+                         << "<item> within an <overlayable> must be inside a <policy> block");
+        error = true;
+        continue;
+      }
+
       // Items specify the name and type of resource that should be overlayable
       Maybe<StringPiece> item_name = xml::FindNonEmptyAttribute(parser, "name");
       if (!item_name) {
@@ -1169,6 +1176,8 @@
             current_policies |= OverlayableItem::Policy::kSystem;
           } else if (trimmed_part == "vendor") {
             current_policies |= OverlayableItem::Policy::kVendor;
+          } else if (trimmed_part == "signature") {
+            current_policies |= OverlayableItem::Policy::kSignature;
           } else {
             diag_->Error(DiagMessage(element_source)
                          << "<policy> has unsupported type '" << trimmed_part << "'");
@@ -1176,6 +1185,11 @@
             continue;
           }
         }
+      } else {
+        diag_->Error(DiagMessage(element_source)
+                         << "<policy> must have a 'type' attribute");
+        error = true;
+        continue;
       }
     } else if (!ShouldIgnoreElement(element_namespace, element_name)) {
       diag_->Error(DiagMessage(element_source) << "invalid element <" << element_name << "> "
diff --git a/tools/aapt2/ResourceParser_test.cpp b/tools/aapt2/ResourceParser_test.cpp
index 827c7de..25b76b0 100644
--- a/tools/aapt2/ResourceParser_test.cpp
+++ b/tools/aapt2/ResourceParser_test.cpp
@@ -894,8 +894,10 @@
 TEST_F(ResourceParserTest, ParseOverlayable) {
   std::string input = R"(
       <overlayable name="Name" actor="overlay://theme">
-        <item type="string" name="foo" />
-        <item type="drawable" name="bar" />
+          <policy type="signature">
+            <item type="string" name="foo" />
+            <item type="drawable" name="bar" />
+          </policy>
       </overlayable>)";
   ASSERT_TRUE(TestParse(input));
 
@@ -906,7 +908,7 @@
   OverlayableItem& result_overlayable_item = search_result.value().entry->overlayable_item.value();
   EXPECT_THAT(result_overlayable_item.overlayable->name, Eq("Name"));
   EXPECT_THAT(result_overlayable_item.overlayable->actor, Eq("overlay://theme"));
-  EXPECT_THAT(result_overlayable_item.policies, Eq(OverlayableItem::Policy::kNone));
+  EXPECT_THAT(result_overlayable_item.policies, Eq(OverlayableItem::Policy::kSignature));
 
   search_result = table_.FindResource(test::ParseNameOrDie("drawable/bar"));
   ASSERT_TRUE(search_result);
@@ -915,7 +917,7 @@
   result_overlayable_item = search_result.value().entry->overlayable_item.value();
   EXPECT_THAT(result_overlayable_item.overlayable->name, Eq("Name"));
   EXPECT_THAT(result_overlayable_item.overlayable->actor, Eq("overlay://theme"));
-  EXPECT_THAT(result_overlayable_item.policies, Eq(OverlayableItem::Policy::kNone));
+  EXPECT_THAT(result_overlayable_item.policies, Eq(OverlayableItem::Policy::kSignature));
 }
 
 TEST_F(ResourceParserTest, ParseOverlayableRequiresName) {
@@ -931,7 +933,6 @@
 TEST_F(ResourceParserTest, ParseOverlayablePolicy) {
   std::string input = R"(
       <overlayable name="Name">
-        <item type="string" name="foo" />
         <policy type="product">
           <item type="string" name="bar" />
         </policy>
@@ -944,23 +945,18 @@
         <policy type="public">
           <item type="string" name="faz" />
         </policy>
+        <policy type="signature">
+          <item type="string" name="foz" />
+        </policy>
       </overlayable>)";
   ASSERT_TRUE(TestParse(input));
 
-  auto search_result = table_.FindResource(test::ParseNameOrDie("string/foo"));
+  auto search_result = table_.FindResource(test::ParseNameOrDie("string/bar"));
   ASSERT_TRUE(search_result);
   ASSERT_THAT(search_result.value().entry, NotNull());
   ASSERT_TRUE(search_result.value().entry->overlayable_item);
   OverlayableItem result_overlayable_item = search_result.value().entry->overlayable_item.value();
   EXPECT_THAT(result_overlayable_item.overlayable->name, Eq("Name"));
-  EXPECT_THAT(result_overlayable_item.policies, Eq(OverlayableItem::Policy::kNone));
-
-  search_result = table_.FindResource(test::ParseNameOrDie("string/bar"));
-  ASSERT_TRUE(search_result);
-  ASSERT_THAT(search_result.value().entry, NotNull());
-  ASSERT_TRUE(search_result.value().entry->overlayable_item);
-  result_overlayable_item = search_result.value().entry->overlayable_item.value();
-  EXPECT_THAT(result_overlayable_item.overlayable->name, Eq("Name"));
   EXPECT_THAT(result_overlayable_item.policies, Eq(OverlayableItem::Policy::kProduct));
 
   search_result = table_.FindResource(test::ParseNameOrDie("string/fiz"));
@@ -986,6 +982,30 @@
   result_overlayable_item = search_result.value().entry->overlayable_item.value();
   EXPECT_THAT(result_overlayable_item.overlayable->name, Eq("Name"));
   EXPECT_THAT(result_overlayable_item.policies, Eq(OverlayableItem::Policy::kPublic));
+
+  search_result = table_.FindResource(test::ParseNameOrDie("string/foz"));
+  ASSERT_TRUE(search_result);
+  ASSERT_THAT(search_result.value().entry, NotNull());
+  ASSERT_TRUE(search_result.value().entry->overlayable_item);
+  result_overlayable_item = search_result.value().entry->overlayable_item.value();
+  EXPECT_THAT(result_overlayable_item.overlayable->name, Eq("Name"));
+  EXPECT_THAT(result_overlayable_item.policies, Eq(OverlayableItem::Policy::kSignature));
+}
+
+TEST_F(ResourceParserTest, ParseOverlayableNoPolicyError) {
+  std::string input = R"(
+      <overlayable name="Name">
+        <item type="string" name="foo" />
+      </overlayable>)";
+  EXPECT_FALSE(TestParse(input));
+
+  input = R"(
+      <overlayable name="Name">
+        <policy>
+          <item name="foo" />
+        </policy>
+      </overlayable>)";
+  EXPECT_FALSE(TestParse(input));
 }
 
 TEST_F(ResourceParserTest, ParseOverlayableBadPolicyError) {
diff --git a/tools/aapt2/ResourceTable.h b/tools/aapt2/ResourceTable.h
index 7ca99ea..32dfd26 100644
--- a/tools/aapt2/ResourceTable.h
+++ b/tools/aapt2/ResourceTable.h
@@ -92,6 +92,9 @@
 
     // The resource can be overlaid by any overlay on the product partition.
     kProduct = 0x08,
+
+    // The resource can be overlaid by any overlay signed with the same signature as its actor.
+    kSignature = 0x010,
   };
 
   std::shared_ptr<Overlayable> overlayable;
diff --git a/tools/aapt2/ResourceUtils.cpp b/tools/aapt2/ResourceUtils.cpp
index ab4805f..0032960 100644
--- a/tools/aapt2/ResourceUtils.cpp
+++ b/tools/aapt2/ResourceUtils.cpp
@@ -727,7 +727,7 @@
           // This must be a FileReference.
           std::unique_ptr<FileReference> file_ref =
               util::make_unique<FileReference>(dst_pool->MakeRef(
-                  str, StringPool::Context(StringPool::Context::kHighPriority, config), data));
+                  str, StringPool::Context(StringPool::Context::kHighPriority, config)));
           if (type == ResourceType::kRaw) {
             file_ref->type = ResourceFile::Type::kUnknown;
           } else if (util::EndsWith(*file_ref->path, ".xml")) {
@@ -739,7 +739,7 @@
         }
 
         // There are no styles associated with this string, so treat it as a simple string.
-        return util::make_unique<String>(dst_pool->MakeRef(str, StringPool::Context(config), data));
+        return util::make_unique<String>(dst_pool->MakeRef(str, StringPool::Context(config)));
       }
     } break;
 
diff --git a/tools/aapt2/Resources.proto b/tools/aapt2/Resources.proto
index 9a1d942..a2fd7c6 100644
--- a/tools/aapt2/Resources.proto
+++ b/tools/aapt2/Resources.proto
@@ -138,10 +138,10 @@
 
 // Represents a set of overlayable resources.
 message Overlayable {
-  // The name of the <overlyabale>.
+  // The name of the <overlayable>.
   string name = 1;
 
-  // The location of the <overlyabale> declaration in the source.
+  // The location of the <overlayable> declaration in the source.
   Source source = 2;
 
   // The component responsible for enabling and disabling overlays targeting this <overlayable>.
diff --git a/tools/aapt2/StringPool.cpp b/tools/aapt2/StringPool.cpp
index a8c2666..8eabd32 100644
--- a/tools/aapt2/StringPool.cpp
+++ b/tools/aapt2/StringPool.cpp
@@ -165,13 +165,12 @@
   return MakeRefImpl(str, Context{}, true);
 }
 
-StringPool::Ref StringPool::MakeRef(const StringPiece& str, const Context& context,
-                                    Maybe<size_t> index) {
-  return MakeRefImpl(str, context, true, index);
+StringPool::Ref StringPool::MakeRef(const StringPiece& str, const Context& context) {
+  return MakeRefImpl(str, context, true);
 }
 
 StringPool::Ref StringPool::MakeRefImpl(const StringPiece& str, const Context& context,
-                                        bool unique, Maybe<size_t> index) {
+                                        bool unique) {
   if (unique) {
     auto range = indexed_strings_.equal_range(str);
     for (auto iter = range.first; iter != range.second; ++iter) {
@@ -181,26 +180,15 @@
     }
   }
 
-  const size_t size = strings_.size();
-  // Insert the string at the end of the string vector if no index is specified
-  const size_t insertion_index = index ? index.value() : size;
-
   std::unique_ptr<Entry> entry(new Entry());
   entry->value = str.to_string();
   entry->context = context;
-  entry->index_ = insertion_index;
+  entry->index_ = strings_.size();
   entry->ref_ = 0;
   entry->pool_ = this;
 
   Entry* borrow = entry.get();
-  if (insertion_index == size) {
-    strings_.emplace_back(std::move(entry));
-  } else {
-    // Allocate enough space for the string at the index
-    strings_.resize(std::max(insertion_index + 1, size));
-    strings_[insertion_index] = std::move(entry);
-  }
-
+  strings_.emplace_back(std::move(entry));
   indexed_strings_.insert(std::make_pair(StringPiece(borrow->value), borrow));
   return Ref(borrow);
 }
diff --git a/tools/aapt2/StringPool.h b/tools/aapt2/StringPool.h
index 115d5d3..1006ca9 100644
--- a/tools/aapt2/StringPool.h
+++ b/tools/aapt2/StringPool.h
@@ -166,8 +166,7 @@
 
   // Adds a string to the pool, unless it already exists, with a context object that can be used
   // when sorting the string pool. Returns a reference to the string in the pool.
-  Ref MakeRef(const android::StringPiece& str, const Context& context,
-              Maybe<size_t> index = {});
+  Ref MakeRef(const android::StringPiece& str, const Context& context);
 
   // Adds a string from another string pool. Returns a reference to the string in the string pool.
   Ref MakeRef(const Ref& ref);
@@ -211,8 +210,7 @@
 
   static bool Flatten(BigBuffer* out, const StringPool& pool, bool utf8, IDiagnostics* diag);
 
-  Ref MakeRefImpl(const android::StringPiece& str, const Context& context, bool unique,
-                  Maybe<size_t> index = {});
+  Ref MakeRefImpl(const android::StringPiece& str, const Context& context, bool unique);
   void ReAssignIndices();
 
   std::vector<std::unique_ptr<Entry>> strings_;
diff --git a/tools/aapt2/StringPool_test.cpp b/tools/aapt2/StringPool_test.cpp
index 648be7d..9a7238b 100644
--- a/tools/aapt2/StringPool_test.cpp
+++ b/tools/aapt2/StringPool_test.cpp
@@ -84,24 +84,6 @@
   EXPECT_THAT(ref_c.index(), Eq(2u));
 }
 
-TEST(StringPoolTest, AssignStringIndex) {
-  StringPool pool;
-
-  StringPool::Ref ref_a = pool.MakeRef("0", StringPool::Context{}, 0u);
-  StringPool::Ref ref_b = pool.MakeRef("1", StringPool::Context{}, 1u);
-  StringPool::Ref ref_c = pool.MakeRef("5", StringPool::Context{}, 5u);
-  StringPool::Ref ref_d = pool.MakeRef("2", StringPool::Context{}, 2u);
-  StringPool::Ref ref_e = pool.MakeRef("4", StringPool::Context{}, 4u);
-  StringPool::Ref ref_f = pool.MakeRef("3", StringPool::Context{}, 3u);
-
-  EXPECT_THAT(ref_a.index(), Eq(0u));
-  EXPECT_THAT(ref_b.index(), Eq(1u));
-  EXPECT_THAT(ref_d.index(), Eq(2u));
-  EXPECT_THAT(ref_f.index(), Eq(3u));
-  EXPECT_THAT(ref_e.index(), Eq(4u));
-  EXPECT_THAT(ref_c.index(), Eq(5u));
-}
-
 TEST(StringPoolTest, PruneStringsWithNoReferences) {
   StringPool pool;
 
diff --git a/tools/aapt2/cmd/Convert.cpp b/tools/aapt2/cmd/Convert.cpp
index 7a74ba9..0cf86cc 100644
--- a/tools/aapt2/cmd/Convert.cpp
+++ b/tools/aapt2/cmd/Convert.cpp
@@ -43,7 +43,8 @@
 
 class IApkSerializer {
  public:
-  IApkSerializer(IAaptContext* context, const Source& source) : context_(context), source_(source) {}
+  IApkSerializer(IAaptContext* context, const Source& source) : context_(context),
+                                                                source_(source) {}
 
   virtual bool SerializeXml(const xml::XmlResource* xml, const std::string& path, bool utf16,
                             IArchiveWriter* writer, uint32_t compression_flags) = 0;
@@ -167,7 +168,7 @@
       std::unique_ptr<io::IData> data = file->file->OpenAsData();
       if (!data) {
         context_->GetDiagnostics()->Error(DiagMessage(source_)
-                                         << "failed to open file " << *file->path);
+                                          << "failed to open file " << *file->path);
         return false;
       }
 
@@ -175,7 +176,7 @@
       std::unique_ptr<xml::XmlResource> xml = xml::Inflate(data->data(), data->size(), &error);
       if (xml == nullptr) {
         context_->GetDiagnostics()->Error(DiagMessage(source_) << "failed to parse binary XML: "
-                                          << error);
+                                                               << error);
         return false;
       }
 
@@ -256,9 +257,6 @@
 int Convert(IAaptContext* context, LoadedApk* apk, IArchiveWriter* output_writer,
             ApkFormat output_format, TableFlattenerOptions table_flattener_options,
             XmlFlattenerOptions xml_flattener_options) {
-  // Do not change the ordering of strings in the values string pool
-  table_flattener_options.sort_stringpool_entries = false;
-
   unique_ptr<IApkSerializer> serializer;
   if (output_format == ApkFormat::kBinary) {
     serializer.reset(new BinaryApkSerializer(context, apk->GetSource(), table_flattener_options,
@@ -274,7 +272,7 @@
   io::IFile* manifest = apk->GetFileCollection()->FindFile(kAndroidManifestPath);
   if (!serializer->SerializeXml(apk->GetManifest(), kAndroidManifestPath, true /*utf16*/,
                                 output_writer, (manifest != nullptr && manifest->WasCompressed())
-                                ? ArchiveEntry::kCompress : 0u)) {
+                                               ? ArchiveEntry::kCompress : 0u)) {
     context->GetDiagnostics()->Error(DiagMessage(apk->GetSource())
                                      << "failed to serialize AndroidManifest.xml");
     return 1;
@@ -303,8 +301,7 @@
               if (files_written.insert(*file->path).second) {
                 if (!serializer->SerializeFile(file, output_writer)) {
                   context->GetDiagnostics()->Error(DiagMessage(apk->GetSource())
-                                                       << "failed to serialize file "
-                                                       << *file->path);
+                                                   << "failed to serialize file " << *file->path);
                   return 1;
                 }
               }
@@ -338,7 +335,7 @@
 
     if (!io::CopyFileToArchivePreserveCompression(context, file, path, output_writer)) {
       context->GetDiagnostics()->Error(DiagMessage(apk->GetSource())
-                                       << "failed to copy file " << path);
+                                           << "failed to copy file " << path);
       return 1;
     }
   }
diff --git a/tools/aapt2/cmd/Link.cpp b/tools/aapt2/cmd/Link.cpp
index 8463046..4961aa5 100644
--- a/tools/aapt2/cmd/Link.cpp
+++ b/tools/aapt2/cmd/Link.cpp
@@ -400,7 +400,8 @@
 
 static bool IsVectorElement(const std::string& name) {
   return name == "vector" || name == "animated-vector" || name == "pathInterpolator" ||
-         name == "objectAnimator" || name == "gradient" || name == "animated-selector";
+         name == "objectAnimator" || name == "gradient" || name == "animated-selector" ||
+         name == "set";
 }
 
 template <typename T>
diff --git a/tools/aapt2/format/binary/BinaryResourceParser.cpp b/tools/aapt2/format/binary/BinaryResourceParser.cpp
index 40aaa05..59eb9ec 100644
--- a/tools/aapt2/format/binary/BinaryResourceParser.cpp
+++ b/tools/aapt2/format/binary/BinaryResourceParser.cpp
@@ -401,7 +401,6 @@
     if (entry->flags & ResTable_entry::FLAG_PUBLIC) {
       Visibility visibility;
       visibility.level = Visibility::Level::kPublic;
-      visibility.source = source_.WithLine(0);
       if (!table_->SetVisibilityWithIdMangled(name, visibility, res_id, diag_)) {
         return false;
       }
@@ -448,7 +447,6 @@
                                                       arraysize(header->name)));
   overlayable->actor = util::Utf16ToUtf8(strcpy16_dtoh((const char16_t*)header->actor,
                                                        arraysize(header->name)));
-  overlayable->source = source_.WithLine(0);
 
   ResChunkPullParser parser(GetChunkData(chunk),
                             GetChunkDataLen(chunk));
@@ -473,6 +471,10 @@
           & ResTable_overlayable_policy_header::POLICY_PRODUCT_PARTITION) {
         policies |= OverlayableItem::Policy::kProduct;
       }
+      if (policy_header->policy_flags
+          & ResTable_overlayable_policy_header::POLICY_SIGNATURE) {
+        policies |= OverlayableItem::Policy::kSignature;
+      }
 
       const ResTable_ref* const ref_begin = reinterpret_cast<const ResTable_ref*>(
           ((uint8_t *)policy_header) + util::DeviceToHost32(policy_header->header.headerSize));
@@ -491,7 +493,6 @@
         }
 
         OverlayableItem overlayable_item(overlayable);
-        overlayable_item.source = source_.WithLine(0);
         overlayable_item.policies = policies;
         if (!table_->SetOverlayable(iter->second, overlayable_item, diag_)) {
           return false;
diff --git a/tools/aapt2/format/binary/TableFlattener.cpp b/tools/aapt2/format/binary/TableFlattener.cpp
index 9d341cc..d677317 100644
--- a/tools/aapt2/format/binary/TableFlattener.cpp
+++ b/tools/aapt2/format/binary/TableFlattener.cpp
@@ -274,7 +274,9 @@
       FlattenLibrarySpec(buffer);
     }
 
-    FlattenOverlayable(buffer);
+    if (!FlattenOverlayable(buffer)) {
+      return false;
+    }
 
     pkg_writer.Finish();
     return true;
@@ -468,23 +470,29 @@
           overlayable_chunk = &chunk;
         }
 
+        if (item.policies == 0) {
+          context_->GetDiagnostics()->Error(DiagMessage(item.overlayable->source)
+                                                << "overlayable "
+                                                << entry->name
+                                                << " does not specify policy");
+          return false;
+        }
+
         uint32_t policy_flags = 0;
-        if (item.policies == OverlayableItem::Policy::kNone) {
-          // Encode overlayable entries defined without a policy as publicly overlayable
+        if (item.policies & OverlayableItem::Policy::kPublic) {
           policy_flags |= ResTable_overlayable_policy_header::POLICY_PUBLIC;
-        } else {
-          if (item.policies & OverlayableItem::Policy::kPublic) {
-            policy_flags |= ResTable_overlayable_policy_header::POLICY_PUBLIC;
-          }
-          if (item.policies & OverlayableItem::Policy::kSystem) {
-            policy_flags |= ResTable_overlayable_policy_header::POLICY_SYSTEM_PARTITION;
-          }
-          if (item.policies & OverlayableItem::Policy::kVendor) {
-            policy_flags |= ResTable_overlayable_policy_header::POLICY_VENDOR_PARTITION;
-          }
-          if (item.policies & OverlayableItem::Policy::kProduct) {
-            policy_flags |= ResTable_overlayable_policy_header::POLICY_PRODUCT_PARTITION;
-          }
+        }
+        if (item.policies & OverlayableItem::Policy::kSystem) {
+          policy_flags |= ResTable_overlayable_policy_header::POLICY_SYSTEM_PARTITION;
+        }
+        if (item.policies & OverlayableItem::Policy::kVendor) {
+          policy_flags |= ResTable_overlayable_policy_header::POLICY_VENDOR_PARTITION;
+        }
+        if (item.policies & OverlayableItem::Policy::kProduct) {
+          policy_flags |= ResTable_overlayable_policy_header::POLICY_PRODUCT_PARTITION;
+        }
+        if (item.policies & OverlayableItem::Policy::kSignature) {
+          policy_flags |= ResTable_overlayable_policy_header::POLICY_SIGNATURE;
         }
 
         auto policy = overlayable_chunk->policy_ids.find(policy_flags);
@@ -702,17 +710,15 @@
 }  // namespace
 
 bool TableFlattener::Consume(IAaptContext* context, ResourceTable* table) {
-  if (options_.sort_stringpool_entries) {
-    // We must do this before writing the resources, since the string pool IDs may change.
-    table->string_pool.Prune();
-    table->string_pool.Sort([](const StringPool::Context &a, const StringPool::Context &b) -> int {
-      int diff = util::compare(a.priority, b.priority);
-      if (diff == 0) {
-        diff = a.config.compare(b.config);
-      }
-      return diff;
-    });
-  }
+  // We must do this before writing the resources, since the string pool IDs may change.
+  table->string_pool.Prune();
+  table->string_pool.Sort([](const StringPool::Context& a, const StringPool::Context& b) -> int {
+    int diff = util::compare(a.priority, b.priority);
+    if (diff == 0) {
+      diff = a.config.compare(b.config);
+    }
+    return diff;
+  });
 
   // Write the ResTable header.
   ChunkWriter table_writer(buffer_);
diff --git a/tools/aapt2/format/binary/TableFlattener.h b/tools/aapt2/format/binary/TableFlattener.h
index 71330e3..73c1729 100644
--- a/tools/aapt2/format/binary/TableFlattener.h
+++ b/tools/aapt2/format/binary/TableFlattener.h
@@ -44,9 +44,6 @@
   // Set of whitelisted resource names to avoid altering in key stringpool
   std::set<std::string> whitelisted_resources;
 
-  // When true, sort the entries in the values string pool by priority and configuration.
-  bool sort_stringpool_entries = true;
-
   // Map from original resource paths to shortened resource paths.
   std::map<std::string, std::string> shortened_path_map;
 };
diff --git a/tools/aapt2/format/binary/TableFlattener_test.cpp b/tools/aapt2/format/binary/TableFlattener_test.cpp
index ddc1173..4c5dbec 100644
--- a/tools/aapt2/format/binary/TableFlattener_test.cpp
+++ b/tools/aapt2/format/binary/TableFlattener_test.cpp
@@ -671,9 +671,6 @@
   overlayable_item_two.policies |= OverlayableItem::Policy::kSystem;
   overlayable_item_two.policies |= OverlayableItem::Policy::kVendor;
 
-  std::string name_three = "com.app.test:integer/overlayable_three_item";
-  OverlayableItem overlayable_item_three(overlayable);
-
   std::unique_ptr<ResourceTable> table =
       test::ResourceTableBuilder()
           .SetPackageId("com.app.test", 0x7f)
@@ -683,8 +680,6 @@
           .SetOverlayable(name_one, overlayable_item_one)
           .AddSimple(name_two, ResourceId(0x7f020002))
           .SetOverlayable(name_two, overlayable_item_two)
-          .AddSimple(name_three, ResourceId(0x7f020003))
-          .SetOverlayable(name_three, overlayable_item_three)
           .Build();
 
   ResourceTable output_table;
@@ -713,16 +708,6 @@
   EXPECT_EQ(overlayable_item.policies, OverlayableItem::Policy::kSystem
                                        | OverlayableItem::Policy::kProduct
                                        | OverlayableItem::Policy::kVendor);
-
-  search_result = output_table.FindResource(test::ParseNameOrDie(name_three));
-  ASSERT_TRUE(search_result);
-  ASSERT_THAT(search_result.value().entry, NotNull());
-  ASSERT_TRUE(search_result.value().entry->overlayable_item);
-  overlayable_item = search_result.value().entry->overlayable_item.value();
-  EXPECT_EQ(overlayable_item.policies, OverlayableItem::Policy::kPublic);
-  EXPECT_EQ(overlayable_item.overlayable->name, "TestName");
-  EXPECT_EQ(overlayable_item.overlayable->actor, "overlay://theme");
-  EXPECT_EQ(overlayable_item.policies, OverlayableItem::Policy::kPublic);
 }
 
 TEST_F(TableFlattenerTest, FlattenMultipleOverlayable) {
@@ -745,6 +730,8 @@
 
   std::string name_three = "com.app.test:integer/overlayable_three";
   OverlayableItem overlayable_item_three(group_one);
+  overlayable_item_three.policies |= OverlayableItem::Policy::kSignature;
+
   std::unique_ptr<ResourceTable> table =
       test::ResourceTableBuilder()
           .SetPackageId("com.app.test", 0x7f)
@@ -793,7 +780,22 @@
   result_overlayable = search_result.value().entry->overlayable_item.value();
   EXPECT_EQ(result_overlayable.overlayable->name, "OtherName");
   EXPECT_EQ(result_overlayable.overlayable->actor, "overlay://customization");
-  EXPECT_EQ(result_overlayable.policies, OverlayableItem::Policy::kPublic);
+  EXPECT_EQ(result_overlayable.policies, OverlayableItem::Policy::kSignature);
+}
+
+TEST_F(TableFlattenerTest, FlattenOverlayableNoPolicyFails) {
+  auto group = std::make_shared<Overlayable>("TestName", "overlay://theme");
+  std::string name_zero = "com.app.test:integer/overlayable_zero";
+  OverlayableItem overlayable_item_zero(group);
+
+  std::unique_ptr<ResourceTable> table =
+      test::ResourceTableBuilder()
+          .SetPackageId("com.app.test", 0x7f)
+          .AddSimple(name_zero, ResourceId(0x7f020000))
+          .SetOverlayable(name_zero, overlayable_item_zero)
+          .Build();
+  ResourceTable output_table;
+  ASSERT_FALSE(Flatten(context_.get(), {}, table.get(), &output_table));
 }
 
 }  // namespace aapt
diff --git a/tools/aapt2/format/proto/ProtoDeserialize.cpp b/tools/aapt2/format/proto/ProtoDeserialize.cpp
index aff1b39..06f1bf7 100644
--- a/tools/aapt2/format/proto/ProtoDeserialize.cpp
+++ b/tools/aapt2/format/proto/ProtoDeserialize.cpp
@@ -390,6 +390,9 @@
       case pb::OverlayableItem::PRODUCT:
         out_overlayable->policies |= OverlayableItem::Policy::kProduct;
         break;
+      case pb::OverlayableItem::SIGNATURE:
+        out_overlayable->policies |= OverlayableItem::Policy::kSignature;
+        break;
       default:
         *out_error = "unknown overlayable policy";
         return false;
diff --git a/tools/aapt2/format/proto/ProtoSerialize.cpp b/tools/aapt2/format/proto/ProtoSerialize.cpp
index b549e23..eb2b1a2 100644
--- a/tools/aapt2/format/proto/ProtoSerialize.cpp
+++ b/tools/aapt2/format/proto/ProtoSerialize.cpp
@@ -309,6 +309,9 @@
   if (overlayable_item.policies & OverlayableItem::Policy::kVendor) {
     pb_overlayable_item->add_policy(pb::OverlayableItem::VENDOR);
   }
+  if (overlayable_item.policies & OverlayableItem::Policy::kSignature) {
+    pb_overlayable_item->add_policy(pb::OverlayableItem::SIGNATURE);
+  }
 
   SerializeSourceToPb(overlayable_item.source, source_pool,
                       pb_overlayable_item->mutable_source());
diff --git a/tools/aapt2/format/proto/ProtoSerialize_test.cpp b/tools/aapt2/format/proto/ProtoSerialize_test.cpp
index cce3939..d369ac4 100644
--- a/tools/aapt2/format/proto/ProtoSerialize_test.cpp
+++ b/tools/aapt2/format/proto/ProtoSerialize_test.cpp
@@ -526,6 +526,10 @@
       "FontPack", "overlay://theme"));
   overlayable_item_baz.policies |= OverlayableItem::Policy::kPublic;
 
+  OverlayableItem overlayable_item_boz(std::make_shared<Overlayable>(
+      "IconPack", "overlay://theme"));
+  overlayable_item_boz.policies |= OverlayableItem::Policy::kSignature;
+
   OverlayableItem overlayable_item_biz(std::make_shared<Overlayable>(
       "Other", "overlay://customization"));
   overlayable_item_biz.comment ="comment";
@@ -536,6 +540,7 @@
           .SetOverlayable("com.app.a:bool/foo", overlayable_item_foo)
           .SetOverlayable("com.app.a:bool/bar", overlayable_item_bar)
           .SetOverlayable("com.app.a:bool/baz", overlayable_item_baz)
+          .SetOverlayable("com.app.a:bool/boz", overlayable_item_boz)
           .SetOverlayable("com.app.a:bool/biz", overlayable_item_biz)
           .AddValue("com.app.a:bool/fiz", ResourceUtils::TryParseBool("true"))
           .Build();
@@ -576,6 +581,14 @@
   EXPECT_THAT(overlayable_item.overlayable->actor, Eq("overlay://theme"));
   EXPECT_THAT(overlayable_item.policies, Eq(OverlayableItem::Policy::kPublic));
 
+  search_result = new_table.FindResource(test::ParseNameOrDie("com.app.a:bool/boz"));
+  ASSERT_TRUE(search_result);
+  ASSERT_TRUE(search_result.value().entry->overlayable_item);
+  overlayable_item = search_result.value().entry->overlayable_item.value();
+  EXPECT_THAT(overlayable_item.overlayable->name, Eq("IconPack"));
+  EXPECT_THAT(overlayable_item.overlayable->actor, Eq("overlay://theme"));
+  EXPECT_THAT(overlayable_item.policies, Eq(OverlayableItem::Policy::kSignature));
+
   search_result = new_table.FindResource(test::ParseNameOrDie("com.app.a:bool/biz"));
   ASSERT_TRUE(search_result);
   ASSERT_TRUE(search_result.value().entry->overlayable_item);
diff --git a/wifi/java/android/net/wifi/p2p/WifiP2pGroup.java b/wifi/java/android/net/wifi/p2p/WifiP2pGroup.java
index 72e57a1..8a1b21c 100644
--- a/wifi/java/android/net/wifi/p2p/WifiP2pGroup.java
+++ b/wifi/java/android/net/wifi/p2p/WifiP2pGroup.java
@@ -67,7 +67,7 @@
     /** The network id in the wpa_supplicant */
     private int mNetId;
 
-    /** The frequency used by this group */
+    /** The frequency (in MHz) used by this group */
     private int mFrequency;
 
     /** P2P group started string pattern */
@@ -273,7 +273,7 @@
         this.mNetId = netId;
     }
 
-    /** Get the operating frequency of the p2p group */
+    /** Get the operating frequency (in MHz) of the p2p group */
     public int getFrequency() {
         return mFrequency;
     }