Merge "Add update network watchlist config ConfigUpdater intent action"
diff --git a/Android.bp b/Android.bp
index 19c0580..9e9faf2 100644
--- a/Android.bp
+++ b/Android.bp
@@ -230,6 +230,7 @@
         "core/java/android/os/ISchedulingPolicyService.aidl",
         "core/java/android/os/IStatsCompanionService.aidl",
         "core/java/android/os/IStatsManager.aidl",
+        "core/java/android/os/ISystemUpdateManager.aidl",
         "core/java/android/os/IThermalEventListener.aidl",
         "core/java/android/os/IThermalService.aidl",
         "core/java/android/os/IUpdateLock.aidl",
@@ -416,6 +417,8 @@
         "media/java/android/media/IMediaRouterService.aidl",
         "media/java/android/media/IMediaScannerListener.aidl",
         "media/java/android/media/IMediaScannerService.aidl",
+        "media/java/android/media/IMediaSession2.aidl",
+        "media/java/android/media/IMediaSession2Callback.aidl",
         "media/java/android/media/IPlaybackConfigDispatcher.aidl",
         ":libaudioclient_aidl",
         "media/java/android/media/IRecordingConfigDispatcher.aidl",
@@ -689,7 +692,10 @@
         "  $(in) " +
         "&& $(location soong_zip) -jar -o $(out) -C $(genDir)/$(in) -D $(genDir)/$(in)",
 
-    srcs: ["core/proto/**/*.proto"],
+    srcs: [
+        "core/proto/**/*.proto",
+        "libs/incident/**/*.proto",
+    ],
     output_extension: "srcjar",
 }
 
diff --git a/Android.mk b/Android.mk
index 298b85d..2254008 100644
--- a/Android.mk
+++ b/Android.mk
@@ -331,6 +331,8 @@
 		$(framework_docs_LOCAL_DROIDDOC_OPTIONS) \
 		-referenceonly \
 		-api $(INTERNAL_PLATFORM_API_FILE) \
+		-privateApi $(INTERNAL_PLATFORM_PRIVATE_API_FILE) \
+		-privateDexApi $(INTERNAL_PLATFORM_PRIVATE_DEX_API_FILE) \
 		-removedApi $(INTERNAL_PLATFORM_REMOVED_API_FILE) \
 		-nodocs
 
@@ -340,7 +342,9 @@
 
 include $(BUILD_DROIDDOC)
 
-$(INTERNAL_PLATFORM_API_FILE): $(full_target)
+$(full_target): .KATI_IMPLICIT_OUTPUTS := $(INTERNAL_PLATFORM_API_FILE) \
+                                          $(INTERNAL_PLATFORM_PRIVATE_API_FILE) \
+                                          $(INTERNAL_PLATFORM_PRIVATE_DEX_API_FILE)
 $(call dist-for-goals,sdk,$(INTERNAL_PLATFORM_API_FILE))
 
 # ====  the system api stubs ===================================
@@ -365,6 +369,8 @@
 		-referenceonly \
 		-showAnnotation android.annotation.SystemApi \
 		-api $(INTERNAL_PLATFORM_SYSTEM_API_FILE) \
+		-privateApi $(INTERNAL_PLATFORM_SYSTEM_PRIVATE_API_FILE) \
+		-privateDexApi $(INTERNAL_PLATFORM_SYSTEM_PRIVATE_DEX_API_FILE) \
 		-removedApi $(INTERNAL_PLATFORM_SYSTEM_REMOVED_API_FILE) \
 		-exactApi $(INTERNAL_PLATFORM_SYSTEM_EXACT_API_FILE) \
 		-nodocs
@@ -375,7 +381,9 @@
 
 include $(BUILD_DROIDDOC)
 
-$(INTERNAL_PLATFORM_SYSTEM_API_FILE): $(full_target)
+$(full_target): .KATI_IMPLICIT_OUTPUTS := $(INTERNAL_PLATFORM_SYSTEM_API_FILE) \
+                                          $(INTERNAL_PLATFORM_SYSTEM_PRIVATE_API_FILE) \
+                                          $(INTERNAL_PLATFORM_SYSTEM_PRIVATE_DEX_API_FILE)
 $(call dist-for-goals,sdk,$(INTERNAL_PLATFORM_SYSTEM_API_FILE))
 
 # ====  the test api stubs ===================================
@@ -781,6 +789,7 @@
 LOCAL_SOURCE_FILES_ALL_GENERATED := true
 LOCAL_SRC_FILES := \
     cmds/am/proto/instrumentation_data.proto \
+    cmds/statsd/src/perfetto/perfetto_config.proto \
     $(call all-proto-files-under, core/proto) \
     $(call all-proto-files-under, libs/incident/proto) \
     $(call all-proto-files-under, cmds/statsd/src)
@@ -797,7 +806,8 @@
     store_unknown_fields = true
 LOCAL_JAVA_LIBRARIES := core-oj core-libart
 LOCAL_SRC_FILES := \
-    $(call all-proto-files-under, core/proto)
+    $(call all-proto-files-under, core/proto) \
+    $(call all-proto-files-under, libs/incident/proto/android/os)
 include $(BUILD_STATIC_JAVA_LIBRARY)
 
 
@@ -809,7 +819,8 @@
 LOCAL_PROTOC_FLAGS := \
     -Iexternal/protobuf/src
 LOCAL_SRC_FILES := \
-    $(call all-proto-files-under, core/proto)
+    $(call all-proto-files-under, core/proto) \
+    $(call all-proto-files-under, libs/incident/proto/android/os)
 include $(BUILD_STATIC_JAVA_LIBRARY)
 
 # Include subdirectory makefiles
diff --git a/apct-tests/perftests/core/AndroidManifest.xml b/apct-tests/perftests/core/AndroidManifest.xml
index bd0b944..132a2f9 100644
--- a/apct-tests/perftests/core/AndroidManifest.xml
+++ b/apct-tests/perftests/core/AndroidManifest.xml
@@ -10,7 +10,11 @@
 
     <application>
         <uses-library android:name="android.test.runner" />
-        <activity android:name="android.perftests.utils.StubActivity" />
+        <activity android:name="android.perftests.utils.StubActivity">
+          <intent-filter>
+            <action android:name="com.android.perftests.core.PERFTEST" />
+          </intent-filter>
+        </activity>
         <service android:name="android.os.SomeService" android:exported="false" android:process=":some_service" />
     </application>
 
diff --git a/apct-tests/perftests/core/src/android/os/PackageManagerPerfTest.java b/apct-tests/perftests/core/src/android/os/PackageManagerPerfTest.java
new file mode 100644
index 0000000..145fbcd
--- /dev/null
+++ b/apct-tests/perftests/core/src/android/os/PackageManagerPerfTest.java
@@ -0,0 +1,114 @@
+/*
+ * Copyright (C) 2017 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.os;
+
+import static android.content.pm.PackageManager.PERMISSION_DENIED;
+import static android.content.pm.PackageManager.PERMISSION_GRANTED;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.perftests.utils.BenchmarkState;
+import android.perftests.utils.PerfStatusReporter;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.LargeTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.Assert;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+@LargeTest
+public class PackageManagerPerfTest {
+    private static final String PERMISSION_NAME_EXISTS =
+            "com.android.perftests.core.TestPermission";
+    private static final String PERMISSION_NAME_DOESNT_EXIST =
+            "com.android.perftests.core.TestBadPermission";
+    private static final ComponentName TEST_ACTIVITY =
+            new ComponentName("com.android.perftests.core", "android.perftests.utils.StubActivity");
+
+    @Rule
+    public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+
+    @Test
+    public void testCheckPermissionExists() {
+        final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final PackageManager pm = InstrumentationRegistry.getTargetContext().getPackageManager();
+        final String packageName = TEST_ACTIVITY.getPackageName();
+
+        while (state.keepRunning()) {
+            int ret = pm.checkPermission(PERMISSION_NAME_EXISTS, packageName);
+        }
+    }
+
+    @Test
+    public void testCheckPermissionDoesntExist() {
+        final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final PackageManager pm = InstrumentationRegistry.getTargetContext().getPackageManager();
+        final String packageName = TEST_ACTIVITY.getPackageName();
+
+        while (state.keepRunning()) {
+            int ret = pm.checkPermission(PERMISSION_NAME_DOESNT_EXIST, packageName);
+        }
+    }
+
+    @Test
+    public void testQueryIntentActivities() {
+        final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final PackageManager pm = InstrumentationRegistry.getTargetContext().getPackageManager();
+        final Intent intent = new Intent("com.android.perftests.core.PERFTEST");
+
+        while (state.keepRunning()) {
+            pm.queryIntentActivities(intent, 0);
+        }
+    }
+
+    @Test
+    public void testGetPackageInfo() throws Exception {
+        final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final PackageManager pm = InstrumentationRegistry.getTargetContext().getPackageManager();
+        final String packageName = TEST_ACTIVITY.getPackageName();
+
+        while (state.keepRunning()) {
+            pm.getPackageInfo(packageName, 0);
+        }
+    }
+
+    @Test
+    public void testGetApplicationInfo() throws Exception {
+        final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final PackageManager pm = InstrumentationRegistry.getTargetContext().getPackageManager();
+        final String packageName = TEST_ACTIVITY.getPackageName();
+        
+        while (state.keepRunning()) {
+            pm.getApplicationInfo(packageName, 0);
+        }
+    }
+
+    @Test
+    public void testGetActivityInfo() throws Exception {
+        final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final PackageManager pm = InstrumentationRegistry.getTargetContext().getPackageManager();
+        
+        while (state.keepRunning()) {
+            pm.getActivityInfo(TEST_ACTIVITY, 0);
+        }
+    }
+}
diff --git a/apct-tests/perftests/core/src/android/os/PermissionTest.java b/apct-tests/perftests/core/src/android/os/PermissionTest.java
deleted file mode 100644
index d292e7d..0000000
--- a/apct-tests/perftests/core/src/android/os/PermissionTest.java
+++ /dev/null
@@ -1,66 +0,0 @@
-/*
- * Copyright (C) 2017 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.os;
-
-import static android.content.pm.PackageManager.PERMISSION_DENIED;
-import static android.content.pm.PackageManager.PERMISSION_GRANTED;
-
-import android.content.Context;
-import android.perftests.utils.BenchmarkState;
-import android.perftests.utils.PerfStatusReporter;
-import android.support.test.InstrumentationRegistry;
-import android.support.test.filters.LargeTest;
-import android.support.test.runner.AndroidJUnit4;
-
-import org.junit.Assert;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-@RunWith(AndroidJUnit4.class)
-@LargeTest
-public class PermissionTest {
-    private static final String PERMISSION_HAS_NAME = "com.android.perftests.core.TestPermission";
-    private static final String PERMISSION_DOESNT_HAVE_NAME =
-            "com.android.perftests.core.TestBadPermission";
-    private static final String THIS_PACKAGE_NAME = "com.android.perftests.core";
-
-    @Rule
-    public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
-
-    @Test
-    public void testHasPermission() {
-        final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
-        final Context context = InstrumentationRegistry.getTargetContext();
-        while (state.keepRunning()) {
-            int ret = context.getPackageManager().checkPermission(PERMISSION_HAS_NAME,
-                    THIS_PACKAGE_NAME);
-        }
-    }
-
-    @Test
-    public void testDoesntHavePermission() {
-        final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
-        final Context context = InstrumentationRegistry.getTargetContext();
-
-        while (state.keepRunning()) {
-            int ret = context.getPackageManager().checkPermission(PERMISSION_DOESNT_HAVE_NAME,
-                    THIS_PACKAGE_NAME);
-        }
-    }
-
-}
diff --git a/apct-tests/perftests/core/src/android/text/StaticLayoutPerfTest.java b/apct-tests/perftests/core/src/android/text/StaticLayoutPerfTest.java
index 6975609..682885b 100644
--- a/apct-tests/perftests/core/src/android/text/StaticLayoutPerfTest.java
+++ b/apct-tests/perftests/core/src/android/text/StaticLayoutPerfTest.java
@@ -25,9 +25,12 @@
 import android.support.test.runner.AndroidJUnit4;
 
 import android.content.res.ColorStateList;
+import android.graphics.Canvas;
 import android.graphics.Typeface;
 import android.text.Layout;
 import android.text.style.TextAppearanceSpan;
+import android.view.DisplayListCanvas;
+import android.view.RenderNode;
 
 import org.junit.Before;
 import org.junit.Rule;
@@ -285,4 +288,157 @@
                     .build();
         }
     }
+
+    @Test
+    public void testDraw_FixedText_NoStyled() {
+        final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final CharSequence text = generateRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT);
+        final RenderNode node = RenderNode.create("benchmark", null);
+        while (state.keepRunning()) {
+            state.pauseTiming();
+            final StaticLayout layout =
+                    StaticLayout.Builder.obtain(text, 0, text.length(), PAINT, TEXT_WIDTH).build();
+            final DisplayListCanvas c = node.start(1200, 200);
+            state.resumeTiming();
+
+            layout.draw(c);
+        }
+    }
+
+    @Test
+    public void testDraw_RandomText_Styled() {
+        final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final RenderNode node = RenderNode.create("benchmark", null);
+        while (state.keepRunning()) {
+            state.pauseTiming();
+            final CharSequence text = generateRandomParagraph(WORD_LENGTH, STYLE_TEXT);
+            final StaticLayout layout =
+                    StaticLayout.Builder.obtain(text, 0, text.length(), PAINT, TEXT_WIDTH).build();
+            final DisplayListCanvas c = node.start(1200, 200);
+            state.resumeTiming();
+
+            layout.draw(c);
+        }
+    }
+
+    @Test
+    public void testDraw_RandomText_NoStyled() {
+        final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final RenderNode node = RenderNode.create("benchmark", null);
+        while (state.keepRunning()) {
+            state.pauseTiming();
+            final CharSequence text = generateRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT);
+            final StaticLayout layout =
+                    StaticLayout.Builder.obtain(text, 0, text.length(), PAINT, TEXT_WIDTH).build();
+            final DisplayListCanvas c = node.start(1200, 200);
+            state.resumeTiming();
+
+            layout.draw(c);
+        }
+    }
+
+    @Test
+    public void testDraw_RandomText_Styled_WithoutCache() {
+        final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final RenderNode node = RenderNode.create("benchmark", null);
+        while (state.keepRunning()) {
+            state.pauseTiming();
+            final CharSequence text = generateRandomParagraph(WORD_LENGTH, STYLE_TEXT);
+            final StaticLayout layout =
+                    StaticLayout.Builder.obtain(text, 0, text.length(), PAINT, TEXT_WIDTH).build();
+            final DisplayListCanvas c = node.start(1200, 200);
+            Canvas.freeTextLayoutCaches();
+            state.resumeTiming();
+
+            layout.draw(c);
+        }
+    }
+
+    @Test
+    public void testDraw_RandomText_NoStyled_WithoutCache() {
+        final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final RenderNode node = RenderNode.create("benchmark", null);
+        while (state.keepRunning()) {
+            state.pauseTiming();
+            final CharSequence text = generateRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT);
+            final StaticLayout layout =
+                    StaticLayout.Builder.obtain(text, 0, text.length(), PAINT, TEXT_WIDTH).build();
+            final DisplayListCanvas c = node.start(1200, 200);
+            Canvas.freeTextLayoutCaches();
+            state.resumeTiming();
+
+            layout.draw(c);
+        }
+    }
+
+    @Test
+    public void testDraw_MeasuredText_Styled() {
+        final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final RenderNode node = RenderNode.create("benchmark", null);
+        while (state.keepRunning()) {
+            state.pauseTiming();
+            final MeasuredText text = new MeasuredText.Builder(
+                    generateRandomParagraph(WORD_LENGTH, STYLE_TEXT), PAINT).build();
+            final StaticLayout layout =
+                    StaticLayout.Builder.obtain(text, 0, text.length(), PAINT, TEXT_WIDTH).build();
+            final DisplayListCanvas c = node.start(1200, 200);
+            state.resumeTiming();
+
+            layout.draw(c);
+        }
+    }
+
+    @Test
+    public void testDraw_MeasuredText_NoStyled() {
+        final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final RenderNode node = RenderNode.create("benchmark", null);
+        while (state.keepRunning()) {
+            state.pauseTiming();
+            final MeasuredText text = new MeasuredText.Builder(
+                    generateRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT), PAINT).build();
+            final StaticLayout layout =
+                    StaticLayout.Builder.obtain(text, 0, text.length(), PAINT, TEXT_WIDTH).build();
+            final DisplayListCanvas c = node.start(1200, 200);
+            state.resumeTiming();
+
+            layout.draw(c);
+        }
+    }
+
+    @Test
+    public void testDraw_MeasuredText_Styled_WithoutCache() {
+        final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final RenderNode node = RenderNode.create("benchmark", null);
+        while (state.keepRunning()) {
+            state.pauseTiming();
+            final MeasuredText text = new MeasuredText.Builder(
+                    generateRandomParagraph(WORD_LENGTH, STYLE_TEXT), PAINT).build();
+            final StaticLayout layout =
+                    StaticLayout.Builder.obtain(text, 0, text.length(), PAINT, TEXT_WIDTH).build();
+            final DisplayListCanvas c = node.start(1200, 200);
+            Canvas.freeTextLayoutCaches();
+            state.resumeTiming();
+
+            layout.draw(c);
+        }
+    }
+
+    @Test
+    public void testDraw_MeasuredText_NoStyled_WithoutCache() {
+        final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final RenderNode node = RenderNode.create("benchmark", null);
+        while (state.keepRunning()) {
+            state.pauseTiming();
+            final MeasuredText text = new MeasuredText.Builder(
+                    generateRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT), PAINT).build();
+            final StaticLayout layout =
+                    StaticLayout.Builder.obtain(text, 0, text.length(), PAINT, TEXT_WIDTH).build();
+            final DisplayListCanvas c = node.start(1200, 200);
+            Canvas.freeTextLayoutCaches();
+            state.resumeTiming();
+
+            layout.draw(c);
+        }
+    }
+
 }
diff --git a/api/current.txt b/api/current.txt
index c065fe9..7c7c11b 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -2796,6 +2796,7 @@
     field public static final int GLOBAL_ACTION_POWER_DIALOG = 6; // 0x6
     field public static final int GLOBAL_ACTION_QUICK_SETTINGS = 5; // 0x5
     field public static final int GLOBAL_ACTION_RECENTS = 3; // 0x3
+    field public static final int GLOBAL_ACTION_TAKE_SCREENSHOT = 9; // 0x9
     field public static final int GLOBAL_ACTION_TOGGLE_SPLIT_SCREEN = 7; // 0x7
     field public static final java.lang.String SERVICE_INTERFACE = "android.accessibilityservice.AccessibilityService";
     field public static final java.lang.String SERVICE_META_DATA = "android.accessibilityservice";
@@ -6346,6 +6347,9 @@
     method public void onTransferOwnershipComplete(android.content.Context, android.os.PersistableBundle);
     method public void onUserAdded(android.content.Context, android.content.Intent, android.os.UserHandle);
     method public void onUserRemoved(android.content.Context, android.content.Intent, android.os.UserHandle);
+    method public void onUserStarted(android.content.Context, android.content.Intent, android.os.UserHandle);
+    method public void onUserStopped(android.content.Context, android.content.Intent, android.os.UserHandle);
+    method public void onUserSwitched(android.content.Context, android.content.Intent, android.os.UserHandle);
     field public static final java.lang.String ACTION_DEVICE_ADMIN_DISABLED = "android.app.action.DEVICE_ADMIN_DISABLED";
     field public static final java.lang.String ACTION_DEVICE_ADMIN_DISABLE_REQUESTED = "android.app.action.DEVICE_ADMIN_DISABLE_REQUESTED";
     field public static final java.lang.String ACTION_DEVICE_ADMIN_ENABLED = "android.app.action.DEVICE_ADMIN_ENABLED";
@@ -6470,6 +6474,7 @@
     method public boolean isMasterVolumeMuted(android.content.ComponentName);
     method public boolean isNetworkLoggingEnabled(android.content.ComponentName);
     method public boolean isPackageSuspended(android.content.ComponentName, java.lang.String) throws android.content.pm.PackageManager.NameNotFoundException;
+    method public boolean isPrintingEnabled();
     method public boolean isProfileOwnerApp(java.lang.String);
     method public boolean isProvisioningAllowed(java.lang.String);
     method public boolean isResetPasswordTokenActive(android.content.ComponentName);
@@ -6539,6 +6544,7 @@
     method public boolean setPermittedAccessibilityServices(android.content.ComponentName, java.util.List<java.lang.String>);
     method public boolean setPermittedCrossProfileNotificationListeners(android.content.ComponentName, java.util.List<java.lang.String>);
     method public boolean setPermittedInputMethods(android.content.ComponentName, java.util.List<java.lang.String>);
+    method public void setPrintingEnabled(android.content.ComponentName, boolean);
     method public void setProfileEnabled(android.content.ComponentName);
     method public void setProfileName(android.content.ComponentName, java.lang.String);
     method public void setRecommendedGlobalProxy(android.content.ComponentName, android.net.ProxyInfo);
@@ -6669,6 +6675,7 @@
     field public static final int PERMISSION_POLICY_PROMPT = 0; // 0x0
     field public static final java.lang.String POLICY_DISABLE_CAMERA = "policy_disable_camera";
     field public static final java.lang.String POLICY_DISABLE_SCREEN_CAPTURE = "policy_disable_screen_capture";
+    field public static final java.lang.String POLICY_MANDATORY_BACKUPS = "policy_mandatory_backups";
     field public static final int RESET_PASSWORD_DO_NOT_ASK_CREDENTIALS_ON_BOOT = 2; // 0x2
     field public static final int RESET_PASSWORD_REQUIRE_ENTRY = 1; // 0x1
     field public static final int SKIP_SETUP_WIZARD = 1; // 0x1
@@ -7249,6 +7256,7 @@
 
   public static class NetworkStats.Bucket {
     ctor public NetworkStats.Bucket();
+    method public int getDefaultNetwork();
     method public long getEndTimeStamp();
     method public int getMetered();
     method public int getRoaming();
@@ -7260,6 +7268,9 @@
     method public long getTxBytes();
     method public long getTxPackets();
     method public int getUid();
+    field public static final int DEFAULT_NETWORK_ALL = -1; // 0xffffffff
+    field public static final int DEFAULT_NETWORK_NO = 1; // 0x1
+    field public static final int DEFAULT_NETWORK_YES = 2; // 0x2
     field public static final int METERED_ALL = -1; // 0xffffffff
     field public static final int METERED_NO = 1; // 0x1
     field public static final int METERED_YES = 2; // 0x2
@@ -15230,18 +15241,26 @@
     method public void writeToParcel(android.os.Parcel, int);
     field public static final int BLOB = 33; // 0x21
     field public static final android.os.Parcelable.Creator<android.hardware.HardwareBuffer> CREATOR;
+    field public static final int DS_24UI8 = 50; // 0x32
+    field public static final int DS_FP32UI8 = 52; // 0x34
+    field public static final int D_16 = 48; // 0x30
+    field public static final int D_24 = 49; // 0x31
+    field public static final int D_FP32 = 51; // 0x33
     field public static final int RGBA_1010102 = 43; // 0x2b
     field public static final int RGBA_8888 = 1; // 0x1
     field public static final int RGBA_FP16 = 22; // 0x16
     field public static final int RGBX_8888 = 2; // 0x2
     field public static final int RGB_565 = 4; // 0x4
     field public static final int RGB_888 = 3; // 0x3
+    field public static final int S_UI8 = 53; // 0x35
     field public static final long USAGE_CPU_READ_OFTEN = 3L; // 0x3L
     field public static final long USAGE_CPU_READ_RARELY = 2L; // 0x2L
     field public static final long USAGE_CPU_WRITE_OFTEN = 48L; // 0x30L
     field public static final long USAGE_CPU_WRITE_RARELY = 32L; // 0x20L
     field public static final long USAGE_GPU_COLOR_OUTPUT = 512L; // 0x200L
+    field public static final long USAGE_GPU_CUBE_MAP = 33554432L; // 0x2000000L
     field public static final long USAGE_GPU_DATA_BUFFER = 16777216L; // 0x1000000L
+    field public static final long USAGE_GPU_MIPMAP_COMPLETE = 67108864L; // 0x4000000L
     field public static final long USAGE_GPU_SAMPLED_IMAGE = 256L; // 0x100L
     field public static final long USAGE_PROTECTED_CONTENT = 16384L; // 0x4000L
     field public static final long USAGE_SENSOR_DIRECT_DATA = 8388608L; // 0x800000L
@@ -15556,8 +15575,10 @@
     method public <T> T get(android.hardware.camera2.CameraCharacteristics.Key<T>);
     method public java.util.List<android.hardware.camera2.CaptureRequest.Key<?>> getAvailableCaptureRequestKeys();
     method public java.util.List<android.hardware.camera2.CaptureResult.Key<?>> getAvailableCaptureResultKeys();
+    method public java.util.List<android.hardware.camera2.CaptureRequest.Key<?>> getAvailablePhysicalCameraRequestKeys();
     method public java.util.List<android.hardware.camera2.CaptureRequest.Key<?>> getAvailableSessionKeys();
     method public java.util.List<android.hardware.camera2.CameraCharacteristics.Key<?>> getKeys();
+    method public java.util.List<java.lang.String> getPhysicalCameraIds();
     field public static final android.hardware.camera2.CameraCharacteristics.Key<int[]> COLOR_CORRECTION_AVAILABLE_ABERRATION_MODES;
     field public static final android.hardware.camera2.CameraCharacteristics.Key<int[]> CONTROL_AE_AVAILABLE_ANTIBANDING_MODES;
     field public static final android.hardware.camera2.CameraCharacteristics.Key<int[]> CONTROL_AE_AVAILABLE_MODES;
@@ -15596,6 +15617,7 @@
     field public static final android.hardware.camera2.CameraCharacteristics.Key<float[]> LENS_POSE_ROTATION;
     field public static final android.hardware.camera2.CameraCharacteristics.Key<float[]> LENS_POSE_TRANSLATION;
     field public static final android.hardware.camera2.CameraCharacteristics.Key<float[]> LENS_RADIAL_DISTORTION;
+    field public static final android.hardware.camera2.CameraCharacteristics.Key<java.lang.Integer> LOGICAL_MULTI_CAMERA_SENSOR_SYNC_TYPE;
     field public static final android.hardware.camera2.CameraCharacteristics.Key<int[]> NOISE_REDUCTION_AVAILABLE_NOISE_REDUCTION_MODES;
     field public static final android.hardware.camera2.CameraCharacteristics.Key<java.lang.Integer> REPROCESS_MAX_CAPTURE_STALL;
     field public static final android.hardware.camera2.CameraCharacteristics.Key<int[]> REQUEST_AVAILABLE_CAPABILITIES;
@@ -15636,6 +15658,7 @@
     field public static final android.hardware.camera2.CameraCharacteristics.Key<int[]> STATISTICS_INFO_AVAILABLE_FACE_DETECT_MODES;
     field public static final android.hardware.camera2.CameraCharacteristics.Key<boolean[]> STATISTICS_INFO_AVAILABLE_HOT_PIXEL_MAP_MODES;
     field public static final android.hardware.camera2.CameraCharacteristics.Key<int[]> STATISTICS_INFO_AVAILABLE_LENS_SHADING_MAP_MODES;
+    field public static final android.hardware.camera2.CameraCharacteristics.Key<int[]> STATISTICS_INFO_AVAILABLE_OIS_DATA_MODES;
     field public static final android.hardware.camera2.CameraCharacteristics.Key<java.lang.Integer> STATISTICS_INFO_MAX_FACE_COUNT;
     field public static final android.hardware.camera2.CameraCharacteristics.Key<java.lang.Integer> SYNC_MAX_LATENCY;
     field public static final android.hardware.camera2.CameraCharacteristics.Key<int[]> TONEMAP_AVAILABLE_TONE_MAP_MODES;
@@ -15656,6 +15679,7 @@
   public abstract class CameraDevice implements java.lang.AutoCloseable {
     method public abstract void close();
     method public abstract android.hardware.camera2.CaptureRequest.Builder createCaptureRequest(int) throws android.hardware.camera2.CameraAccessException;
+    method public android.hardware.camera2.CaptureRequest.Builder createCaptureRequest(int, java.util.Set<java.lang.String>) throws android.hardware.camera2.CameraAccessException;
     method public abstract void createCaptureSession(java.util.List<android.view.Surface>, android.hardware.camera2.CameraCaptureSession.StateCallback, android.os.Handler) throws android.hardware.camera2.CameraAccessException;
     method public void createCaptureSession(android.hardware.camera2.params.SessionConfiguration) throws android.hardware.camera2.CameraAccessException;
     method public abstract void createCaptureSessionByOutputConfigurations(java.util.List<android.hardware.camera2.params.OutputConfiguration>, android.hardware.camera2.CameraCaptureSession.StateCallback, android.os.Handler) throws android.hardware.camera2.CameraAccessException;
@@ -15826,6 +15850,7 @@
     field public static final int HOT_PIXEL_MODE_HIGH_QUALITY = 2; // 0x2
     field public static final int HOT_PIXEL_MODE_OFF = 0; // 0x0
     field public static final int INFO_SUPPORTED_HARDWARE_LEVEL_3 = 3; // 0x3
+    field public static final int INFO_SUPPORTED_HARDWARE_LEVEL_EXTERNAL = 4; // 0x4
     field public static final int INFO_SUPPORTED_HARDWARE_LEVEL_FULL = 1; // 0x1
     field public static final int INFO_SUPPORTED_HARDWARE_LEVEL_LEGACY = 2; // 0x2
     field public static final int INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED = 0; // 0x0
@@ -15841,6 +15866,8 @@
     field public static final int LENS_POSE_REFERENCE_PRIMARY_CAMERA = 0; // 0x0
     field public static final int LENS_STATE_MOVING = 1; // 0x1
     field public static final int LENS_STATE_STATIONARY = 0; // 0x0
+    field public static final int LOGICAL_MULTI_CAMERA_SENSOR_SYNC_TYPE_APPROXIMATE = 0; // 0x0
+    field public static final int LOGICAL_MULTI_CAMERA_SENSOR_SYNC_TYPE_CALIBRATED = 1; // 0x1
     field public static final int NOISE_REDUCTION_MODE_FAST = 1; // 0x1
     field public static final int NOISE_REDUCTION_MODE_HIGH_QUALITY = 2; // 0x2
     field public static final int NOISE_REDUCTION_MODE_MINIMAL = 3; // 0x3
@@ -15850,6 +15877,7 @@
     field public static final int REQUEST_AVAILABLE_CAPABILITIES_BURST_CAPTURE = 6; // 0x6
     field public static final int REQUEST_AVAILABLE_CAPABILITIES_CONSTRAINED_HIGH_SPEED_VIDEO = 9; // 0x9
     field public static final int REQUEST_AVAILABLE_CAPABILITIES_DEPTH_OUTPUT = 8; // 0x8
+    field public static final int REQUEST_AVAILABLE_CAPABILITIES_LOGICAL_MULTI_CAMERA = 11; // 0xb
     field public static final int REQUEST_AVAILABLE_CAPABILITIES_MANUAL_POST_PROCESSING = 2; // 0x2
     field public static final int REQUEST_AVAILABLE_CAPABILITIES_MANUAL_SENSOR = 1; // 0x1
     field public static final int REQUEST_AVAILABLE_CAPABILITIES_MOTION_TRACKING = 10; // 0xa
@@ -15899,6 +15927,8 @@
     field public static final int STATISTICS_FACE_DETECT_MODE_SIMPLE = 1; // 0x1
     field public static final int STATISTICS_LENS_SHADING_MAP_MODE_OFF = 0; // 0x0
     field public static final int STATISTICS_LENS_SHADING_MAP_MODE_ON = 1; // 0x1
+    field public static final int STATISTICS_OIS_DATA_MODE_OFF = 0; // 0x0
+    field public static final int STATISTICS_OIS_DATA_MODE_ON = 1; // 0x1
     field public static final int STATISTICS_SCENE_FLICKER_50HZ = 1; // 0x1
     field public static final int STATISTICS_SCENE_FLICKER_60HZ = 2; // 0x2
     field public static final int STATISTICS_SCENE_FLICKER_NONE = 0; // 0x0
@@ -15981,6 +16011,7 @@
     field public static final android.hardware.camera2.CaptureRequest.Key<java.lang.Integer> STATISTICS_FACE_DETECT_MODE;
     field public static final android.hardware.camera2.CaptureRequest.Key<java.lang.Boolean> STATISTICS_HOT_PIXEL_MAP_MODE;
     field public static final android.hardware.camera2.CaptureRequest.Key<java.lang.Integer> STATISTICS_LENS_SHADING_MAP_MODE;
+    field public static final android.hardware.camera2.CaptureRequest.Key<java.lang.Integer> STATISTICS_OIS_DATA_MODE;
     field public static final android.hardware.camera2.CaptureRequest.Key<android.hardware.camera2.params.TonemapCurve> TONEMAP_CURVE;
     field public static final android.hardware.camera2.CaptureRequest.Key<java.lang.Float> TONEMAP_GAMMA;
     field public static final android.hardware.camera2.CaptureRequest.Key<java.lang.Integer> TONEMAP_MODE;
@@ -15991,8 +16022,10 @@
     method public void addTarget(android.view.Surface);
     method public android.hardware.camera2.CaptureRequest build();
     method public <T> T get(android.hardware.camera2.CaptureRequest.Key<T>);
+    method public <T> T getPhysicalCameraKey(android.hardware.camera2.CaptureRequest.Key<T>, java.lang.String);
     method public void removeTarget(android.view.Surface);
     method public <T> void set(android.hardware.camera2.CaptureRequest.Key<T>, T);
+    method public <T> android.hardware.camera2.CaptureRequest.Builder setPhysicalCameraKey(android.hardware.camera2.CaptureRequest.Key<T>, T, java.lang.String);
     method public void setTag(java.lang.Object);
   }
 
@@ -16080,6 +16113,10 @@
     field public static final android.hardware.camera2.CaptureResult.Key<java.lang.Boolean> STATISTICS_HOT_PIXEL_MAP_MODE;
     field public static final android.hardware.camera2.CaptureResult.Key<android.hardware.camera2.params.LensShadingMap> STATISTICS_LENS_SHADING_CORRECTION_MAP;
     field public static final android.hardware.camera2.CaptureResult.Key<java.lang.Integer> STATISTICS_LENS_SHADING_MAP_MODE;
+    field public static final android.hardware.camera2.CaptureResult.Key<java.lang.Integer> STATISTICS_OIS_DATA_MODE;
+    field public static final android.hardware.camera2.CaptureResult.Key<long[]> STATISTICS_OIS_TIMESTAMPS;
+    field public static final android.hardware.camera2.CaptureResult.Key<float[]> STATISTICS_OIS_X_SHIFTS;
+    field public static final android.hardware.camera2.CaptureResult.Key<float[]> STATISTICS_OIS_Y_SHIFTS;
     field public static final android.hardware.camera2.CaptureResult.Key<java.lang.Integer> STATISTICS_SCENE_FLICKER;
     field public static final android.hardware.camera2.CaptureResult.Key<android.hardware.camera2.params.TonemapCurve> TONEMAP_CURVE;
     field public static final android.hardware.camera2.CaptureResult.Key<java.lang.Float> TONEMAP_GAMMA;
@@ -16188,6 +16225,7 @@
     method public int getSurfaceGroupId();
     method public java.util.List<android.view.Surface> getSurfaces();
     method public void removeSurface(android.view.Surface);
+    method public void setPhysicalCameraId(java.lang.String);
     method public void writeToParcel(android.os.Parcel, int);
     field public static final android.os.Parcelable.Creator<android.hardware.camera2.params.OutputConfiguration> CREATOR;
     field public static final int SURFACE_GROUP_ID_NONE = -1; // 0xffffffff
@@ -21734,7 +21772,13 @@
     field public static final int CHANNEL_OUT_STEREO = 12; // 0xc
     field public static final int CHANNEL_OUT_SURROUND = 1052; // 0x41c
     field public static final android.os.Parcelable.Creator<android.media.AudioFormat> CREATOR;
+    field public static final int ENCODING_AAC_ELD = 15; // 0xf
+    field public static final int ENCODING_AAC_HE_V1 = 11; // 0xb
+    field public static final int ENCODING_AAC_HE_V2 = 12; // 0xc
+    field public static final int ENCODING_AAC_LC = 10; // 0xa
+    field public static final int ENCODING_AAC_XHE = 16; // 0x10
     field public static final int ENCODING_AC3 = 5; // 0x5
+    field public static final int ENCODING_AC4 = 17; // 0x11
     field public static final int ENCODING_DEFAULT = 1; // 0x1
     field public static final int ENCODING_DOLBY_TRUEHD = 14; // 0xe
     field public static final int ENCODING_DTS = 7; // 0x7
@@ -21742,6 +21786,7 @@
     field public static final int ENCODING_E_AC3 = 6; // 0x6
     field public static final int ENCODING_IEC61937 = 13; // 0xd
     field public static final int ENCODING_INVALID = 0; // 0x0
+    field public static final int ENCODING_MP3 = 9; // 0x9
     field public static final int ENCODING_PCM_16BIT = 2; // 0x2
     field public static final int ENCODING_PCM_8BIT = 3; // 0x3
     field public static final int ENCODING_PCM_FLOAT = 4; // 0x4
@@ -21784,6 +21829,7 @@
     method public boolean isBluetoothScoOn();
     method public boolean isMicrophoneMute();
     method public boolean isMusicActive();
+    method public boolean isOffloadedPlaybackSupported(android.media.AudioFormat);
     method public boolean isSpeakerphoneOn();
     method public boolean isStreamMute(int);
     method public boolean isVolumeFixed();
@@ -22084,6 +22130,7 @@
     method public int reloadStaticData();
     method public void removeOnRoutingChangedListener(android.media.AudioRouting.OnRoutingChangedListener);
     method public deprecated void removeOnRoutingChangedListener(android.media.AudioTrack.OnRoutingChangedListener);
+    method public void removeStreamEventCallback();
     method public int setAuxEffectSendLevel(float);
     method public int setBufferSizeInFrames(int);
     method public int setLoopPoints(int, int, int);
@@ -22097,6 +22144,7 @@
     method public boolean setPreferredDevice(android.media.AudioDeviceInfo);
     method protected deprecated void setState(int);
     method public deprecated int setStereoVolume(float, float);
+    method public void setStreamEventCallback(java.util.concurrent.Executor, android.media.AudioTrack.StreamEventCallback);
     method public int setVolume(float);
     method public void stop() throws java.lang.IllegalStateException;
     method public int write(byte[], int, int);
@@ -22132,6 +22180,7 @@
     method public android.media.AudioTrack.Builder setAudioAttributes(android.media.AudioAttributes) throws java.lang.IllegalArgumentException;
     method public android.media.AudioTrack.Builder setAudioFormat(android.media.AudioFormat) throws java.lang.IllegalArgumentException;
     method public android.media.AudioTrack.Builder setBufferSizeInBytes(int) throws java.lang.IllegalArgumentException;
+    method public android.media.AudioTrack.Builder setOffloadedPlayback(boolean);
     method public android.media.AudioTrack.Builder setPerformanceMode(int);
     method public android.media.AudioTrack.Builder setSessionId(int) throws java.lang.IllegalArgumentException;
     method public android.media.AudioTrack.Builder setTransferMode(int) throws java.lang.IllegalArgumentException;
@@ -22147,6 +22196,12 @@
     method public default void onRoutingChanged(android.media.AudioRouting);
   }
 
+  public static abstract class AudioTrack.StreamEventCallback {
+    method public void onStreamDataRequest(android.media.AudioTrack);
+    method public void onStreamPresentationEnd(android.media.AudioTrack);
+    method public void onTearDown(android.media.AudioTrack);
+  }
+
   public class CamcorderProfile {
     method public static android.media.CamcorderProfile get(int);
     method public static android.media.CamcorderProfile get(int, int);
@@ -26112,6 +26167,7 @@
     method public deprecated android.net.NetworkInfo getNetworkInfo(int);
     method public android.net.NetworkInfo getNetworkInfo(android.net.Network);
     method public deprecated int getNetworkPreference();
+    method public byte[] getNetworkWatchlistConfigHash();
     method public static deprecated android.net.Network getProcessDefaultNetwork();
     method public int getRestrictBackgroundStatus();
     method public boolean isActiveNetworkMetered();
@@ -27342,7 +27398,7 @@
     method public int addNetwork(android.net.wifi.WifiConfiguration);
     method public void addOrUpdatePasspointConfiguration(android.net.wifi.hotspot2.PasspointConfiguration);
     method public static int calculateSignalLevel(int, int);
-    method public void cancelWps(android.net.wifi.WifiManager.WpsCallback);
+    method public deprecated void cancelWps(android.net.wifi.WifiManager.WpsCallback);
     method public static int compareSignalLevel(int, int);
     method public android.net.wifi.WifiManager.MulticastLock createMulticastLock(java.lang.String);
     method public android.net.wifi.WifiManager.WifiLock createWifiLock(int, java.lang.String);
@@ -27375,7 +27431,7 @@
     method public boolean setWifiEnabled(boolean);
     method public void startLocalOnlyHotspot(android.net.wifi.WifiManager.LocalOnlyHotspotCallback, android.os.Handler);
     method public deprecated boolean startScan();
-    method public void startWps(android.net.wifi.WpsInfo, android.net.wifi.WifiManager.WpsCallback);
+    method public deprecated void startWps(android.net.wifi.WpsInfo, android.net.wifi.WifiManager.WpsCallback);
     method public int updateNetwork(android.net.wifi.WifiConfiguration);
     field public static final java.lang.String ACTION_PICK_WIFI_NETWORK = "android.net.wifi.PICK_WIFI_NETWORK";
     field public static final java.lang.String ACTION_REQUEST_SCAN_ALWAYS_AVAILABLE = "android.net.wifi.action.REQUEST_SCAN_ALWAYS_AVAILABLE";
@@ -27405,11 +27461,11 @@
     field public static final int WIFI_STATE_ENABLED = 3; // 0x3
     field public static final int WIFI_STATE_ENABLING = 2; // 0x2
     field public static final int WIFI_STATE_UNKNOWN = 4; // 0x4
-    field public static final int WPS_AUTH_FAILURE = 6; // 0x6
-    field public static final int WPS_OVERLAP_ERROR = 3; // 0x3
-    field public static final int WPS_TIMED_OUT = 7; // 0x7
-    field public static final int WPS_TKIP_ONLY_PROHIBITED = 5; // 0x5
-    field public static final int WPS_WEP_PROHIBITED = 4; // 0x4
+    field public static final deprecated int WPS_AUTH_FAILURE = 6; // 0x6
+    field public static final deprecated int WPS_OVERLAP_ERROR = 3; // 0x3
+    field public static final deprecated int WPS_TIMED_OUT = 7; // 0x7
+    field public static final deprecated int WPS_TKIP_ONLY_PROHIBITED = 5; // 0x5
+    field public static final deprecated int WPS_WEP_PROHIBITED = 4; // 0x4
   }
 
   public static class WifiManager.LocalOnlyHotspotCallback {
@@ -27443,11 +27499,11 @@
     method public void setWorkSource(android.os.WorkSource);
   }
 
-  public static abstract class WifiManager.WpsCallback {
+  public static abstract deprecated class WifiManager.WpsCallback {
     ctor public WifiManager.WpsCallback();
-    method public abstract void onFailed(int);
-    method public abstract void onStarted(java.lang.String);
-    method public abstract void onSucceeded();
+    method public abstract deprecated void onFailed(int);
+    method public abstract deprecated void onStarted(java.lang.String);
+    method public abstract deprecated void onSucceeded();
   }
 
   public class WpsInfo implements android.os.Parcelable {
@@ -27889,6 +27945,29 @@
 
 package android.net.wifi.rtt {
 
+  public final class LocationCivic implements android.os.Parcelable {
+    method public int describeContents();
+    method public byte[] getData();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.net.wifi.rtt.LocationCivic> CREATOR;
+  }
+
+  public final class LocationConfigurationInformation implements android.os.Parcelable {
+    method public int describeContents();
+    method public double getAltitude();
+    method public int getAltitudeType();
+    method public double getAltitudeUncertainty();
+    method public double getLatitude();
+    method public double getLatitudeUncertainty();
+    method public double getLongitude();
+    method public double getLongitudeUncertainty();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final int ALTITUDE_IN_FLOORS = 2; // 0x2
+    field public static final int ALTITUDE_IN_METERS = 1; // 0x1
+    field public static final int ALTITUDE_UNKNOWN = 0; // 0x0
+    field public static final android.os.Parcelable.Creator<android.net.wifi.rtt.LocationConfigurationInformation> CREATOR;
+  }
+
   public final class RangingRequest implements android.os.Parcelable {
     method public int describeContents();
     method public static int getMaxPeers();
@@ -27912,6 +27991,8 @@
     method public android.net.MacAddress getMacAddress();
     method public android.net.wifi.aware.PeerHandle getPeerHandle();
     method public long getRangingTimestampUs();
+    method public android.net.wifi.rtt.LocationCivic getReportedLocationCivic();
+    method public android.net.wifi.rtt.LocationConfigurationInformation getReportedLocationConfigurationInformation();
     method public int getRssi();
     method public int getStatus();
     method public void writeToParcel(android.os.Parcel, int);
@@ -31513,7 +31594,7 @@
   }
 
   public final class Debug {
-    method public static void attachJvmtiAgent(java.lang.String, java.lang.String) throws java.io.IOException;
+    method public static void attachJvmtiAgent(java.lang.String, java.lang.String, java.lang.ClassLoader) throws java.io.IOException;
     method public static deprecated void changeDebugPort(int);
     method public static void dumpHprofData(java.lang.String) throws java.io.IOException;
     method public static boolean dumpService(java.lang.String, java.io.FileDescriptor, java.lang.String[]);
@@ -33990,9 +34071,10 @@
     field public static final java.lang.String DURATION = "duration";
     field public static final java.lang.String EXTRA_CALL_TYPE_FILTER = "android.provider.extra.CALL_TYPE_FILTER";
     field public static final java.lang.String FEATURES = "features";
+    field public static final int FEATURES_ASSISTED_DIALING_USED = 16; // 0x10
     field public static final int FEATURES_HD_CALL = 4; // 0x4
     field public static final int FEATURES_PULLED_EXTERNALLY = 2; // 0x2
-    field public static final int FEATURES_RTT = 16; // 0x10
+    field public static final int FEATURES_RTT = 32; // 0x20
     field public static final int FEATURES_VIDEO = 1; // 0x1
     field public static final int FEATURES_WIFI = 8; // 0x8
     field public static final java.lang.String GEOCODED_LOCATION = "geocoded_location";
@@ -37907,7 +37989,6 @@
   }
 
   public static final class FieldClassification.Match {
-    method public java.lang.String getAlgorithm();
     method public java.lang.String getRemoteId();
     method public float getScore();
   }
@@ -39841,6 +39922,7 @@
     field public static final int CAPABILITY_SUPPORTS_VT_REMOTE_TX = 2048; // 0x800
     field public static final int CAPABILITY_SUPPORT_HOLD = 2; // 0x2
     field public static final int CAPABILITY_SWAP_CONFERENCE = 8; // 0x8
+    field public static final int PROPERTY_ASSISTED_DIALING_USED = 512; // 0x200
     field public static final int PROPERTY_CONFERENCE = 1; // 0x1
     field public static final int PROPERTY_EMERGENCY_CALLBACK_MODE = 4; // 0x4
     field public static final int PROPERTY_ENTERPRISE_CALL = 32; // 0x20
@@ -40061,6 +40143,7 @@
     field public static final java.lang.String EXTRA_CALL_SUBJECT = "android.telecom.extra.CALL_SUBJECT";
     field public static final java.lang.String EXTRA_CHILD_ADDRESS = "android.telecom.extra.CHILD_ADDRESS";
     field public static final java.lang.String EXTRA_LAST_FORWARDED_NUMBER = "android.telecom.extra.LAST_FORWARDED_NUMBER";
+    field public static final int PROPERTY_ASSISTED_DIALING_USED = 512; // 0x200
     field public static final int PROPERTY_HAS_CDMA_VOICE_PRIVACY = 32; // 0x20
     field public static final int PROPERTY_IS_EXTERNAL_CALL = 16; // 0x10
     field public static final int PROPERTY_IS_RTT = 256; // 0x100
@@ -40483,6 +40566,7 @@
     field public static final java.lang.String ACTION_SHOW_RESPOND_VIA_SMS_SETTINGS = "android.telecom.action.SHOW_RESPOND_VIA_SMS_SETTINGS";
     field public static final char DTMF_CHARACTER_PAUSE = 44; // 0x002c ','
     field public static final char DTMF_CHARACTER_WAIT = 59; // 0x003b ';'
+    field public static final java.lang.String EXTRA_ASSISTED_DIALING_TRANSFORMATION_INFO = "android.telecom.extra.ASSISTED_DIALING_TRANSFORMATION_INFO";
     field public static final java.lang.String EXTRA_CALL_BACK_NUMBER = "android.telecom.extra.CALL_BACK_NUMBER";
     field public static final java.lang.String EXTRA_CALL_DISCONNECT_CAUSE = "android.telecom.extra.CALL_DISCONNECT_CAUSE";
     field public static final java.lang.String EXTRA_CALL_DISCONNECT_MESSAGE = "android.telecom.extra.CALL_DISCONNECT_MESSAGE";
@@ -40498,6 +40582,7 @@
     field public static final java.lang.String EXTRA_START_CALL_WITH_RTT = "android.telecom.extra.START_CALL_WITH_RTT";
     field public static final java.lang.String EXTRA_START_CALL_WITH_SPEAKERPHONE = "android.telecom.extra.START_CALL_WITH_SPEAKERPHONE";
     field public static final java.lang.String EXTRA_START_CALL_WITH_VIDEO_STATE = "android.telecom.extra.START_CALL_WITH_VIDEO_STATE";
+    field public static final java.lang.String EXTRA_USE_ASSISTED_DIALING = "android.telecom.extra.USE_ASSISTED_DIALING";
     field public static final java.lang.String GATEWAY_ORIGINAL_ADDRESS = "android.telecom.extra.GATEWAY_ORIGINAL_ADDRESS";
     field public static final java.lang.String GATEWAY_PROVIDER_PACKAGE = "android.telecom.extra.GATEWAY_PROVIDER_PACKAGE";
     field public static final java.lang.String METADATA_INCLUDE_EXTERNAL_CALLS = "android.telecom.INCLUDE_EXTERNAL_CALLS";
@@ -40510,6 +40595,18 @@
     field public static final int PRESENTATION_UNKNOWN = 3; // 0x3
   }
 
+  public final class TransformationInfo implements android.os.Parcelable {
+    ctor public TransformationInfo(java.lang.String, java.lang.String, java.lang.String, java.lang.String, int);
+    method public int describeContents();
+    method public java.lang.String getOriginalNumber();
+    method public java.lang.String getTransformedNumber();
+    method public int getTransformedNumberCountryCallingCode();
+    method public java.lang.String getUserHomeCountryCode();
+    method public java.lang.String getUserRoamingCountryCode();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.telecom.TransformationInfo> CREATOR;
+  }
+
   public class VideoProfile implements android.os.Parcelable {
     ctor public VideoProfile(int);
     ctor public VideoProfile(int, int);
@@ -40673,6 +40770,7 @@
     field public static final java.lang.String KEY_ALLOW_NON_EMERGENCY_CALLS_IN_ECM_BOOL = "allow_non_emergency_calls_in_ecm_bool";
     field public static final java.lang.String KEY_ALWAYS_SHOW_EMERGENCY_ALERT_ONOFF_BOOL = "always_show_emergency_alert_onoff_bool";
     field public static final java.lang.String KEY_APN_EXPAND_BOOL = "apn_expand_bool";
+    field public static final java.lang.String KEY_ASSISTED_DIALING_ENABLED_BOOL = "assisted_dialing_enabled_bool";
     field public static final java.lang.String KEY_AUTO_RETRY_ENABLED_BOOL = "auto_retry_enabled_bool";
     field public static final java.lang.String KEY_CALL_FORWARDING_BLOCKS_WHILE_ROAMING_STRING_ARRAY = "call_forwarding_blocks_while_roaming_string_array";
     field public static final java.lang.String KEY_CARRIER_ALLOW_TURNOFF_IMS_BOOL = "carrier_allow_turnoff_ims_bool";
@@ -41418,6 +41516,8 @@
     method public android.telephony.TelephonyManager createForPhoneAccountHandle(android.telecom.PhoneAccountHandle);
     method public android.telephony.TelephonyManager createForSubscriptionId(int);
     method public java.util.List<android.telephony.CellInfo> getAllCellInfo();
+    method public int getAndroidCarrierIdForSubscription();
+    method public java.lang.CharSequence getAndroidCarrierNameForSubscription();
     method public int getCallState();
     method public android.os.PersistableBundle getCarrierConfig();
     method public deprecated android.telephony.CellLocation getCellLocation();
@@ -41455,8 +41555,6 @@
     method public int getSimState();
     method public int getSimState(int);
     method public java.lang.String getSubscriberId();
-    method public int getSubscriptionCarrierId();
-    method public java.lang.String getSubscriptionCarrierName();
     method public java.lang.String getVisualVoicemailPackageName();
     method public java.lang.String getVoiceMailAlphaTag();
     method public java.lang.String getVoiceMailNumber();
@@ -41674,6 +41772,7 @@
     method public java.net.InetAddress getMmsProxy();
     method public java.net.URL getMmsc();
     method public java.lang.String getMvnoType();
+    method public int getNetworkTypeBitmask();
     method public java.lang.String getOperatorNumeric();
     method public java.lang.String getPassword();
     method public int getPort();
@@ -41717,11 +41816,11 @@
     method public android.telephony.data.ApnSetting.Builder setAuthType(int);
     method public android.telephony.data.ApnSetting.Builder setCarrierEnabled(boolean);
     method public android.telephony.data.ApnSetting.Builder setEntryName(java.lang.String);
-    method public android.telephony.data.ApnSetting.Builder setId(int);
     method public android.telephony.data.ApnSetting.Builder setMmsPort(int);
     method public android.telephony.data.ApnSetting.Builder setMmsProxy(java.net.InetAddress);
     method public android.telephony.data.ApnSetting.Builder setMmsc(java.net.URL);
     method public android.telephony.data.ApnSetting.Builder setMvnoType(java.lang.String);
+    method public android.telephony.data.ApnSetting.Builder setNetworkTypeBitmask(int);
     method public android.telephony.data.ApnSetting.Builder setOperatorNumeric(java.lang.String);
     method public android.telephony.data.ApnSetting.Builder setPassword(java.lang.String);
     method public android.telephony.data.ApnSetting.Builder setPort(int);
@@ -43058,9 +43157,13 @@
     ctor public BulletSpan();
     ctor public BulletSpan(int);
     ctor public BulletSpan(int, int);
+    ctor public BulletSpan(int, int, int);
     ctor public BulletSpan(android.os.Parcel);
     method public int describeContents();
     method public void drawLeadingMargin(android.graphics.Canvas, android.graphics.Paint, int, int, int, int, int, java.lang.CharSequence, int, int, boolean, android.text.Layout);
+    method public int getBulletRadius();
+    method public int getColor();
+    method public int getGapWidth();
     method public int getLeadingMargin(boolean);
     method public int getSpanTypeId();
     method public void writeToParcel(android.os.Parcel, int);
@@ -50132,6 +50235,7 @@
     method public java.lang.String getTitle();
     method public java.lang.String getUrl();
     method public android.webkit.WebChromeClient getWebChromeClient();
+    method public static java.lang.ClassLoader getWebViewClassLoader();
     method public android.webkit.WebViewClient getWebViewClient();
     method public void goBack();
     method public void goBackOrForward(int);
diff --git a/api/system-current.txt b/api/system-current.txt
index 72db3b8..306a573 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -75,6 +75,7 @@
     field public static final java.lang.String INSTALL_GRANT_RUNTIME_PERMISSIONS = "android.permission.INSTALL_GRANT_RUNTIME_PERMISSIONS";
     field public static final java.lang.String INSTALL_LOCATION_PROVIDER = "android.permission.INSTALL_LOCATION_PROVIDER";
     field public static final java.lang.String INSTALL_PACKAGES = "android.permission.INSTALL_PACKAGES";
+    field public static final java.lang.String INSTALL_PACKAGE_UPDATES = "android.permission.INSTALL_PACKAGE_UPDATES";
     field public static final java.lang.String INSTALL_SELF_UPDATES = "android.permission.INSTALL_SELF_UPDATES";
     field public static final java.lang.String INTENT_FILTER_VERIFICATION_AGENT = "android.permission.INTENT_FILTER_VERIFICATION_AGENT";
     field public static final java.lang.String INTERACT_ACROSS_USERS = "android.permission.INTERACT_ACROSS_USERS";
@@ -131,6 +132,7 @@
     field public static final java.lang.String READ_PRIVILEGED_PHONE_STATE = "android.permission.READ_PRIVILEGED_PHONE_STATE";
     field public static final java.lang.String READ_RUNTIME_PROFILES = "android.permission.READ_RUNTIME_PROFILES";
     field public static final java.lang.String READ_SEARCH_INDEXABLES = "android.permission.READ_SEARCH_INDEXABLES";
+    field public static final java.lang.String READ_SYSTEM_UPDATE_INFO = "android.permission.READ_SYSTEM_UPDATE_INFO";
     field public static final java.lang.String READ_WALLPAPER_INTERNAL = "android.permission.READ_WALLPAPER_INTERNAL";
     field public static final java.lang.String READ_WIFI_CREDENTIAL = "android.permission.READ_WIFI_CREDENTIAL";
     field public static final java.lang.String REAL_GET_TASKS = "android.permission.REAL_GET_TASKS";
@@ -380,6 +382,7 @@
     method public java.lang.CharSequence getDeviceOwnerOrganizationName();
     method public java.util.List<java.lang.String> getPermittedAccessibilityServices(int);
     method public java.util.List<java.lang.String> getPermittedInputMethodsForCurrentUser();
+    method public java.lang.CharSequence getPrintingDisabledReason();
     method public android.content.ComponentName getProfileOwner() throws java.lang.IllegalArgumentException;
     method public java.lang.String getProfileOwnerNameAsUser(int) throws java.lang.IllegalArgumentException;
     method public int getUserProvisioningState();
@@ -760,6 +763,7 @@
     field public static final java.lang.String OEM_LOCK_SERVICE = "oem_lock";
     field public static final java.lang.String PERSISTENT_DATA_BLOCK_SERVICE = "persistent_data_block";
     field public static final java.lang.String STATS_MANAGER = "stats";
+    field public static final java.lang.String SYSTEM_UPDATE_SERVICE = "system_update";
     field public static final java.lang.String VR_SERVICE = "vrmanager";
     field public static final java.lang.String WIFI_RTT_SERVICE = "rttmanager";
     field public static final java.lang.String WIFI_SCANNING_SERVICE = "wifiscanner";
@@ -1777,9 +1781,9 @@
     method public android.hardware.radio.ProgramSelector.Identifier[] getAllIds(int);
     method public long getFirstId(int);
     method public android.hardware.radio.ProgramSelector.Identifier getPrimaryId();
-    method public int getProgramType();
+    method public deprecated int getProgramType();
     method public android.hardware.radio.ProgramSelector.Identifier[] getSecondaryIds();
-    method public long[] getVendorIds();
+    method public deprecated long[] getVendorIds();
     method public void writeToParcel(android.os.Parcel, int);
     field public static final android.os.Parcelable.Creator<android.hardware.radio.ProgramSelector> CREATOR;
     field public static final int IDENTIFIER_TYPE_AMFM_FREQUENCY = 1; // 0x1
@@ -1787,27 +1791,31 @@
     field public static final int IDENTIFIER_TYPE_DAB_FREQUENCY = 8; // 0x8
     field public static final int IDENTIFIER_TYPE_DAB_SCID = 7; // 0x7
     field public static final int IDENTIFIER_TYPE_DAB_SIDECC = 5; // 0x5
+    field public static final int IDENTIFIER_TYPE_DAB_SID_EXT = 5; // 0x5
     field public static final int IDENTIFIER_TYPE_DRMO_FREQUENCY = 10; // 0xa
-    field public static final int IDENTIFIER_TYPE_DRMO_MODULATION = 11; // 0xb
+    field public static final deprecated int IDENTIFIER_TYPE_DRMO_MODULATION = 11; // 0xb
     field public static final int IDENTIFIER_TYPE_DRMO_SERVICE_ID = 9; // 0x9
     field public static final int IDENTIFIER_TYPE_HD_STATION_ID_EXT = 3; // 0x3
-    field public static final int IDENTIFIER_TYPE_HD_SUBCHANNEL = 4; // 0x4
+    field public static final int IDENTIFIER_TYPE_HD_STATION_NAME = 10004; // 0x2714
+    field public static final deprecated int IDENTIFIER_TYPE_HD_SUBCHANNEL = 4; // 0x4
     field public static final int IDENTIFIER_TYPE_INVALID = 0; // 0x0
     field public static final int IDENTIFIER_TYPE_RDS_PI = 2; // 0x2
     field public static final int IDENTIFIER_TYPE_SXM_CHANNEL = 13; // 0xd
     field public static final int IDENTIFIER_TYPE_SXM_SERVICE_ID = 12; // 0xc
-    field public static final int IDENTIFIER_TYPE_VENDOR_PRIMARY_END = 1999; // 0x7cf
-    field public static final int IDENTIFIER_TYPE_VENDOR_PRIMARY_START = 1000; // 0x3e8
-    field public static final int PROGRAM_TYPE_AM = 1; // 0x1
-    field public static final int PROGRAM_TYPE_AM_HD = 3; // 0x3
-    field public static final int PROGRAM_TYPE_DAB = 5; // 0x5
-    field public static final int PROGRAM_TYPE_DRMO = 6; // 0x6
-    field public static final int PROGRAM_TYPE_FM = 2; // 0x2
-    field public static final int PROGRAM_TYPE_FM_HD = 4; // 0x4
-    field public static final int PROGRAM_TYPE_INVALID = 0; // 0x0
-    field public static final int PROGRAM_TYPE_SXM = 7; // 0x7
-    field public static final int PROGRAM_TYPE_VENDOR_END = 1999; // 0x7cf
-    field public static final int PROGRAM_TYPE_VENDOR_START = 1000; // 0x3e8
+    field public static final int IDENTIFIER_TYPE_VENDOR_END = 1999; // 0x7cf
+    field public static final deprecated int IDENTIFIER_TYPE_VENDOR_PRIMARY_END = 1999; // 0x7cf
+    field public static final deprecated int IDENTIFIER_TYPE_VENDOR_PRIMARY_START = 1000; // 0x3e8
+    field public static final int IDENTIFIER_TYPE_VENDOR_START = 1000; // 0x3e8
+    field public static final deprecated int PROGRAM_TYPE_AM = 1; // 0x1
+    field public static final deprecated int PROGRAM_TYPE_AM_HD = 3; // 0x3
+    field public static final deprecated int PROGRAM_TYPE_DAB = 5; // 0x5
+    field public static final deprecated int PROGRAM_TYPE_DRMO = 6; // 0x6
+    field public static final deprecated int PROGRAM_TYPE_FM = 2; // 0x2
+    field public static final deprecated int PROGRAM_TYPE_FM_HD = 4; // 0x4
+    field public static final deprecated int PROGRAM_TYPE_INVALID = 0; // 0x0
+    field public static final deprecated int PROGRAM_TYPE_SXM = 7; // 0x7
+    field public static final deprecated int PROGRAM_TYPE_VENDOR_END = 1999; // 0x7cf
+    field public static final deprecated int PROGRAM_TYPE_VENDOR_START = 1000; // 0x3e8
   }
 
   public static final class ProgramSelector.Identifier implements android.os.Parcelable {
@@ -1822,7 +1830,7 @@
   public static abstract class ProgramSelector.IdentifierType implements java.lang.annotation.Annotation {
   }
 
-  public static abstract class ProgramSelector.ProgramType implements java.lang.annotation.Annotation {
+  public static abstract deprecated class ProgramSelector.ProgramType implements java.lang.annotation.Annotation {
   }
 
   public class RadioManager {
@@ -2860,9 +2868,18 @@
     method public void onTetheringStarted();
   }
 
+  public final class IpSecManager {
+    method public android.net.IpSecManager.IpSecTunnelInterface createIpSecTunnelInterface(java.net.InetAddress, java.net.InetAddress, android.net.Network) throws java.io.IOException, android.net.IpSecManager.ResourceUnavailableException;
+  }
+
+  public static final class IpSecManager.IpSecTunnelInterface implements java.lang.AutoCloseable {
+    method public void close();
+    method public java.lang.String getInterfaceName();
+  }
+
   public static class IpSecTransform.Builder {
+    method public android.net.IpSecTransform buildTunnelModeTransform(java.net.InetAddress, android.net.IpSecManager.SecurityParameterIndex) throws java.io.IOException, android.net.IpSecManager.ResourceUnavailableException, android.net.IpSecManager.SpiUnavailableException;
     method public android.net.IpSecTransform.Builder setNattKeepalive(int);
-    method public android.net.IpSecTransform.Builder setUnderlyingNetwork(android.net.Network);
   }
 
   public class NetworkKey implements android.os.Parcelable {
@@ -3490,6 +3507,30 @@
     method public abstract void onResult(android.os.Bundle);
   }
 
+  public class SystemProperties {
+    method public static java.lang.String get(java.lang.String);
+    method public static java.lang.String get(java.lang.String, java.lang.String);
+    method public static boolean getBoolean(java.lang.String, boolean);
+    method public static int getInt(java.lang.String, int);
+    method public static long getLong(java.lang.String, long);
+  }
+
+  public class SystemUpdateManager {
+    method public android.os.Bundle retrieveSystemUpdateInfo();
+    method public void updateSystemUpdateInfo(android.os.PersistableBundle);
+    field public static final java.lang.String KEY_IS_SECURITY_UPDATE = "is_security_update";
+    field public static final java.lang.String KEY_STATUS = "status";
+    field public static final java.lang.String KEY_TARGET_BUILD_FINGERPRINT = "target_build_fingerprint";
+    field public static final java.lang.String KEY_TARGET_SECURITY_PATCH_LEVEL = "target_security_patch_level";
+    field public static final java.lang.String KEY_TITLE = "title";
+    field public static final int STATUS_IDLE = 1; // 0x1
+    field public static final int STATUS_IN_PROGRESS = 3; // 0x3
+    field public static final int STATUS_UNKNOWN = 0; // 0x0
+    field public static final int STATUS_WAITING_DOWNLOAD = 2; // 0x2
+    field public static final int STATUS_WAITING_INSTALL = 4; // 0x4
+    field public static final int STATUS_WAITING_REBOOT = 5; // 0x5
+  }
+
   public class UpdateEngine {
     ctor public UpdateEngine();
     method public void applyPayload(java.lang.String, long, long, java.lang.String[]);
@@ -3863,20 +3904,10 @@
 
   public abstract class AutofillFieldClassificationService extends android.app.Service {
     method public android.os.IBinder onBind(android.content.Intent);
-    method public java.util.List<java.lang.String> onGetAvailableAlgorithms();
-    method public java.lang.String onGetDefaultAlgorithm();
-    method public android.service.autofill.AutofillFieldClassificationService.Scores onGetScores(java.lang.String, android.os.Bundle, java.util.List<android.view.autofill.AutofillValue>, java.util.List<java.lang.String>);
+    method public float[][] onGetScores(java.lang.String, android.os.Bundle, java.util.List<android.view.autofill.AutofillValue>, java.util.List<java.lang.String>);
     field public static final java.lang.String SERVICE_INTERFACE = "android.service.autofill.AutofillFieldClassificationService";
-  }
-
-  public static final class AutofillFieldClassificationService.Scores implements android.os.Parcelable {
-    ctor public AutofillFieldClassificationService.Scores(java.lang.String, int, int);
-    ctor public AutofillFieldClassificationService.Scores(android.os.Parcel);
-    method public int describeContents();
-    method public java.lang.String getAlgorithm();
-    method public float[][] getScores();
-    method public void writeToParcel(android.os.Parcel, int);
-    field public static final android.os.Parcelable.Creator<android.service.autofill.AutofillFieldClassificationService.Scores> CREATOR;
+    field public static final java.lang.String SERVICE_META_DATA_KEY_AVAILABLE_ALGORITHMS = "android.autofill.field_classification.available_algorithms";
+    field public static final java.lang.String SERVICE_META_DATA_KEY_DEFAULT_ALGORITHM = "android.autofill.field_classification.default_algorithm";
   }
 
 }
@@ -4966,6 +4997,7 @@
     method public abstract android.webkit.TracingController getTracingController();
     method public abstract android.webkit.WebIconDatabase getWebIconDatabase();
     method public abstract android.webkit.WebStorage getWebStorage();
+    method public abstract java.lang.ClassLoader getWebViewClassLoader();
     method public abstract android.webkit.WebViewDatabase getWebViewDatabase(android.content.Context);
   }
 
diff --git a/cmds/incident_helper/src/TextParserBase.h b/cmds/incident_helper/src/TextParserBase.h
index c41612d..1667966 100644
--- a/cmds/incident_helper/src/TextParserBase.h
+++ b/cmds/incident_helper/src/TextParserBase.h
@@ -68,4 +68,4 @@
     virtual status_t Parse(const int in, const int out) const;
 };
 
-#endif // TEXT_PARSER_BASE_H
\ No newline at end of file
+#endif // TEXT_PARSER_BASE_H
diff --git a/cmds/incidentd/Android.mk b/cmds/incidentd/Android.mk
index 8420bc8..368a70b 100644
--- a/cmds/incidentd/Android.mk
+++ b/cmds/incidentd/Android.mk
@@ -134,6 +134,7 @@
     libcutils \
     libincident \
     liblog \
+    libprotobuf-cpp-lite \
     libprotoutil \
     libselinux \
     libservices \
diff --git a/cmds/incidentd/README.md b/cmds/incidentd/README.md
index ad0fa08..71c6deb 100644
--- a/cmds/incidentd/README.md
+++ b/cmds/incidentd/README.md
@@ -12,8 +12,8 @@
 
 ```
 root$ mmm -j frameworks/base/cmds/incidentd && \
-adb push $OUT/data/nativetest/incidentd_test/* /data/nativetest/incidentd_test/ && \
-adb shell /data/nativetest/incidentd_test/incidentd_test 2>/dev/null
+adb push $OUT/data/nativetest/incidentd_test/* /data/nativetest/ && \
+adb shell /data/nativetest/incidentd_test 2>/dev/null
 ```
 
 Run the test via AndroidTest.xml
diff --git a/cmds/incidentd/incidentd.rc b/cmds/incidentd/incidentd.rc
index 66667dc..1bd1468 100644
--- a/cmds/incidentd/incidentd.rc
+++ b/cmds/incidentd/incidentd.rc
@@ -19,4 +19,4 @@
 
 on post-fs-data
     # Create directory for incidentd
-    mkdir /data/misc/incidents 0770 root root
+    mkdir /data/misc/incidents 0770 incidentd incidentd
diff --git a/cmds/incidentd/src/FdBuffer.cpp b/cmds/incidentd/src/FdBuffer.cpp
index 30dd339..0fff4e6 100644
--- a/cmds/incidentd/src/FdBuffer.cpp
+++ b/cmds/incidentd/src/FdBuffer.cpp
@@ -63,12 +63,14 @@
 
         int64_t remainingTime = (mStartTime + timeout) - uptimeMillis();
         if (remainingTime <= 0) {
+            if (DEBUG) ALOGD("timed out due to long read");
             mTimedOut = true;
             break;
         }
 
         int count = poll(&pfds, 1, remainingTime);
         if (count == 0) {
+            if (DEBUG) ALOGD("timed out due to block calling poll");
             mTimedOut = true;
             break;
         } else if (count < 0) {
@@ -129,6 +131,7 @@
 
         int64_t remainingTime = (mStartTime + timeoutMs) - uptimeMillis();
         if (remainingTime <= 0) {
+            if (DEBUG) ALOGD("timed out due to long read");
             mTimedOut = true;
             break;
         }
@@ -136,6 +139,7 @@
         // wait for any pfds to be ready to perform IO
         int count = poll(pfds, 3, remainingTime);
         if (count == 0) {
+            if (DEBUG) ALOGD("timed out due to block calling poll");
             mTimedOut = true;
             break;
         } else if (count < 0) {
diff --git a/cmds/incidentd/src/IncidentService.cpp b/cmds/incidentd/src/IncidentService.cpp
index c4b54bb..1d5ab59 100644
--- a/cmds/incidentd/src/IncidentService.cpp
+++ b/cmds/incidentd/src/IncidentService.cpp
@@ -43,24 +43,44 @@
 String16 const USAGE_STATS_PERMISSION("android.permission.PACKAGE_USAGE_STATS");
 
 static Status
-checkIncidentPermissions()
+checkIncidentPermissions(const IncidentReportArgs& args)
 {
+    uid_t callingUid = IPCThreadState::self()->getCallingUid();
+    if (callingUid == AID_ROOT || callingUid == AID_SHELL) {
+        // root doesn't have permission.DUMP if don't do this!
+        return Status::ok();
+    }
+
+    // checking calling permission.
     if (!checkCallingPermission(DUMP_PERMISSION)) {
         ALOGW("Calling pid %d and uid %d does not have permission: android.permission.DUMP",
-                IPCThreadState::self()->getCallingPid(), IPCThreadState::self()->getCallingUid());
+                IPCThreadState::self()->getCallingPid(), callingUid);
         return Status::fromExceptionCode(Status::EX_SECURITY,
                 "Calling process does not have permission: android.permission.DUMP");
     }
     if (!checkCallingPermission(USAGE_STATS_PERMISSION)) {
         ALOGW("Calling pid %d and uid %d does not have permission: android.permission.USAGE_STATS",
-                IPCThreadState::self()->getCallingPid(), IPCThreadState::self()->getCallingUid());
+                IPCThreadState::self()->getCallingPid(), callingUid);
         return Status::fromExceptionCode(Status::EX_SECURITY,
                 "Calling process does not have permission: android.permission.USAGE_STATS");
     }
+
+    // checking calling request uid permission.
+    switch (args.dest()) {
+        case DEST_LOCAL:
+            if (callingUid != AID_SHELL || callingUid != AID_ROOT) {
+                return Status::fromExceptionCode(Status::EX_SECURITY,
+                    "Calling process does not have permission to get local data.");
+            }
+        case DEST_EXPLICIT:
+            if (callingUid != AID_SHELL || callingUid != AID_ROOT ||
+                callingUid != AID_STATSD || callingUid != AID_SYSTEM) {
+                return Status::fromExceptionCode(Status::EX_SECURITY,
+                    "Calling process does not have permission to get explicit data.");
+            }
+    }
     return Status::ok();
 }
-
-
 // ================================================================================
 ReportRequestQueue::ReportRequestQueue()
 {
@@ -71,7 +91,7 @@
 }
 
 void
-ReportRequestQueue::addRequest(const sp<ReportRequest>& request) 
+ReportRequestQueue::addRequest(const sp<ReportRequest>& request)
 {
     unique_lock<mutex> lock(mLock);
     mQueue.push_back(request);
@@ -196,7 +216,7 @@
 {
     ALOGI("reportIncident");
 
-    Status status = checkIncidentPermissions();
+    Status status = checkIncidentPermissions(args);
     if (!status.isOk()) {
         return status;
     }
@@ -212,7 +232,7 @@
 {
     ALOGI("reportIncidentToStream");
 
-    Status status = checkIncidentPermissions();
+    Status status = checkIncidentPermissions(args);
     if (!status.isOk()) {
         return status;
     }
diff --git a/cmds/incidentd/src/Reporter.cpp b/cmds/incidentd/src/Reporter.cpp
index 34930aa..bd559d6 100644
--- a/cmds/incidentd/src/Reporter.cpp
+++ b/cmds/incidentd/src/Reporter.cpp
@@ -251,7 +251,7 @@
     // Override umask. Not super critical. If it fails go on with life.
     chmod(filename, 0660);
 
-    if (chown(filename, AID_SYSTEM, AID_SYSTEM)) {
+    if (chown(filename, AID_INCIDENTD, AID_INCIDENTD)) {
         ALOGE("Unable to change ownership of incident file %s: %s\n", filename, strerror(errno));
         status_t err = -errno;
         unlink(mFilename.c_str());
diff --git a/cmds/incidentd/src/Section.cpp b/cmds/incidentd/src/Section.cpp
index 61d16f8..0827785 100644
--- a/cmds/incidentd/src/Section.cpp
+++ b/cmds/incidentd/src/Section.cpp
@@ -19,6 +19,7 @@
 #include "Section.h"
 
 #include <errno.h>
+#include <sys/prctl.h>
 #include <unistd.h>
 #include <wait.h>
 
@@ -30,7 +31,6 @@
 #include <log/log_event_list.h>
 #include <log/logprint.h>
 #include <log/log_read.h>
-#include <private/android_filesystem_config.h> // for AID_NOBODY
 #include <private/android_logger.h>
 
 #include "FdBuffer.h"
@@ -55,26 +55,20 @@
 fork_execute_incident_helper(const int id, const char* name, Fpipe& p2cPipe, Fpipe& c2pPipe)
 {
     const char* ihArgs[] { INCIDENT_HELPER, "-s", String8::format("%d", id).string(), NULL };
-
     // fork used in multithreaded environment, avoid adding unnecessary code in child process
     pid_t pid = fork();
     if (pid == 0) {
-        // child process executes incident helper as nobody
-        if (setgid(AID_NOBODY) == -1) {
-            ALOGW("%s can't change gid: %s", name, strerror(errno));
-            _exit(EXIT_FAILURE);
-        }
-        if (setuid(AID_NOBODY) == -1) {
-            ALOGW("%s can't change uid: %s", name, strerror(errno));
-            _exit(EXIT_FAILURE);
-        }
-
-        if (dup2(p2cPipe.readFd(),  STDIN_FILENO)  != 0 || !p2cPipe.close() ||
-            dup2(c2pPipe.writeFd(), STDOUT_FILENO) != 1 || !c2pPipe.close()) {
+        if (TEMP_FAILURE_RETRY(dup2(p2cPipe.readFd(),  STDIN_FILENO))  != 0
+            || !p2cPipe.close()
+            || TEMP_FAILURE_RETRY(dup2(c2pPipe.writeFd(), STDOUT_FILENO)) != 1
+            || !c2pPipe.close()) {
             ALOGW("%s can't setup stdin and stdout for incident helper", name);
             _exit(EXIT_FAILURE);
         }
 
+        /* make sure the child dies when incidentd dies */
+        prctl(PR_SET_PDEATHSIG, SIGKILL);
+
         execv(INCIDENT_HELPER, const_cast<char**>(ihArgs));
 
         ALOGW("%s failed in incident helper process: %s", name, strerror(errno));
@@ -87,11 +81,23 @@
 }
 
 // ================================================================================
+static status_t statusCode(int status) {
+    if (WIFSIGNALED(status)) {
+      ALOGD("return by signal: %s", strerror(WTERMSIG(status)));
+      return -WTERMSIG(status);
+    } else if (WIFEXITED(status) && WEXITSTATUS(status) > 0) {
+      ALOGD("return by exit: %s", strerror(WEXITSTATUS(status)));
+      return -WEXITSTATUS(status);
+    }
+    return NO_ERROR;
+}
+
 static status_t kill_child(pid_t pid) {
     int status;
+    ALOGD("try to kill child process %d", pid);
     kill(pid, SIGKILL);
     if (waitpid(pid, &status, 0) == -1) return -1;
-    return WIFEXITED(status) == 0 ? NO_ERROR : -WEXITSTATUS(status);
+    return statusCode(status);
 }
 
 static status_t wait_child(pid_t pid) {
@@ -104,7 +110,7 @@
         nanosleep(&WAIT_INTERVAL_NS, NULL);
     }
     if (!died) return kill_child(pid);
-    return WIFEXITED(status) == 0 ? NO_ERROR : -WEXITSTATUS(status);
+    return statusCode(status);
 }
 // ================================================================================
 static const Privacy*
@@ -275,9 +281,9 @@
     status_t readStatus = buffer.readProcessedDataInStream(fd, p2cPipe.writeFd(), c2pPipe.readFd(),
             this->timeoutMs, mIsSysfs);
     if (readStatus != NO_ERROR || buffer.timedOut()) {
-        ALOGW("FileSection '%s' failed to read data from incident helper: %s, timedout: %s, kill: %s",
-            this->name.string(), strerror(-readStatus), buffer.timedOut() ? "true" : "false",
-            strerror(-kill_child(pid)));
+        ALOGW("FileSection '%s' failed to read data from incident helper: %s, timedout: %s",
+            this->name.string(), strerror(-readStatus), buffer.timedOut() ? "true" : "false");
+        kill_child(pid);
         return readStatus;
     }
 
@@ -543,10 +549,10 @@
     close(cmdPipe.writeFd());
     status_t readStatus = buffer.read(ihPipe.readFd(), this->timeoutMs);
     if (readStatus != NO_ERROR || buffer.timedOut()) {
-        ALOGW("CommandSection '%s' failed to read data from incident helper: %s, "
-            "timedout: %s, kill command: %s, kill incident helper: %s",
-            this->name.string(), strerror(-readStatus), buffer.timedOut() ? "true" : "false",
-            strerror(-kill_child(cmdPid)), strerror(-kill_child(ihPid)));
+        ALOGW("CommandSection '%s' failed to read data from incident helper: %s, timedout: %s",
+            this->name.string(), strerror(-readStatus), buffer.timedOut() ? "true" : "false");
+        kill_child(cmdPid);
+        kill_child(ihPid);
         return readStatus;
     }
 
diff --git a/cmds/incidentd/src/io_util.cpp b/cmds/incidentd/src/io_util.cpp
index af4a35c..90f543e 100644
--- a/cmds/incidentd/src/io_util.cpp
+++ b/cmds/incidentd/src/io_util.cpp
@@ -23,7 +23,7 @@
 status_t write_all(int fd, uint8_t const* buf, size_t size)
 {
     while (size > 0) {
-        ssize_t amt = ::write(fd, buf, size);
+        ssize_t amt = TEMP_FAILURE_RETRY(::write(fd, buf, size));
         if (amt < 0) {
             return -errno;
         }
diff --git a/cmds/incidentd/src/report_directory.cpp b/cmds/incidentd/src/report_directory.cpp
index 65030b3..20111d8 100644
--- a/cmds/incidentd/src/report_directory.cpp
+++ b/cmds/incidentd/src/report_directory.cpp
@@ -58,26 +58,9 @@
                 goto done;
             }
         } else {
-            if (mkdir(dir, 0770)) {
-                ALOGE("No incident reports today. "
-                        "Unable to create incident report dir %s: %s", dir,
-                        strerror(errno));
-                err = -errno;
-                goto done;
-            }
-            if (chmod(dir, 0770)) {
-                ALOGE("No incident reports today. "
-                        "Unable to set permissions for incident report dir %s: %s", dir,
-                        strerror(errno));
-                err = -errno;
-                goto done;
-            }
-            if (chown(dir, AID_SYSTEM, AID_SYSTEM)) {
-                ALOGE("No incident reports today. Unable to change ownership of dir %s: %s\n",
-                        dir, strerror(errno));
-                err = -errno;
-                goto done;
-            }
+            ALOGE("No such directory %s, something wrong.", dir);
+            err = -1;
+            goto done;
         }
         if (!last) {
             *d++ = '/';
@@ -97,8 +80,7 @@
         err = BAD_VALUE;
         goto done;
     }
-    if ((st.st_uid != AID_SYSTEM && st.st_uid != AID_ROOT) ||
-        (st.st_gid != AID_SYSTEM && st.st_gid != AID_ROOT)) {
+    if (st.st_uid != AID_INCIDENTD || st.st_gid != AID_INCIDENTD) {
         ALOGE("No incident reports today. Owner is %d and group is %d on report directory %s",
                 st.st_uid, st.st_gid, directory);
         err = BAD_VALUE;
diff --git a/cmds/incidentd/tests/Reporter_test.cpp b/cmds/incidentd/tests/Reporter_test.cpp
index 531c9f2..c494bd6 100644
--- a/cmds/incidentd/tests/Reporter_test.cpp
+++ b/cmds/incidentd/tests/Reporter_test.cpp
@@ -17,6 +17,7 @@
 #include "Reporter.h"
 
 #include <android/os/BnIncidentReportStatusListener.h>
+#include <frameworks/base/libs/incident/proto/android/os/header.pb.h>
 
 #include <android-base/file.h>
 #include <android-base/test_utils.h>
@@ -29,6 +30,7 @@
 using namespace android;
 using namespace android::base;
 using namespace android::binder;
+using namespace android::os;
 using namespace std;
 using ::testing::StrEq;
 using ::testing::Test;
@@ -141,7 +143,8 @@
     IncidentReportArgs args1, args2;
     args1.addSection(1);
     args2.addSection(2);
-    std::vector<uint8_t> header {'a', 'b', 'c', 'd', 'e'};
+    IncidentHeaderProto header;
+    header.set_alert_id(12);
     args2.addHeader(header);
     sp<ReportRequest> r1 = new ReportRequest(args1, l, tf.fd);
     sp<ReportRequest> r2 = new ReportRequest(args2, l, tf.fd);
@@ -153,7 +156,7 @@
 
     string result;
     ReadFileToString(tf.path, &result);
-    EXPECT_THAT(result, StrEq("\n\x5" "abcde"));
+    EXPECT_THAT(result, StrEq("\n\x2" "\b\f"));
 
     EXPECT_EQ(l->startInvoked, 2);
     EXPECT_EQ(l->finishInvoked, 2);
@@ -164,13 +167,16 @@
 
 TEST_F(ReporterTest, RunReportToGivenDirectory) {
     IncidentReportArgs args;
-    args.addHeader({'1', '2', '3'});
-    args.addHeader({'a', 'b', 'c', 'd'});
+    IncidentHeaderProto header1, header2;
+    header1.set_alert_id(12);
+    header2.set_reason("abcd");
+    args.addHeader(header1);
+    args.addHeader(header2);
     sp<ReportRequest> r = new ReportRequest(args, l, -1);
     reporter->batch.add(r);
 
     ASSERT_EQ(Reporter::REPORT_FINISHED, reporter->runReport());
     vector<string> results = InspectFiles();
     ASSERT_EQ((int)results.size(), 1);
-    EXPECT_EQ(results[0], "\n\x3" "123\n\x4" "abcd");
+    EXPECT_EQ(results[0], "\n\x2" "\b\f\n\x6" "\x12\x4" "abcd");
 }
diff --git a/cmds/incidentd/tests/Section_test.cpp b/cmds/incidentd/tests/Section_test.cpp
index cbfb896..2cfd7df 100644
--- a/cmds/incidentd/tests/Section_test.cpp
+++ b/cmds/incidentd/tests/Section_test.cpp
@@ -18,6 +18,7 @@
 
 #include <android-base/file.h>
 #include <android-base/test_utils.h>
+#include <frameworks/base/libs/incident/proto/android/os/header.pb.h>
 #include <gmock/gmock.h>
 #include <gtest/gtest.h>
 #include <string.h>
@@ -34,6 +35,7 @@
 
 using namespace android::base;
 using namespace android::binder;
+using namespace android::os;
 using namespace std;
 using ::testing::StrEq;
 using ::testing::internal::CaptureStdout;
@@ -66,15 +68,9 @@
     args1.addSection(2);
     args2.setAll(true);
 
-    vector<uint8_t> head1;
-    head1.push_back('a');
-    head1.push_back('x');
-    head1.push_back('e');
-
-    vector<uint8_t> head2;
-    head2.push_back('p');
-    head2.push_back('u');
-    head2.push_back('p');
+    IncidentHeaderProto head1, head2;
+    head1.set_reason("axe");
+    head2.set_reason("pup");
 
     args1.addHeader(head1);
     args1.addHeader(head2);
@@ -87,10 +83,10 @@
     string content;
     CaptureStdout();
     ASSERT_EQ(NO_ERROR, hs.Execute(&requests));
-    EXPECT_THAT(GetCapturedStdout(), StrEq("\n\x3" "axe\n\x03pup"));
+    EXPECT_THAT(GetCapturedStdout(), StrEq("\n\x5" "\x12\x3" "axe\n\x05\x12\x03pup"));
 
     EXPECT_TRUE(ReadFileToString(output2.path, &content));
-    EXPECT_THAT(content, StrEq("\n\x03pup"));
+    EXPECT_THAT(content, StrEq("\n\x05\x12\x03pup"));
 }
 
 TEST(SectionTest, FileSection) {
diff --git a/cmds/statsd/Android.mk b/cmds/statsd/Android.mk
index 5eff548..a7daa3f 100644
--- a/cmds/statsd/Android.mk
+++ b/cmds/statsd/Android.mk
@@ -34,6 +34,7 @@
     src/config/ConfigKey.cpp \
     src/config/ConfigListener.cpp \
     src/config/ConfigManager.cpp \
+    src/external/Perfetto.cpp \
     src/external/StatsPuller.cpp \
     src/external/StatsCompanionServicePuller.cpp \
     src/external/SubsystemSleepStatePuller.cpp \
@@ -57,6 +58,7 @@
     src/metrics/MetricsManager.cpp \
     src/metrics/metrics_manager_util.cpp \
     src/packages/UidMap.cpp \
+    src/perfetto/perfetto_config.proto \
     src/storage/StorageManager.cpp \
     src/StatsLogProcessor.cpp \
     src/StatsService.cpp \
@@ -134,7 +136,7 @@
 
 LOCAL_MODULE_CLASS := EXECUTABLES
 
-#LOCAL_INIT_RC := statsd.rc
+LOCAL_INIT_RC := statsd.rc
 
 include $(BUILD_EXECUTABLE)
 
@@ -209,6 +211,7 @@
 LOCAL_SRC_FILES := \
     src/stats_log.proto \
     src/statsd_config.proto \
+    src/perfetto/perfetto_config.proto \
     src/atoms.proto
 
 LOCAL_PROTOC_OPTIMIZE_TYPE := lite
diff --git a/cmds/statsd/src/StatsLogProcessor.cpp b/cmds/statsd/src/StatsLogProcessor.cpp
index 9d6d8a1..7a7a2f6 100644
--- a/cmds/statsd/src/StatsLogProcessor.cpp
+++ b/cmds/statsd/src/StatsLogProcessor.cpp
@@ -127,6 +127,10 @@
     StatsdStats::getInstance().noteAtomLogged(
         event->GetTagId(), event->GetTimestampNs() / NS_PER_SEC);
 
+    if (mMetricsManagers.empty()) {
+        return;
+    }
+
     // Hard-coded logic to update the isolated uid's in the uid-map.
     // The field numbers need to be currently updated by hand with atoms.proto
     if (event->GetTagId() == android::util::ISOLATED_UID_CHANGED) {
diff --git a/cmds/statsd/src/anomaly/AnomalyTracker.cpp b/cmds/statsd/src/anomaly/AnomalyTracker.cpp
index f10b2cf..e34aed3 100644
--- a/cmds/statsd/src/anomaly/AnomalyTracker.cpp
+++ b/cmds/statsd/src/anomaly/AnomalyTracker.cpp
@@ -18,7 +18,9 @@
 #include "Log.h"
 
 #include "AnomalyTracker.h"
+#include "external/Perfetto.h"
 #include "guardrail/StatsdStats.h"
+#include "frameworks/base/libs/incident/proto/android/os/header.pb.h"
 
 #include <android/os/IIncidentManager.h>
 #include <android/os/IncidentReportArgs.h>
@@ -239,7 +241,7 @@
                 }
                 break;
             case Subscription::SubscriberInformationCase::kPerfettoDetails:
-                ALOGW("Perfetto reports not implemented.");
+                CollectPerfettoTraceAndUploadToDropbox(subscription.perfetto_details());
                 break;
             default:
                 break;
@@ -253,10 +255,10 @@
             for (const auto section : incidentdSections) {
                 incidentReport.addSection(section);
             }
-            int64_t alertId = mAlert.id();
-            std::vector<uint8_t> header;
-            uint8_t* src = static_cast<uint8_t*>(static_cast<void*>(&alertId));
-            header.insert(header.end(), src, src + sizeof(int64_t));
+            android::os::IncidentHeaderProto header;
+            header.set_alert_id(mAlert.id());
+            header.mutable_config_key()->set_uid(mConfigKey.GetUid());
+            header.mutable_config_key()->set_id(mConfigKey.GetId());
             incidentReport.addHeader(header);
             service->reportIncident(incidentReport);
         } else {
diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto
index 7d513cd..7a9588d 100644
--- a/cmds/statsd/src/atoms.proto
+++ b/cmds/statsd/src/atoms.proto
@@ -84,6 +84,8 @@
         AppStartChanged app_start_changed = 48;
         AppStartCancelChanged app_start_cancel_changed = 49;
         AppStartFullyDrawnChanged app_start_fully_drawn_changed = 50;
+        LmkEventOccurred lmk_event_occurred = 51;
+        PictureInPictureStateChanged picture_in_picture_state_changed = 52;
         // TODO: Reorder the numbering so that the most frequent occur events occur in the first 15.
     }
 
@@ -101,6 +103,13 @@
         CpuTimePerUidFreq cpu_time_per_uid_freq = 10010;
         WifiActivityEnergyInfo wifi_activity_energy_info = 10011;
         ModemActivityInfo modem_activity_info = 10012;
+        MemoryStat memory_stat = 10013;
+        CpuSuspendTime cpu_suspend_time = 10014;
+        CpuIdleTime cpu_idle_time = 10015;
+        CpuActiveTime cpu_active_time = 10016;
+        CpuClusterTime cpu_cluster_time = 10017;
+        DiskSpace disk_space = 10018;
+        SystemUptime system_uptime = 10019;
     }
 }
 
@@ -321,8 +330,9 @@
     optional string name = 2;
 
     enum State {
-        OFF = 0;
-        ON = 1;
+        FINISHED = 0;
+        STARTED = 1;
+        SCHEDULED = 2;
     }
     optional State state = 3;
 
@@ -931,6 +941,31 @@
 }
 
 /**
+ * Logs a picture-in-picture action
+ * Logged from:
+ *      frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java
+ *      frameworks/base/services/core/java/com/android/server/am/ActivityStackSupervisor.java
+ *      frameworks/base/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java
+ */
+message PictureInPictureStateChanged {
+    optional int32 uid = 1;
+
+    optional string package_name = 2;
+
+    optional string class_name = 3;
+
+    // Picture-in-Picture action occurred, similar to
+    // frameworks/base/proto/src/metrics_constants.proto
+    enum State {
+        ENTERED = 1;
+        EXPANDED_TO_FULL_SCREEN = 2;
+        MINIMIZED = 3;
+        DISMISSED = 4;
+    }
+    optional State state = 4;
+}
+
+/**
  * Pulls bytes transferred via wifi (Sum of foreground and background usage).
  *
  * Pulled from:
@@ -1177,3 +1212,114 @@
     // product of current(mA), voltage(V) and time(ms)
     optional uint64 energy_used = 10;
 }
+
+/*
+ * Logs the memory stats for a process
+ */
+message MemoryStat {
+    // The uid if available. -1 means not available.
+    optional int32 uid = 1;
+
+    // The app package name.
+    optional string pkg_name = 2;
+
+    // # of page-faults
+    optional int64 pgfault = 3;
+
+    // # of major page-faults
+    optional int64 pgmajfault = 4;
+
+    // RSS+CACHE(+SWAP)
+    optional int64 usage_in_bytes = 5;
+}
+
+/*
+ * Logs the event when LMKD kills a process to reduce memory pressure
+ * Logged from:
+ *      system/core/lmkd/lmkd.c
+ */
+message LmkEventOccurred {
+    // The uid if available. -1 means not available.
+    optional int32 uid = 1;
+
+    // The app package name.
+    optional string pkg_name = 2;
+
+    // oom adj score.
+    optional int32 oom_score = 3;
+
+    // Used as start/stop boundaries for the event
+    enum State {
+        UNKNOWN = 0;
+        START = 1;
+        END = 2;
+    }
+    optional State state = 4;
+}
+
+/*
+ * Cpu syspend time for cpu power calculation.
+ */
+message CpuSuspendTime {
+    optional uint64 time = 1;
+}
+
+/*
+ * Cpu idle time for cpu power calculation.
+ */
+message CpuIdleTime {
+    optional uint64 time = 1;
+}
+
+/*
+ * Reads from /proc/uid_concurrent_active_time which has the format:
+ * active: X (X is # cores)
+ * [uid0]: [time-0] [time-1] [time-2] ... (# entries = # cores)
+ * [uid1]: [time-0] [time-1] [time-2] ... ...
+ * ...
+ * Time-N means the CPU time a UID spent running concurrently with N other processes.
+ * The file contains a monotonically increasing count of time for a single boot.
+ */
+message CpuActiveTime {
+    optional uint64 uid = 1;
+    optional uint64 idx = 2;
+    optional uint64 time_ms = 3;
+}
+
+/**
+ * Reads from /proc/uid_concurrent_policy_time which has the format:
+ * policy0: X policy4: Y (there are X cores on policy0, Y cores on policy4)
+ * [uid0]: [time-0-0] [time-0-1] ... [time-1-0] [time-1-1] ...
+ * [uid1]: [time-0-0] [time-0-1] ... [time-1-0] [time-1-1] ...
+ * ...
+ * Time-X-Y means the time a UID spent on clusterX running concurrently with Y other processes.
+ * The file contains a monotonically increasing count of time for a single boot.
+ */
+message CpuClusterTime {
+    optional uint64 uid = 1;
+    optional uint64 idx = 2;
+    optional uint64 time_ms = 3;
+}
+
+/*
+ * Pulls free disk space, for data, system partition and temporary directory.
+ */
+message DiskSpace {
+    // available bytes in data partition
+    optional uint64 data_available_bytes = 1;
+    // available bytes in system partition
+    optional uint64 system_available_bytes = 2;
+    // available bytes in download cache or temp directories
+    optional uint64 temp_available_bytes = 3;
+}
+
+/*
+ * Pulls system up time.
+ */
+message SystemUptime {
+    // Milliseconds since the system was booted.
+    // This clock stops when the system enters deep sleep (CPU off, display dark, device waiting
+    // for external input).
+    // It is not affected by clock scaling, idle, or other power saving mechanisms.
+    optional uint64 uptime_ms = 1;
+}
\ No newline at end of file
diff --git a/cmds/statsd/src/condition/condition_util.cpp b/cmds/statsd/src/condition/condition_util.cpp
index a5aee73..ddfb8d1 100644
--- a/cmds/statsd/src/condition/condition_util.cpp
+++ b/cmds/statsd/src/condition/condition_util.cpp
@@ -158,15 +158,15 @@
 std::vector<HashableDimensionKey> getDimensionKeysForCondition(
     const LogEvent& event, const MetricConditionLink& link) {
     std::vector<Field> whatFields;
-    getFieldsFromFieldMatcher(link.dimensions_in_what(), &whatFields);
+    getFieldsFromFieldMatcher(link.fields_in_what(), &whatFields);
     std::vector<Field> conditionFields;
-    getFieldsFromFieldMatcher(link.dimensions_in_condition(), &conditionFields);
+    getFieldsFromFieldMatcher(link.fields_in_condition(), &conditionFields);
 
     std::vector<HashableDimensionKey> hashableDimensionKeys;
 
     // TODO(yanglu): here we could simplify the logic to get the leaf value node in what and
     // directly construct the full condition value tree.
-    std::vector<DimensionsValue> whatValues = getDimensionKeys(event, link.dimensions_in_what());
+    std::vector<DimensionsValue> whatValues = getDimensionKeys(event, link.fields_in_what());
 
     for (size_t i = 0; i < whatValues.size(); ++i) {
         std::vector<DimensionsValue> whatLeaves;
@@ -185,7 +185,7 @@
             conditionValueMap.insert(std::make_pair(conditionFields[j], whatLeaves[j]));
         }
         std::vector<DimensionsValue> conditionValues;
-        findDimensionsValues(conditionValueMap, link.dimensions_in_condition(), &conditionValues);
+        findDimensionsValues(conditionValueMap, link.fields_in_condition(), &conditionValues);
         if (conditionValues.size() != 1) {
             ALOGE("Not able to find unambiguous field value in condition atom.");
             continue;
diff --git a/cmds/statsd/src/config/ConfigManager.cpp b/cmds/statsd/src/config/ConfigManager.cpp
index 1481921..42994b5 100644
--- a/cmds/statsd/src/config/ConfigManager.cpp
+++ b/cmds/statsd/src/config/ConfigManager.cpp
@@ -296,10 +296,10 @@
     metric->set_condition(204);
     MetricConditionLink* link = metric->add_links();
     link->set_condition(203);
-    link->mutable_dimensions_in_what()->set_field(WAKE_LOCK_TAG_ID);
-    link->mutable_dimensions_in_what()->add_child()->set_field(WAKE_LOCK_UID_KEY_ID);
-    link->mutable_dimensions_in_condition()->set_field(APP_USAGE_TAG_ID);
-    link->mutable_dimensions_in_condition()->add_child()->set_field(APP_USAGE_UID_KEY_ID);
+    link->mutable_fields_in_what()->set_field(WAKE_LOCK_TAG_ID);
+    link->mutable_fields_in_what()->add_child()->set_field(WAKE_LOCK_UID_KEY_ID);
+    link->mutable_fields_in_condition()->set_field(APP_USAGE_TAG_ID);
+    link->mutable_fields_in_condition()->add_child()->set_field(APP_USAGE_UID_KEY_ID);
 
     // Duration of an app holding any wl, while screen on and app in background, slice by uid
     DurationMetric* durationMetric = config.add_duration_metric();
@@ -313,10 +313,10 @@
     durationMetric->set_condition(204);
     link = durationMetric->add_links();
     link->set_condition(203);
-    link->mutable_dimensions_in_what()->set_field(WAKE_LOCK_TAG_ID);
-    link->mutable_dimensions_in_what()->add_child()->set_field(WAKE_LOCK_UID_KEY_ID);
-    link->mutable_dimensions_in_condition()->set_field(APP_USAGE_TAG_ID);
-    link->mutable_dimensions_in_condition()->add_child()->set_field(APP_USAGE_UID_KEY_ID);
+    link->mutable_fields_in_what()->set_field(WAKE_LOCK_TAG_ID);
+    link->mutable_fields_in_what()->add_child()->set_field(WAKE_LOCK_UID_KEY_ID);
+    link->mutable_fields_in_condition()->set_field(APP_USAGE_TAG_ID);
+    link->mutable_fields_in_condition()->add_child()->set_field(APP_USAGE_UID_KEY_ID);
 
     // max Duration of an app holding any wl, while screen on and app in background, slice by uid
     durationMetric = config.add_duration_metric();
@@ -330,10 +330,10 @@
     durationMetric->set_condition(204);
     link = durationMetric->add_links();
     link->set_condition(203);
-    link->mutable_dimensions_in_what()->set_field(WAKE_LOCK_TAG_ID);
-    link->mutable_dimensions_in_what()->add_child()->set_field(WAKE_LOCK_UID_KEY_ID);
-    link->mutable_dimensions_in_condition()->set_field(APP_USAGE_TAG_ID);
-    link->mutable_dimensions_in_condition()->add_child()->set_field(APP_USAGE_UID_KEY_ID);
+    link->mutable_fields_in_what()->set_field(WAKE_LOCK_TAG_ID);
+    link->mutable_fields_in_what()->add_child()->set_field(WAKE_LOCK_UID_KEY_ID);
+    link->mutable_fields_in_condition()->set_field(APP_USAGE_TAG_ID);
+    link->mutable_fields_in_condition()->add_child()->set_field(APP_USAGE_UID_KEY_ID);
 
     // Duration of an app holding any wl, while screen on and app in background
     durationMetric = config.add_duration_metric();
@@ -344,10 +344,10 @@
     durationMetric->set_condition(204);
     link = durationMetric->add_links();
     link->set_condition(203);
-    link->mutable_dimensions_in_what()->set_field(WAKE_LOCK_TAG_ID);
-    link->mutable_dimensions_in_what()->add_child()->set_field(WAKE_LOCK_UID_KEY_ID);
-    link->mutable_dimensions_in_condition()->set_field(APP_USAGE_TAG_ID);
-    link->mutable_dimensions_in_condition()->add_child()->set_field(APP_USAGE_UID_KEY_ID);
+    link->mutable_fields_in_what()->set_field(WAKE_LOCK_TAG_ID);
+    link->mutable_fields_in_what()->add_child()->set_field(WAKE_LOCK_UID_KEY_ID);
+    link->mutable_fields_in_condition()->set_field(APP_USAGE_TAG_ID);
+    link->mutable_fields_in_condition()->add_child()->set_field(APP_USAGE_UID_KEY_ID);
 
 
     // Duration of screen on time.
diff --git a/cmds/statsd/src/external/CpuTimePerUidFreqPuller.cpp b/cmds/statsd/src/external/CpuTimePerUidFreqPuller.cpp
index a61afb4..a751273 100644
--- a/cmds/statsd/src/external/CpuTimePerUidFreqPuller.cpp
+++ b/cmds/statsd/src/external/CpuTimePerUidFreqPuller.cpp
@@ -34,7 +34,7 @@
 namespace os {
 namespace statsd {
 
-static const string sProcFile = "/proc/uid_cputime/show_uid_stat";
+static const string sProcFile = "/proc/uid_time_in_state";
 static const int kLineBufferSize = 1024;
 
 /**
diff --git a/cmds/statsd/src/external/KernelUidCpuActiveTimeReader.cpp b/cmds/statsd/src/external/KernelUidCpuActiveTimeReader.cpp
new file mode 100644
index 0000000..7a2d199
--- /dev/null
+++ b/cmds/statsd/src/external/KernelUidCpuActiveTimeReader.cpp
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define DEBUG true  // STOPSHIP if true
+#include "Log.h"
+
+#include <fstream>
+
+#include "KernelUidCpuActiveTimeReader.h"
+#include "guardrail/StatsdStats.h"
+#include "logd/LogEvent.h"
+#include "statslog.h"
+
+using std::make_shared;
+using std::shared_ptr;
+using std::ifstream;
+
+namespace android {
+namespace os {
+namespace statsd {
+
+static const string sProcFile = "/proc/uid_concurrent_active_time";
+static const int kLineBufferSize = 1024;
+
+/**
+ * Reads /proc/uid_concurrent_active_time which has the format:
+ * active: X (X is # cores)
+ * [uid0]: [time-0] [time-1] [time-2] ... (# entries = # cores)
+ * [uid1]: [time-0] [time-1] [time-2] ... ...
+ * ...
+ * Time-N means the CPU time a UID spent running concurrently with N other processes.
+ * The file contains a monotonically increasing count of time for a single boot.
+ */
+KernelUidCpuActiveTimeReader::KernelUidCpuActiveTimeReader() : StatsPuller(android::util::CPU_ACTIVE_TIME) {
+}
+
+bool KernelUidCpuActiveTimeReader::PullInternal(vector<shared_ptr<LogEvent>>* data) {
+    data->clear();
+
+    ifstream fin;
+    fin.open(sProcFile);
+    if (!fin.good()) {
+        VLOG("Failed to read pseudo file %s", sProcFile.c_str());
+        return false;
+    }
+
+    uint64_t timestamp = time(nullptr) * NS_PER_SEC;
+    char buf[kLineBufferSize];
+    char* pch;
+    while (!fin.eof()) {
+        fin.getline(buf, kLineBufferSize);
+        pch = strtok(buf, " :");
+        if (pch == NULL) break;
+        uint64_t uid = std::stoull(pch);
+        pch = strtok(NULL, " ");
+        uint64_t timeMs;
+        int idx = 0;
+        do {
+            timeMs = std::stoull(pch);
+            auto ptr = make_shared<LogEvent>(mTagId, timestamp);
+            ptr->write(uid);
+            ptr->write(idx);
+            ptr->write(timeMs);
+            ptr->init();
+            data->push_back(ptr);
+            VLOG("uid %lld, freq idx %d, active time %lld", (long long)uid, idx, (long long)timeMs);
+            idx++;
+            pch = strtok(NULL, " ");
+        } while (pch != NULL);
+    }
+    return true;
+}
+
+}  // namespace statsd
+}  // namespace os
+}  // namespace android
diff --git a/cmds/statsd/src/external/KernelUidCpuActiveTimeReader.h b/cmds/statsd/src/external/KernelUidCpuActiveTimeReader.h
new file mode 100644
index 0000000..fcae35f
--- /dev/null
+++ b/cmds/statsd/src/external/KernelUidCpuActiveTimeReader.h
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <utils/String16.h>
+#include "StatsPuller.h"
+
+namespace android {
+namespace os {
+namespace statsd {
+
+class KernelUidCpuActiveTimeReader : public StatsPuller {
+ public:
+    KernelUidCpuActiveTimeReader();
+    bool PullInternal(vector<std::shared_ptr<LogEvent>>* data) override;
+};
+
+}  // namespace statsd
+}  // namespace os
+}  // namespace android
diff --git a/cmds/statsd/src/external/KernelUidCpuClusterTimeReader.cpp b/cmds/statsd/src/external/KernelUidCpuClusterTimeReader.cpp
new file mode 100644
index 0000000..7426e74
--- /dev/null
+++ b/cmds/statsd/src/external/KernelUidCpuClusterTimeReader.cpp
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define DEBUG true  // STOPSHIP if true
+#include "Log.h"
+
+#include <fstream>
+#include "KernelUidCpuClusterTimeReader.h"
+#include "guardrail/StatsdStats.h"
+#include "logd/LogEvent.h"
+#include "statslog.h"
+
+using std::make_shared;
+using std::shared_ptr;
+using std::ifstream;
+
+namespace android {
+namespace os {
+namespace statsd {
+
+static const string sProcFile = "/proc/uid_concurrent_policy_time";
+static const int kLineBufferSize = 1024;
+
+/**
+ * Reads /proc/uid_concurrent_policy_time which has the format:
+ * policy0: X policy4: Y (there are X cores on policy0, Y cores on policy4)
+ * [uid0]: [time-0-0] [time-0-1] ... [time-1-0] [time-1-1] ...
+ * [uid1]: [time-0-0] [time-0-1] ... [time-1-0] [time-1-1] ...
+ * ...
+ * Time-X-Y means the time a UID spent on clusterX running concurrently with Y other processes.
+ * The file contains a monotonically increasing count of time for a single boot.
+ */
+KernelUidCpuClusterTimeReader::KernelUidCpuClusterTimeReader() : StatsPuller(android::util::CPU_CLUSTER_TIME) {
+}
+
+bool KernelUidCpuClusterTimeReader::PullInternal(vector<shared_ptr<LogEvent>>* data) {
+    data->clear();
+
+    ifstream fin;
+    fin.open(sProcFile);
+    if (!fin.good()) {
+        VLOG("Failed to read pseudo file %s", sProcFile.c_str());
+        return false;
+    }
+
+    uint64_t timestamp = time(nullptr) * NS_PER_SEC;
+    char buf[kLineBufferSize];
+    char* pch;
+    while (!fin.eof()) {
+        fin.getline(buf, kLineBufferSize);
+        pch = strtok(buf, " :");
+        if (pch == NULL) break;
+        uint64_t uid = std::stoull(pch);
+        pch = strtok(NULL, " ");
+        uint64_t timeMs;
+        int idx = 0;
+        do {
+            timeMs = std::stoull(pch);
+            auto ptr = make_shared<LogEvent>(mTagId, timestamp);
+            ptr->write(uid);
+            ptr->write(idx);
+            ptr->write(timeMs);
+            ptr->init();
+            data->push_back(ptr);
+            VLOG("uid %lld, freq idx %d, cluster time %lld", (long long)uid, idx, (long long)timeMs);
+            idx++;
+            pch = strtok(NULL, " ");
+        } while (pch != NULL);
+    }
+    return true;
+}
+
+}  // namespace statsd
+}  // namespace os
+}  // namespace android
diff --git a/cmds/statsd/src/external/KernelUidCpuClusterTimeReader.h b/cmds/statsd/src/external/KernelUidCpuClusterTimeReader.h
new file mode 100644
index 0000000..90236ae
--- /dev/null
+++ b/cmds/statsd/src/external/KernelUidCpuClusterTimeReader.h
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <utils/String16.h>
+#include "StatsPuller.h"
+
+namespace android {
+namespace os {
+namespace statsd {
+
+/**
+ * Reads /proc/uid_cputime/show_uid_stat which has the line format:
+ *
+ * uid: user_time_micro_seconds system_time_micro_seconds
+ *
+ * This provides the time a UID's processes spent executing in user-space and kernel-space.
+ * The file contains a monotonically increasing count of time for a single boot.
+ */
+class KernelUidCpuClusterTimeReader : public StatsPuller {
+ public:
+    KernelUidCpuClusterTimeReader();
+    bool PullInternal(vector<std::shared_ptr<LogEvent>>* data) override;
+};
+
+}  // namespace statsd
+}  // namespace os
+}  // namespace android
diff --git a/cmds/statsd/src/external/Perfetto.cpp b/cmds/statsd/src/external/Perfetto.cpp
new file mode 100644
index 0000000..f7b33e7
--- /dev/null
+++ b/cmds/statsd/src/external/Perfetto.cpp
@@ -0,0 +1,111 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "Log.h"
+
+#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h"  // Alert
+
+#include <android-base/unique_fd.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+#include <string>
+
+namespace {
+const char kDropboxTag[] = "perfetto";
+}
+
+namespace android {
+namespace os {
+namespace statsd {
+
+bool CollectPerfettoTraceAndUploadToDropbox(const PerfettoDetails& config) {
+    ALOGD("Starting trace collection through perfetto");
+
+    if (!config.has_trace_config()) {
+        ALOGE("The perfetto trace config is empty, aborting");
+        return false;
+    }
+
+    android::base::unique_fd readPipe;
+    android::base::unique_fd writePipe;
+    if (!android::base::Pipe(&readPipe, &writePipe)) {
+        ALOGE("pipe() failed while calling the Perfetto client: %s", strerror(errno));
+        return false;
+    }
+
+    pid_t pid = fork();
+    if (pid < 0) {
+        ALOGE("fork() failed while calling the Perfetto client: %s", strerror(errno));
+        return false;
+    }
+
+    if (pid == 0) {
+        // Child process.
+
+        // No malloc calls or library calls after this point. Remember that even
+        // ALOGx (aka android_printLog()) can use dynamic memory for vsprintf().
+
+        writePipe.reset();  // Close the write end (owned by the main process).
+
+        // Replace stdin with |readPipe| so the main process can write into it.
+        if (dup2(readPipe.get(), STDIN_FILENO) < 0) _exit(1);
+        execl("/system/bin/perfetto", "perfetto", "--background", "--config", "-", "--dropbox",
+              kDropboxTag, nullptr);
+
+        // execl() doesn't return in case of success, if we get here something failed.
+        _exit(1);
+    }
+
+    // Main process.
+
+    readPipe.reset();  // Close the read end (owned by the child process).
+
+    // Using fopen() because fwrite() has the right logic to chunking write()
+    // over a pipe (see __sfvwrite()).
+    FILE* writePipeStream = fdopen(writePipe.get(), "wb");
+    if (!writePipeStream) {
+        ALOGE("fdopen() failed while calling the Perfetto client: %s", strerror(errno));
+        return false;
+    }
+
+    std::string cfgProto = config.trace_config().SerializeAsString();
+    size_t bytesWritten = fwrite(cfgProto.data(), 1, cfgProto.size(), writePipeStream);
+    fclose(writePipeStream);
+    if (bytesWritten != cfgProto.size() || cfgProto.size() == 0) {
+        ALOGE("fwrite() failed (ret: %zd) while calling the Perfetto client: %s", bytesWritten,
+              strerror(errno));
+        return false;
+    }
+
+    // This does NOT wait for the full duration of the trace. It just waits until the process
+    // has read the config from stdin and detached.
+    int childStatus = 0;
+    waitpid(pid, &childStatus, 0);
+    if (!WIFEXITED(childStatus) || WEXITSTATUS(childStatus) != 0) {
+        ALOGE("Child process failed (0x%x) while calling the Perfetto client", childStatus);
+        return false;
+    }
+
+    ALOGD("CollectPerfettoTraceAndUploadToDropbox() succeeded");
+    return true;
+}
+
+}  // namespace statsd
+}  // namespace os
+}  // namespace android
diff --git a/cmds/statsd/src/external/Perfetto.h b/cmds/statsd/src/external/Perfetto.h
new file mode 100644
index 0000000..e2e0253
--- /dev/null
+++ b/cmds/statsd/src/external/Perfetto.h
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+using android::os::StatsLogEventWrapper;
+
+namespace android {
+namespace os {
+namespace statsd {
+
+class PerfettoDetails;  // Declared in statsd_config.pb.h
+
+// Starts the collection of a Perfetto trace with the given |config|.
+// The trace is uploaded to Dropbox by the perfetto cmdline util once done.
+// This method returns immediately after passing the config and does NOT wait
+// for the full duration of the trace.
+bool CollectPerfettoTraceAndUploadToDropbox(const PerfettoDetails& config);
+
+}  // namespace statsd
+}  // namespace os
+}  // namespace android
diff --git a/cmds/statsd/src/external/StatsPullerManagerImpl.cpp b/cmds/statsd/src/external/StatsPullerManagerImpl.cpp
index bb2e8c0..e06ae48 100644
--- a/cmds/statsd/src/external/StatsPullerManagerImpl.cpp
+++ b/cmds/statsd/src/external/StatsPullerManagerImpl.cpp
@@ -64,6 +64,17 @@
     mPullers.insert({android::util::CPU_TIME_PER_FREQ, make_shared<StatsCompanionServicePuller>(android::util::CPU_TIME_PER_FREQ)});
     mPullers.insert({android::util::CPU_TIME_PER_UID, make_shared<CpuTimePerUidPuller>()});
     mPullers.insert({android::util::CPU_TIME_PER_UID_FREQ, make_shared<CpuTimePerUidFreqPuller>()});
+    mPullers.insert({android::util::CPU_SUSPEND_TIME, make_shared<StatsCompanionServicePuller>(android::util::CPU_SUSPEND_TIME)});
+    mPullers.insert({android::util::CPU_IDLE_TIME, make_shared<StatsCompanionServicePuller>(android::util::CPU_IDLE_TIME)});
+    mPullers.insert({android::util::DISK_SPACE,
+                     make_shared<StatsCompanionServicePuller>(android::util::DISK_SPACE)});
+    mPullers.insert({android::util::SYSTEM_UPTIME,
+                     make_shared<StatsCompanionServicePuller>(android::util::SYSTEM_UPTIME)});
+    mPullers.insert(
+            {android::util::WIFI_ACTIVITY_ENERGY_INFO,
+             make_shared<StatsCompanionServicePuller>(android::util::WIFI_ACTIVITY_ENERGY_INFO)});
+    mPullers.insert({android::util::MODEM_ACTIVITY_INFO,
+                     make_shared<StatsCompanionServicePuller>(android::util::MODEM_ACTIVITY_INFO)});
 
     mStatsCompanionService = StatsService::getStatsCompanionService();
 }
diff --git a/cmds/statsd/src/logd/LogEvent.cpp b/cmds/statsd/src/logd/LogEvent.cpp
index 6782f3f..34fa3c4 100644
--- a/cmds/statsd/src/logd/LogEvent.cpp
+++ b/cmds/statsd/src/logd/LogEvent.cpp
@@ -36,13 +36,13 @@
 using android::util::ProtoOutputStream;
 
 LogEvent::LogEvent(log_msg& msg) {
-    android_log_context context =
+    mContext =
             create_android_log_parser(msg.msg() + sizeof(uint32_t), msg.len() - sizeof(uint32_t));
     mTimestampNs = msg.entry_v1.sec * NS_PER_SEC + msg.entry_v1.nsec;
     mLogUid = msg.entry_v4.uid;
-    init(context);
-    if (context) {
-        android_log_destroy(&context);
+    init(mContext);
+    if (mContext) {
+        android_log_destroy(&mContext);
     }
 }
 
diff --git a/cmds/statsd/src/logd/LogReader.cpp b/cmds/statsd/src/logd/LogReader.cpp
index 7636268..5d43ef3 100644
--- a/cmds/statsd/src/logd/LogReader.cpp
+++ b/cmds/statsd/src/logd/LogReader.cpp
@@ -98,7 +98,10 @@
 
             // Read a message
             err = android_logger_list_read(loggers, &msg);
-            if (err < 0) {
+            // err = 0 - no content, unexpected connection drop or EOF.
+            // err = +ive number - size of retrieved data from logger
+            // err = -ive number, OS supplied error _except_ for -EAGAIN
+            if (err <= 0) {
                 fprintf(stderr, "logcat read failure: %s\n", strerror(err));
                 break;
             }
diff --git a/cmds/statsd/src/metrics/ValueMetricProducer.cpp b/cmds/statsd/src/metrics/ValueMetricProducer.cpp
index c7550f7..e985873 100644
--- a/cmds/statsd/src/metrics/ValueMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/ValueMetricProducer.cpp
@@ -285,8 +285,15 @@
             interval.start = value;
             interval.startUpdated = true;
         } else {
+            // Generally we expect value to be monotonically increasing.
+            // If not, there was a reset event. We take the absolute value as
+            // diff in this case.
             if (interval.startUpdated) {
-                interval.sum += (value - interval.start);
+                if (value > interval.start) {
+                    interval.sum += (value - interval.start);
+                } else {
+                    interval.sum += value;
+                }
                 interval.startUpdated = false;
             } else {
                 VLOG("No start for matching end %ld", value);
diff --git a/cmds/statsd/src/perfetto/perfetto_config.proto b/cmds/statsd/src/perfetto/perfetto_config.proto
new file mode 100644
index 0000000..dc868f9
--- /dev/null
+++ b/cmds/statsd/src/perfetto/perfetto_config.proto
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+syntax = "proto2";
+option optimize_for = LITE_RUNTIME;
+
+package perfetto.protos;
+
+message DataSourceConfig {
+  message FtraceConfig {
+    repeated string event_names = 1;
+  }
+
+  optional string name = 1;
+
+  optional uint32 target_buffer = 2;
+
+  optional FtraceConfig ftrace_config = 100;
+}
+
+message TraceConfig {
+  message BufferConfig {
+    optional uint32 size_kb = 1;
+
+    enum OptimizeFor {
+      DEFAULT = 0;
+      ONE_SHOT_READ = 1;
+
+    }
+    optional OptimizeFor optimize_for = 3;
+
+    enum FillPolicy {
+      UNSPECIFIED = 0;
+      RING_BUFFER = 1;
+    }
+    optional FillPolicy fill_policy = 4;
+  }
+  repeated BufferConfig buffers = 1;
+
+  message DataSource {
+    optional protos.DataSourceConfig config = 1;
+
+    repeated string producer_name_filter = 2;
+  }
+  repeated DataSource data_sources = 2;
+
+  optional uint32 duration_ms = 3;
+}
diff --git a/cmds/statsd/src/statsd_config.proto b/cmds/statsd/src/statsd_config.proto
index afb0bc4..cd60ee7 100644
--- a/cmds/statsd/src/statsd_config.proto
+++ b/cmds/statsd/src/statsd_config.proto
@@ -22,6 +22,8 @@
 option java_package = "com.android.internal.os";
 option java_outer_classname = "StatsdConfigProto";
 
+import "frameworks/base/cmds/statsd/src/perfetto/perfetto_config.proto";
+
 enum Position {
     POSITION_UNKNOWN = 0;
     FIRST = 1;
@@ -151,9 +153,9 @@
 message MetricConditionLink {
     optional int64 condition = 1;
 
-    optional FieldMatcher dimensions_in_what = 2;
+    optional FieldMatcher fields_in_what = 2;
 
-    optional FieldMatcher dimensions_in_condition = 3;
+    optional FieldMatcher fields_in_condition = 3;
 }
 
 message FieldFilter {
@@ -272,7 +274,7 @@
 }
 
 message PerfettoDetails {
-    optional int32 perfetto_stuff = 1;
+    optional perfetto.protos.TraceConfig trace_config = 1;
 }
 
 message Subscription {
diff --git a/cmds/statsd/tests/e2e/GaugeMetric_e2e_test.cpp b/cmds/statsd/tests/e2e/GaugeMetric_e2e_test.cpp
index 8250297..e56a6c5 100644
--- a/cmds/statsd/tests/e2e/GaugeMetric_e2e_test.cpp
+++ b/cmds/statsd/tests/e2e/GaugeMetric_e2e_test.cpp
@@ -57,10 +57,10 @@
 
     auto links = gaugeMetric->add_links();
     links->set_condition(isInBackgroundPredicate.id());
-    auto dimensionWhat = links->mutable_dimensions_in_what();
+    auto dimensionWhat = links->mutable_fields_in_what();
     dimensionWhat->set_field(android::util::APP_START_CHANGED);
     dimensionWhat->add_child()->set_field(1);  // uid field.
-    auto dimensionCondition = links->mutable_dimensions_in_condition();
+    auto dimensionCondition = links->mutable_fields_in_condition();
     dimensionCondition->set_field(android::util::ACTIVITY_FOREGROUND_STATE_CHANGED);
     dimensionCondition->add_child()->set_field(1);  // uid field.
     return config;
diff --git a/cmds/statsd/tests/e2e/MetricConditionLink_e2e_test.cpp b/cmds/statsd/tests/e2e/MetricConditionLink_e2e_test.cpp
index 7512abc..eda16a2 100644
--- a/cmds/statsd/tests/e2e/MetricConditionLink_e2e_test.cpp
+++ b/cmds/statsd/tests/e2e/MetricConditionLink_e2e_test.cpp
@@ -75,20 +75,20 @@
     // Links between crash atom and condition of app is in syncing.
     auto links = countMetric->add_links();
     links->set_condition(isSyncingPredicate.id());
-    auto dimensionWhat = links->mutable_dimensions_in_what();
+    auto dimensionWhat = links->mutable_fields_in_what();
     dimensionWhat->set_field(android::util::PROCESS_LIFE_CYCLE_STATE_CHANGED);
     dimensionWhat->add_child()->set_field(1);  // uid field.
-    auto dimensionCondition = links->mutable_dimensions_in_condition();
+    auto dimensionCondition = links->mutable_fields_in_condition();
     dimensionCondition->set_field(android::util::SYNC_STATE_CHANGED);
     dimensionCondition->add_child()->set_field(1);  // uid field.
 
     // Links between crash atom and condition of app is in background.
     links = countMetric->add_links();
     links->set_condition(isInBackgroundPredicate.id());
-    dimensionWhat = links->mutable_dimensions_in_what();
+    dimensionWhat = links->mutable_fields_in_what();
     dimensionWhat->set_field(android::util::PROCESS_LIFE_CYCLE_STATE_CHANGED);
     dimensionWhat->add_child()->set_field(1);  // uid field.
-    dimensionCondition = links->mutable_dimensions_in_condition();
+    dimensionCondition = links->mutable_fields_in_condition();
     dimensionCondition->set_field(android::util::ACTIVITY_FOREGROUND_STATE_CHANGED);
     dimensionCondition->add_child()->set_field(1);  // uid field.
     return config;
diff --git a/cmds/statsd/tests/metrics/CountMetricProducer_test.cpp b/cmds/statsd/tests/metrics/CountMetricProducer_test.cpp
index c391513..897328d 100644
--- a/cmds/statsd/tests/metrics/CountMetricProducer_test.cpp
+++ b/cmds/statsd/tests/metrics/CountMetricProducer_test.cpp
@@ -149,8 +149,8 @@
     metric.set_condition(StringToId("APP_IN_BACKGROUND_PER_UID_AND_SCREEN_ON"));
     MetricConditionLink* link = metric.add_links();
     link->set_condition(StringToId("APP_IN_BACKGROUND_PER_UID"));
-    *link->mutable_dimensions_in_what() = buildSimpleAtomFieldMatcher(tagId, 1);
-    *link->mutable_dimensions_in_condition() = buildSimpleAtomFieldMatcher(conditionTagId, 2);
+    *link->mutable_fields_in_what() = buildSimpleAtomFieldMatcher(tagId, 1);
+    *link->mutable_fields_in_condition() = buildSimpleAtomFieldMatcher(conditionTagId, 2);
 
     LogEvent event1(tagId, bucketStartTimeNs + 1);
     event1.write("111");  // uid
diff --git a/cmds/statsd/tests/metrics/EventMetricProducer_test.cpp b/cmds/statsd/tests/metrics/EventMetricProducer_test.cpp
index 7171de9..34cde60 100644
--- a/cmds/statsd/tests/metrics/EventMetricProducer_test.cpp
+++ b/cmds/statsd/tests/metrics/EventMetricProducer_test.cpp
@@ -98,8 +98,8 @@
     metric.set_condition(StringToId("APP_IN_BACKGROUND_PER_UID_AND_SCREEN_ON"));
     MetricConditionLink* link = metric.add_links();
     link->set_condition(StringToId("APP_IN_BACKGROUND_PER_UID"));
-    *link->mutable_dimensions_in_what() = buildSimpleAtomFieldMatcher(tagId, 1);
-    *link->mutable_dimensions_in_condition() = buildSimpleAtomFieldMatcher(conditionTagId, 2);
+    *link->mutable_fields_in_what() = buildSimpleAtomFieldMatcher(tagId, 1);
+    *link->mutable_fields_in_condition() = buildSimpleAtomFieldMatcher(conditionTagId, 2);
 
     LogEvent event1(tagId, bucketStartTimeNs + 1);
     EXPECT_TRUE(event1.write("111"));
diff --git a/cmds/statsd/tools/dogfood/Android.mk b/cmds/statsd/tools/dogfood/Android.mk
index a4c0800..32a85b1 100644
--- a/cmds/statsd/tools/dogfood/Android.mk
+++ b/cmds/statsd/tools/dogfood/Android.mk
@@ -19,13 +19,10 @@
 LOCAL_PACKAGE_NAME := StatsdDogfood
 
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
-LOCAL_SRC_FILES += ../../src/stats_log.proto \
-                   ../../src/atoms.proto \
-                   ../../src/statsd_config.proto
 
-LOCAL_PROTOC_FLAGS := --proto_path=$(LOCAL_PATH)/../../src/
 LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
-LOCAL_STATIC_JAVA_LIBRARIES := platformprotoslite
+LOCAL_STATIC_JAVA_LIBRARIES := platformprotoslite \
+                               statsdprotolite
 
 LOCAL_PROTOC_OPTIMIZE_TYPE := lite
 LOCAL_PRIVILEGED_MODULE := true
diff --git a/cmds/statsd/tools/loadtest/Android.mk b/cmds/statsd/tools/loadtest/Android.mk
index 0a0fd66..f5722c2 100644
--- a/cmds/statsd/tools/loadtest/Android.mk
+++ b/cmds/statsd/tools/loadtest/Android.mk
@@ -21,6 +21,7 @@
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
 LOCAL_SRC_FILES += ../../src/stats_log.proto \
                    ../../src/atoms.proto \
+                   ../../src/perfetto/perfetto_config.proto \
                    ../../src/statsd_config.proto
 LOCAL_PROTOC_FLAGS := --proto_path=$(LOCAL_PATH)/../../src/
 LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
diff --git a/core/java/android/accessibilityservice/AccessibilityService.java b/core/java/android/accessibilityservice/AccessibilityService.java
index 97dcb90..0a4541b 100644
--- a/core/java/android/accessibilityservice/AccessibilityService.java
+++ b/core/java/android/accessibilityservice/AccessibilityService.java
@@ -363,6 +363,11 @@
      */
     public static final int GLOBAL_ACTION_LOCK_SCREEN = 8;
 
+    /**
+     * Action to take a screenshot
+     */
+    public static final int GLOBAL_ACTION_TAKE_SCREENSHOT = 9;
+
     private static final String LOG_TAG = "AccessibilityService";
 
     /**
diff --git a/core/java/android/app/ActivityManagerInternal.java b/core/java/android/app/ActivityManagerInternal.java
index ccb54f9..da9f728 100644
--- a/core/java/android/app/ActivityManagerInternal.java
+++ b/core/java/android/app/ActivityManagerInternal.java
@@ -339,4 +339,9 @@
      * Returns maximum number of users that can run simultaneously.
      */
     public abstract int getMaxRunningUsers();
+
+    /**
+     * Returns is the caller has the same uid as the Recents component
+     */
+    public abstract boolean isCallerRecents(int callingUid);
 }
diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java
index 57f9f67..7ca6802 100644
--- a/core/java/android/app/AppOpsManager.java
+++ b/core/java/android/app/AppOpsManager.java
@@ -1326,6 +1326,25 @@
     }
 
     /**
+     * Retrieve the human readable mode.
+     * @hide
+     */
+    public static String modeToString(int mode) {
+        switch (mode) {
+            case MODE_ALLOWED:
+                return "allow";
+            case MODE_IGNORED:
+                return "ignore";
+            case MODE_ERRORED:
+                return "deny";
+            case MODE_DEFAULT:
+                return "default";
+            default:
+                return "mode=" + mode;
+        }
+    }
+
+    /**
      * Retrieve whether the op allows itself to be reset.
      * @hide
      */
diff --git a/core/java/android/app/INotificationManager.aidl b/core/java/android/app/INotificationManager.aidl
index d1aacad..d378f22 100644
--- a/core/java/android/app/INotificationManager.aidl
+++ b/core/java/android/app/INotificationManager.aidl
@@ -75,6 +75,7 @@
     NotificationChannelGroup getNotificationChannelGroup(String pkg, String channelGroupId);
     ParceledListSlice getNotificationChannelGroups(String pkg);
     boolean onlyHasDefaultChannel(String pkg, int uid);
+    ParceledListSlice getRecentNotifyingAppsForUser(int userId);
 
     // TODO: Remove this when callers have been migrated to the equivalent
     // INotificationListener method.
diff --git a/core/java/android/app/Instrumentation.java b/core/java/android/app/Instrumentation.java
index b469de5..c5a58f2 100644
--- a/core/java/android/app/Instrumentation.java
+++ b/core/java/android/app/Instrumentation.java
@@ -17,6 +17,7 @@
 package android.app;
 
 import android.annotation.IntDef;
+import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.content.ActivityNotFoundException;
 import android.content.ComponentName;
@@ -458,7 +459,8 @@
      *
      * @see Context#startActivity(Intent, Bundle)
      */
-    public Activity startActivitySync(Intent intent, @Nullable Bundle options) {
+    @NonNull
+    public Activity startActivitySync(@NonNull Intent intent, @Nullable Bundle options) {
         validateNotAppThread();
 
         synchronized (mSync) {
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index 75dc571..2e3b8af 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -6469,8 +6469,11 @@
                     ? super.mBigContentTitle
                     : mConversationTitle;
             boolean isOneToOne = TextUtils.isEmpty(conversationTitle);
+            CharSequence nameReplacement = null;
             if (hasOnlyWhiteSpaceSenders()) {
                 isOneToOne = true;
+                nameReplacement = conversationTitle;
+                conversationTitle = null;
             }
             RemoteViews contentView = mBuilder.applyStandardTemplateWithActions(
                     mBuilder.getMessagingLayoutResource(),
@@ -6489,6 +6492,8 @@
                     mBuilder.resolveContrastColor());
             contentView.setIcon(R.id.status_bar_latest_event_content, "setLargeIcon",
                     mBuilder.mN.mLargeIcon);
+            contentView.setCharSequence(R.id.status_bar_latest_event_content, "setNameReplacement",
+                    nameReplacement);
             contentView.setBoolean(R.id.status_bar_latest_event_content, "setIsOneToOne",
                     isOneToOne);
             contentView.setBundle(R.id.status_bar_latest_event_content, "setData",
diff --git a/core/java/android/app/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java
index 33277ea..fb8d101 100644
--- a/core/java/android/app/SystemServiceRegistry.java
+++ b/core/java/android/app/SystemServiceRegistry.java
@@ -112,6 +112,7 @@
 import android.os.IHardwarePropertiesManager;
 import android.os.IPowerManager;
 import android.os.IRecoverySystem;
+import android.os.ISystemUpdateManager;
 import android.os.IUserManager;
 import android.os.IncidentManager;
 import android.os.PowerManager;
@@ -119,6 +120,7 @@
 import android.os.RecoverySystem;
 import android.os.ServiceManager;
 import android.os.ServiceManager.ServiceNotFoundException;
+import android.os.SystemUpdateManager;
 import android.os.SystemVibrator;
 import android.os.UserHandle;
 import android.os.UserManager;
@@ -485,6 +487,17 @@
                 return new StorageStatsManager(ctx, service);
             }});
 
+        registerService(Context.SYSTEM_UPDATE_SERVICE, SystemUpdateManager.class,
+                new CachedServiceFetcher<SystemUpdateManager>() {
+                    @Override
+                    public SystemUpdateManager createService(ContextImpl ctx)
+                            throws ServiceNotFoundException {
+                        IBinder b = ServiceManager.getServiceOrThrow(
+                                Context.SYSTEM_UPDATE_SERVICE);
+                        ISystemUpdateManager service = ISystemUpdateManager.Stub.asInterface(b);
+                        return new SystemUpdateManager(service);
+                    }});
+
         registerService(Context.TELEPHONY_SERVICE, TelephonyManager.class,
                 new CachedServiceFetcher<TelephonyManager>() {
             @Override
diff --git a/core/java/android/app/admin/DeviceAdminReceiver.java b/core/java/android/app/admin/DeviceAdminReceiver.java
index 302d52f..ffb3aff 100644
--- a/core/java/android/app/admin/DeviceAdminReceiver.java
+++ b/core/java/android/app/admin/DeviceAdminReceiver.java
@@ -339,7 +339,7 @@
     /**
      * Broadcast action: notify the device owner that a user or profile has been removed.
      * Carries an extra {@link Intent#EXTRA_USER} that has the {@link UserHandle} of
-     * the new user.
+     * the user.
      * @hide
      */
     @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
@@ -347,6 +347,36 @@
     public static final String ACTION_USER_REMOVED = "android.app.action.USER_REMOVED";
 
     /**
+     * Broadcast action: notify the device owner that a user or profile has been started.
+     * Carries an extra {@link Intent#EXTRA_USER} that has the {@link UserHandle} of
+     * the user.
+     * @hide
+     */
+    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+    @BroadcastBehavior(explicitOnly = true)
+    public static final String ACTION_USER_STARTED = "android.app.action.USER_STARTED";
+
+    /**
+     * Broadcast action: notify the device owner that a user or profile has been stopped.
+     * Carries an extra {@link Intent#EXTRA_USER} that has the {@link UserHandle} of
+     * the user.
+     * @hide
+     */
+    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+    @BroadcastBehavior(explicitOnly = true)
+    public static final String ACTION_USER_STOPPED = "android.app.action.USER_STOPPED";
+
+    /**
+     * Broadcast action: notify the device owner that a user or profile has been switched to.
+     * Carries an extra {@link Intent#EXTRA_USER} that has the {@link UserHandle} of
+     * the user.
+     * @hide
+     */
+    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+    @BroadcastBehavior(explicitOnly = true)
+    public static final String ACTION_USER_SWITCHED = "android.app.action.USER_SWITCHED";
+
+    /**
      * A string containing the SHA-256 hash of the bugreport file.
      *
      * @see #ACTION_BUGREPORT_SHARE
@@ -914,6 +944,42 @@
      }
 
     /**
+     * Called when a user or profile is started.
+     *
+     * <p>This callback is only applicable to device owners.
+     *
+     * @param context The running context as per {@link #onReceive}.
+     * @param intent The received intent as per {@link #onReceive}.
+     * @param startedUser The {@link UserHandle} of the user that has just been started.
+     */
+    public void onUserStarted(Context context, Intent intent, UserHandle startedUser) {
+    }
+
+    /**
+     * Called when a user or profile is stopped.
+     *
+     * <p>This callback is only applicable to device owners.
+     *
+     * @param context The running context as per {@link #onReceive}.
+     * @param intent The received intent as per {@link #onReceive}.
+     * @param stoppedUser The {@link UserHandle} of the user that has just been stopped.
+     */
+    public void onUserStopped(Context context, Intent intent, UserHandle stoppedUser) {
+    }
+
+    /**
+     * Called when a user or profile is switched to.
+     *
+     * <p>This callback is only applicable to device owners.
+     *
+     * @param context The running context as per {@link #onReceive}.
+     * @param intent The received intent as per {@link #onReceive}.
+     * @param switchedUser The {@link UserHandle} of the user that has just been switched to.
+     */
+    public void onUserSwitched(Context context, Intent intent, UserHandle switchedUser) {
+    }
+
+    /**
      * Called on the newly assigned owner (either device owner or profile owner) when the ownership
      * transfer has completed successfully.
      *
@@ -989,6 +1055,12 @@
             onUserAdded(context, intent, intent.getParcelableExtra(Intent.EXTRA_USER));
         } else if (ACTION_USER_REMOVED.equals(action)) {
             onUserRemoved(context, intent, intent.getParcelableExtra(Intent.EXTRA_USER));
+        } else if (ACTION_USER_STARTED.equals(action)) {
+            onUserStarted(context, intent, intent.getParcelableExtra(Intent.EXTRA_USER));
+        } else if (ACTION_USER_STOPPED.equals(action)) {
+            onUserStopped(context, intent, intent.getParcelableExtra(Intent.EXTRA_USER));
+        } else if (ACTION_USER_SWITCHED.equals(action)) {
+            onUserSwitched(context, intent, intent.getParcelableExtra(Intent.EXTRA_USER));
         } else if (ACTION_TRANSFER_OWNERSHIP_COMPLETE.equals(action)) {
             PersistableBundle bundle =
                     intent.getParcelableExtra(EXTRA_TRANSFER_OWNER_ADMIN_EXTRAS_BUNDLE);
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index 0be5564..7fccda8 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -1157,9 +1157,17 @@
     public static final String POLICY_DISABLE_SCREEN_CAPTURE = "policy_disable_screen_capture";
 
     /**
+     * Constant to indicate the feature of mandatory backups. Used as argument to
+     * {@link #createAdminSupportIntent(String)}.
+     * @see #setMandatoryBackupTransport(ComponentName, ComponentName)
+     */
+    public static final String POLICY_MANDATORY_BACKUPS = "policy_mandatory_backups";
+
+    /**
      * A String indicating a specific restricted feature. Can be a user restriction from the
      * {@link UserManager}, e.g. {@link UserManager#DISALLOW_ADJUST_VOLUME}, or one of the values
-     * {@link #POLICY_DISABLE_CAMERA} or {@link #POLICY_DISABLE_SCREEN_CAPTURE}.
+     * {@link #POLICY_DISABLE_CAMERA}, {@link #POLICY_DISABLE_SCREEN_CAPTURE} or
+     * {@link #POLICY_MANDATORY_BACKUPS}.
      * @see #createAdminSupportIntent(String)
      * @hide
      */
@@ -6806,7 +6814,8 @@
      * @param restriction Indicates for which feature the dialog should be displayed. Can be a
      *            user restriction from {@link UserManager}, e.g.
      *            {@link UserManager#DISALLOW_ADJUST_VOLUME}, or one of the constants
-     *            {@link #POLICY_DISABLE_CAMERA} or {@link #POLICY_DISABLE_SCREEN_CAPTURE}.
+     *            {@link #POLICY_DISABLE_CAMERA}, {@link #POLICY_DISABLE_SCREEN_CAPTURE} or
+     *            {@link #POLICY_MANDATORY_BACKUPS}.
      * @return Intent An intent to be used to start the dialog-activity if the restriction is
      *            set by an admin, or null if the restriction does not exist or no admin set it.
      */
@@ -9201,10 +9210,13 @@
     /**
      * Allows/disallows printing.
      *
+     * Called by a device owner or a profile owner.
+     * Device owner changes policy for all users. Profile owner can override it if present.
+     * Printing is enabled by default. If {@code FEATURE_PRINTING} is absent, the call is ignored.
+     *
      * @param admin which {@link DeviceAdminReceiver} this request is associated with.
      * @param enabled whether printing should be allowed or not.
      * @throws SecurityException if {@code admin} is neither device, nor profile owner.
-     * @hide
      */
     public void setPrintingEnabled(@NonNull ComponentName admin, boolean enabled) {
         try {
@@ -9215,10 +9227,12 @@
     }
 
     /**
-     * Returns whether printing is enabled for current user.
+     * Returns whether printing is enabled for this user.
+     *
+     * Always {@code false} if {@code FEATURE_PRINTING} is absent.
+     * Otherwise, {@code true} by default.
      *
      * @return {@code true} iff printing is enabled.
-     * @hide
      */
     public boolean isPrintingEnabled() {
         try {
@@ -9233,9 +9247,9 @@
      *
      * Used only by PrintService.
      * @return Localized error message.
-     * @throws SecurityException if caller is not system.
      * @hide
      */
+    @SystemApi
     public CharSequence getPrintingDisabledReason() {
         try {
             return mService.getPrintingDisabledReason();
diff --git a/core/java/android/app/slice/ISliceManager.aidl b/core/java/android/app/slice/ISliceManager.aidl
index 5f0e542..4461b16 100644
--- a/core/java/android/app/slice/ISliceManager.aidl
+++ b/core/java/android/app/slice/ISliceManager.aidl
@@ -29,4 +29,6 @@
     void unpinSlice(String pkg, in Uri uri);
     boolean hasSliceAccess(String pkg);
     SliceSpec[] getPinnedSpecs(in Uri uri, String pkg);
+    int checkSlicePermission(in Uri uri, String pkg, int pid, int uid);
+    void grantPermissionFromUser(in Uri uri, String pkg, String callingPkg, boolean allSlices);
 }
diff --git a/core/java/android/app/slice/Slice.java b/core/java/android/app/slice/Slice.java
index 6093a4a..5bd3440 100644
--- a/core/java/android/app/slice/Slice.java
+++ b/core/java/android/app/slice/Slice.java
@@ -156,6 +156,13 @@
      */
     public static final String HINT_SEE_MORE = "see_more";
     /**
+     * A hint to tell the system that this slice cares about the return value of
+     * {@link SliceProvider#getBindingPackage} and should not cache the result
+     * for multiple apps.
+     * @hide
+     */
+    public static final String HINT_CALLER_NEEDED = "caller_needed";
+    /**
      * Key to retrieve an extra added to an intent when a control is changed.
      */
     public static final String EXTRA_TOGGLE_STATE = "android.app.slice.extra.TOGGLE_STATE";
diff --git a/core/java/android/app/slice/SliceManager.java b/core/java/android/app/slice/SliceManager.java
index 74864cb..09c420c 100644
--- a/core/java/android/app/slice/SliceManager.java
+++ b/core/java/android/app/slice/SliceManager.java
@@ -53,12 +53,34 @@
 
     private static final String TAG = "SliceManager";
 
+    /**
+     * @hide
+     */
+    public static final String ACTION_REQUEST_SLICE_PERMISSION =
+            "android.intent.action.REQUEST_SLICE_PERMISSION";
+
     private final ISliceManager mService;
     private final Context mContext;
     private final ArrayMap<Pair<Uri, SliceCallback>, ISliceListener> mListenerLookup =
             new ArrayMap<>();
 
     /**
+     * Permission denied.
+     * @hide
+     */
+    public static final int PERMISSION_DENIED = -1;
+    /**
+     * Permission granted.
+     * @hide
+     */
+    public static final int PERMISSION_GRANTED = 0;
+    /**
+     * Permission just granted by the user, and should be granted uri permission as well.
+     * @hide
+     */
+    public static final int PERMISSION_USER_GRANTED = 1;
+
+    /**
      * @hide
      */
     public SliceManager(Context context, Handler handler) throws ServiceNotFoundException {
@@ -284,7 +306,7 @@
             extras.putParcelable(SliceProvider.EXTRA_BIND_URI, uri);
             extras.putParcelableArrayList(SliceProvider.EXTRA_SUPPORTED_SPECS,
                     new ArrayList<>(supportedSpecs));
-            final Bundle res = provider.call(resolver.getPackageName(), SliceProvider.METHOD_SLICE,
+            final Bundle res = provider.call(mContext.getPackageName(), SliceProvider.METHOD_SLICE,
                     null, extras);
             Bundle.setDefusable(res, true);
             if (res == null) {
@@ -342,7 +364,7 @@
             extras.putParcelable(SliceProvider.EXTRA_INTENT, intent);
             extras.putParcelableArrayList(SliceProvider.EXTRA_SUPPORTED_SPECS,
                     new ArrayList<>(supportedSpecs));
-            final Bundle res = provider.call(resolver.getPackageName(),
+            final Bundle res = provider.call(mContext.getPackageName(),
                     SliceProvider.METHOD_MAP_INTENT, null, extras);
             if (res == null) {
                 return null;
@@ -358,6 +380,45 @@
     }
 
     /**
+     * Does the permission check to see if a caller has access to a specific slice.
+     * @hide
+     */
+    public void enforceSlicePermission(Uri uri, String pkg, int pid, int uid) {
+        try {
+            if (pkg == null) {
+                throw new SecurityException("No pkg specified");
+            }
+            int result = mService.checkSlicePermission(uri, pkg, pid, uid);
+            if (result == PERMISSION_DENIED) {
+                throw new SecurityException("User " + uid + " does not have slice permission for "
+                        + uri + ".");
+            }
+            if (result == PERMISSION_USER_GRANTED) {
+                // We just had a user grant of this permission and need to grant this to the app
+                // permanently.
+                mContext.grantUriPermission(pkg, uri.buildUpon().path("").build(),
+                        Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION
+                                | Intent.FLAG_GRANT_WRITE_URI_PERMISSION
+                                | Intent.FLAG_GRANT_PREFIX_URI_PERMISSION);
+            }
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Called by SystemUI to grant a slice permission after a dialog is shown.
+     * @hide
+     */
+    public void grantPermissionFromUser(Uri uri, String pkg, boolean allSlices) {
+        try {
+            mService.grantPermissionFromUser(uri, pkg, mContext.getPackageName(), allSlices);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
      * Class that listens to changes in {@link Slice}s.
      */
     public interface SliceCallback {
diff --git a/core/java/android/app/slice/SliceProvider.java b/core/java/android/app/slice/SliceProvider.java
index aa41f14..bd4103f 100644
--- a/core/java/android/app/slice/SliceProvider.java
+++ b/core/java/android/app/slice/SliceProvider.java
@@ -15,13 +15,19 @@
  */
 package android.app.slice;
 
-import android.Manifest.permission;
 import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.app.PendingIntent;
+import android.content.ComponentName;
 import android.content.ContentProvider;
 import android.content.ContentResolver;
 import android.content.ContentValues;
+import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.content.pm.ProviderInfo;
 import android.database.ContentObserver;
 import android.database.Cursor;
 import android.net.Uri;
@@ -83,7 +89,7 @@
  */
 public abstract class SliceProvider extends ContentProvider {
     /**
-     * This is the Android platform's MIME type for a slice: URI
+     * This is the Android platform's MIME type for a URI
      * containing a slice implemented through {@link SliceProvider}.
      */
     public static final String SLICE_TYPE = "vnd.android.slice";
@@ -129,9 +135,41 @@
      * @hide
      */
     public static final String EXTRA_SLICE_DESCENDANTS = "slice_descendants";
+    /**
+     * @hide
+     */
+    public static final String EXTRA_PKG = "pkg";
+    /**
+     * @hide
+     */
+    public static final String EXTRA_PROVIDER_PKG = "provider_pkg";
+    /**
+     * @hide
+     */
+    public static final String EXTRA_OVERRIDE_PKG = "override_pkg";
 
     private static final boolean DEBUG = false;
 
+    private String mBindingPkg;
+    private SliceManager mSliceManager;
+
+    /**
+     * Return the package name of the caller that initiated the binding request
+     * currently happening. The returned package will have been
+     * verified to belong to the calling UID. Returns {@code null} if not
+     * currently performing an {@link #onBindSlice(Uri, List)}.
+     * @hide
+     */
+    public final @Nullable String getBindingPackage() {
+        return mBindingPkg;
+    }
+
+    @Override
+    public void attachInfo(Context context, ProviderInfo info) {
+        super.attachInfo(context, info);
+        mSliceManager = context.getSystemService(SliceManager.class);
+    }
+
     /**
      * Implemented to create a slice. Will be called on the main thread.
      * <p>
@@ -261,54 +299,47 @@
     @Override
     public Bundle call(String method, String arg, Bundle extras) {
         if (method.equals(METHOD_SLICE)) {
-            Uri uri = extras.getParcelable(EXTRA_BIND_URI);
-            if (!UserHandle.isSameApp(Binder.getCallingUid(), Process.myUid())) {
-                getContext().enforceUriPermission(uri, permission.BIND_SLICE,
-                        permission.BIND_SLICE, Binder.getCallingPid(), Binder.getCallingUid(),
-                        Intent.FLAG_GRANT_WRITE_URI_PERMISSION,
-                        "Slice binding requires the permission BIND_SLICE");
-            }
+            Uri uri = getUriWithoutUserId(extras.getParcelable(EXTRA_BIND_URI));
             List<SliceSpec> supportedSpecs = extras.getParcelableArrayList(EXTRA_SUPPORTED_SPECS);
 
-            Slice s = handleBindSlice(uri, supportedSpecs);
+            String callingPackage = getCallingPackage();
+            if (extras.containsKey(EXTRA_OVERRIDE_PKG)) {
+                if (Binder.getCallingUid() != Process.SYSTEM_UID) {
+                    throw new SecurityException("Only the system can override calling pkg");
+                }
+                callingPackage = extras.getString(EXTRA_OVERRIDE_PKG);
+            }
+            Slice s = handleBindSlice(uri, supportedSpecs, callingPackage);
             Bundle b = new Bundle();
             b.putParcelable(EXTRA_SLICE, s);
             return b;
         } else if (method.equals(METHOD_MAP_INTENT)) {
-            getContext().enforceCallingPermission(permission.BIND_SLICE,
-                    "Slice binding requires the permission BIND_SLICE");
             Intent intent = extras.getParcelable(EXTRA_INTENT);
             if (intent == null) return null;
             Uri uri = onMapIntentToUri(intent);
             List<SliceSpec> supportedSpecs = extras.getParcelableArrayList(EXTRA_SUPPORTED_SPECS);
             Bundle b = new Bundle();
             if (uri != null) {
-                Slice s = handleBindSlice(uri, supportedSpecs);
+                Slice s = handleBindSlice(uri, supportedSpecs, getCallingPackage());
                 b.putParcelable(EXTRA_SLICE, s);
             } else {
                 b.putParcelable(EXTRA_SLICE, null);
             }
             return b;
         } else if (method.equals(METHOD_PIN)) {
-            Uri uri = extras.getParcelable(EXTRA_BIND_URI);
-            if (!UserHandle.isSameApp(Binder.getCallingUid(), Process.myUid())) {
-                getContext().enforceUriPermission(uri, permission.BIND_SLICE,
-                        permission.BIND_SLICE, Binder.getCallingPid(), Binder.getCallingUid(),
-                        Intent.FLAG_GRANT_WRITE_URI_PERMISSION,
-                        "Slice binding requires the permission BIND_SLICE");
+            Uri uri = getUriWithoutUserId(extras.getParcelable(EXTRA_BIND_URI));
+            if (Binder.getCallingUid() != Process.SYSTEM_UID) {
+                throw new SecurityException("Only the system can pin/unpin slices");
             }
             handlePinSlice(uri);
         } else if (method.equals(METHOD_UNPIN)) {
-            Uri uri = extras.getParcelable(EXTRA_BIND_URI);
-            if (!UserHandle.isSameApp(Binder.getCallingUid(), Process.myUid())) {
-                getContext().enforceUriPermission(uri, permission.BIND_SLICE,
-                        permission.BIND_SLICE, Binder.getCallingPid(), Binder.getCallingUid(),
-                        Intent.FLAG_GRANT_WRITE_URI_PERMISSION,
-                        "Slice binding requires the permission BIND_SLICE");
+            Uri uri = getUriWithoutUserId(extras.getParcelable(EXTRA_BIND_URI));
+            if (Binder.getCallingUid() != Process.SYSTEM_UID) {
+                throw new SecurityException("Only the system can pin/unpin slices");
             }
             handleUnpinSlice(uri);
         } else if (method.equals(METHOD_GET_DESCENDANTS)) {
-            Uri uri = extras.getParcelable(EXTRA_BIND_URI);
+            Uri uri = getUriWithoutUserId(extras.getParcelable(EXTRA_BIND_URI));
             Bundle b = new Bundle();
             b.putParcelableArrayList(EXTRA_SLICE_DESCENDANTS,
                     new ArrayList<>(handleGetDescendants(uri)));
@@ -370,14 +401,27 @@
         }
     }
 
-    private Slice handleBindSlice(Uri sliceUri, List<SliceSpec> supportedSpecs) {
+    private Slice handleBindSlice(Uri sliceUri, List<SliceSpec> supportedSpecs,
+            String callingPkg) {
+        // This can be removed once Slice#bindSlice is removed and everyone is using
+        // SliceManager#bindSlice.
+        String pkg = callingPkg != null ? callingPkg
+                : getContext().getPackageManager().getNameForUid(Binder.getCallingUid());
+        if (!UserHandle.isSameApp(Binder.getCallingUid(), Process.myUid())) {
+            try {
+                mSliceManager.enforceSlicePermission(sliceUri, pkg,
+                        Binder.getCallingPid(), Binder.getCallingUid());
+            } catch (SecurityException e) {
+                return createPermissionSlice(getContext(), sliceUri, pkg);
+            }
+        }
         if (Looper.myLooper() == Looper.getMainLooper()) {
-            return onBindSliceStrict(sliceUri, supportedSpecs);
+            return onBindSliceStrict(sliceUri, supportedSpecs, pkg);
         } else {
             CountDownLatch latch = new CountDownLatch(1);
             Slice[] output = new Slice[1];
             Handler.getMain().post(() -> {
-                output[0] = onBindSliceStrict(sliceUri, supportedSpecs);
+                output[0] = onBindSliceStrict(sliceUri, supportedSpecs, pkg);
                 latch.countDown();
             });
             try {
@@ -389,15 +433,66 @@
         }
     }
 
-    private Slice onBindSliceStrict(Uri sliceUri, List<SliceSpec> supportedSpecs) {
+    /**
+     * @hide
+     */
+    public static Slice createPermissionSlice(Context context, Uri sliceUri,
+            String callingPackage) {
+        return new Slice.Builder(sliceUri)
+                .addAction(createPermissionIntent(context, sliceUri, callingPackage),
+                        new Slice.Builder(sliceUri.buildUpon().appendPath("permission").build())
+                                .addText(getPermissionString(context, callingPackage), null)
+                                .build())
+                .addHints(Slice.HINT_LIST_ITEM)
+                .build();
+    }
+
+    /**
+     * @hide
+     */
+    public static PendingIntent createPermissionIntent(Context context, Uri sliceUri,
+            String callingPackage) {
+        Intent intent = new Intent(SliceManager.ACTION_REQUEST_SLICE_PERMISSION);
+        intent.setComponent(new ComponentName("com.android.systemui",
+                "com.android.systemui.SlicePermissionActivity"));
+        intent.putExtra(EXTRA_BIND_URI, sliceUri);
+        intent.putExtra(EXTRA_PKG, callingPackage);
+        intent.putExtra(EXTRA_PROVIDER_PKG, context.getPackageName());
+        // Unique pending intent.
+        intent.setData(sliceUri.buildUpon().appendQueryParameter("package", callingPackage)
+                .build());
+
+        return PendingIntent.getActivity(context, 0, intent, 0);
+    }
+
+    /**
+     * @hide
+     */
+    public static CharSequence getPermissionString(Context context, String callingPackage) {
+        PackageManager pm = context.getPackageManager();
+        try {
+            return context.getString(
+                    com.android.internal.R.string.slices_permission_request,
+                    pm.getApplicationInfo(callingPackage, 0).loadLabel(pm),
+                    context.getApplicationInfo().loadLabel(pm));
+        } catch (NameNotFoundException e) {
+            // This shouldn't be possible since the caller is verified.
+            throw new RuntimeException("Unknown calling app", e);
+        }
+    }
+
+    private Slice onBindSliceStrict(Uri sliceUri, List<SliceSpec> supportedSpecs,
+            String callingPackage) {
         ThreadPolicy oldPolicy = StrictMode.getThreadPolicy();
         try {
             StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder()
                     .detectAll()
                     .penaltyDeath()
                     .build());
+            mBindingPkg = callingPackage;
             return onBindSlice(sliceUri, supportedSpecs);
         } finally {
+            mBindingPkg = null;
             StrictMode.setThreadPolicy(oldPolicy);
         }
     }
diff --git a/core/java/android/app/usage/NetworkStats.java b/core/java/android/app/usage/NetworkStats.java
index 2e44a63..da36157 100644
--- a/core/java/android/app/usage/NetworkStats.java
+++ b/core/java/android/app/usage/NetworkStats.java
@@ -227,6 +227,30 @@
          */
         public static final int ROAMING_YES = 0x2;
 
+        /** @hide */
+        @IntDef(prefix = { "DEFAULT_NETWORK_" }, value = {
+                DEFAULT_NETWORK_ALL,
+                DEFAULT_NETWORK_NO,
+                DEFAULT_NETWORK_YES
+        })
+        @Retention(RetentionPolicy.SOURCE)
+        public @interface DefaultNetwork {}
+
+        /**
+         * Combined usage for this network regardless of whether it was the active default network.
+         */
+        public static final int DEFAULT_NETWORK_ALL = -1;
+
+        /**
+         * Usage that occurs while this network is not the active default network.
+         */
+        public static final int DEFAULT_NETWORK_NO = 0x1;
+
+        /**
+         * Usage that occurs while this network is the active default network.
+         */
+        public static final int DEFAULT_NETWORK_YES = 0x2;
+
         /**
          * Special TAG value for total data across all tags
          */
@@ -235,6 +259,7 @@
         private int mUid;
         private int mTag;
         private int mState;
+        private int mDefaultNetwork;
         private int mMetered;
         private int mRoaming;
         private long mBeginTimeStamp;
@@ -286,6 +311,15 @@
             return 0;
         }
 
+        private static @DefaultNetwork int convertDefaultNetwork(int defaultNetwork) {
+            switch (defaultNetwork) {
+                case android.net.NetworkStats.DEFAULT_NETWORK_ALL : return DEFAULT_NETWORK_ALL;
+                case android.net.NetworkStats.DEFAULT_NETWORK_NO: return DEFAULT_NETWORK_NO;
+                case android.net.NetworkStats.DEFAULT_NETWORK_YES: return DEFAULT_NETWORK_YES;
+            }
+            return 0;
+        }
+
         public Bucket() {
         }
 
@@ -351,6 +385,21 @@
         }
 
         /**
+         * Default network state. One of the following values:<p/>
+         * <ul>
+         * <li>{@link #DEFAULT_NETWORK_ALL}</li>
+         * <li>{@link #DEFAULT_NETWORK_NO}</li>
+         * <li>{@link #DEFAULT_NETWORK_YES}</li>
+         * </ul>
+         * <p>Indicates whether the network usage occurred on the system default network for this
+         * type of traffic, or whether the application chose to send this traffic on a network that
+         * was not the one selected by the system.
+         */
+        public @DefaultNetwork int getDefaultNetwork() {
+            return mDefaultNetwork;
+        }
+
+        /**
          * Start timestamp of the bucket's time interval. Defined in terms of "Unix time", see
          * {@link java.lang.System#currentTimeMillis}.
          * @return Start of interval.
@@ -551,6 +600,8 @@
         bucketOut.mUid = Bucket.convertUid(mRecycledSummaryEntry.uid);
         bucketOut.mTag = Bucket.convertTag(mRecycledSummaryEntry.tag);
         bucketOut.mState = Bucket.convertState(mRecycledSummaryEntry.set);
+        bucketOut.mDefaultNetwork = Bucket.convertDefaultNetwork(
+                mRecycledSummaryEntry.defaultNetwork);
         bucketOut.mMetered = Bucket.convertMetered(mRecycledSummaryEntry.metered);
         bucketOut.mRoaming = Bucket.convertRoaming(mRecycledSummaryEntry.roaming);
         bucketOut.mBeginTimeStamp = mStartTimeStamp;
@@ -600,6 +651,7 @@
                 bucketOut.mUid = Bucket.convertUid(getUid());
                 bucketOut.mTag = Bucket.convertTag(mTag);
                 bucketOut.mState = Bucket.STATE_ALL;
+                bucketOut.mDefaultNetwork = Bucket.DEFAULT_NETWORK_ALL;
                 bucketOut.mMetered = Bucket.METERED_ALL;
                 bucketOut.mRoaming = Bucket.ROAMING_ALL;
                 bucketOut.mBeginTimeStamp = mRecycledHistoryEntry.bucketStart;
diff --git a/core/java/android/app/usage/NetworkStatsManager.java b/core/java/android/app/usage/NetworkStatsManager.java
index 853b003..5576e86 100644
--- a/core/java/android/app/usage/NetworkStatsManager.java
+++ b/core/java/android/app/usage/NetworkStatsManager.java
@@ -60,10 +60,11 @@
  * {@link #queryDetailsForUid} <p />
  * {@link #queryDetails} <p />
  * These queries do not aggregate over time but do aggregate over state, metered and roaming.
- * Therefore there can be multiple buckets for a particular key but all Bucket's state is going to
- * be {@link NetworkStats.Bucket#STATE_ALL}, all Bucket's metered is going to be
- * {@link NetworkStats.Bucket#METERED_ALL}, and all Bucket's roaming is going to be
- * {@link NetworkStats.Bucket#ROAMING_ALL}.
+ * Therefore there can be multiple buckets for a particular key. However, all Buckets will have
+ * {@code state} {@link NetworkStats.Bucket#STATE_ALL},
+ * {@code defaultNetwork} {@link NetworkStats.Bucket#DEFAULT_NETWORK_ALL},
+ * {@code metered } {@link NetworkStats.Bucket#METERED_ALL},
+ * {@code roaming} {@link NetworkStats.Bucket#ROAMING_ALL}.
  * <p />
  * <b>NOTE:</b> Calling {@link #querySummaryForDevice} or accessing stats for apps other than the
  * calling app requires the permission {@link android.Manifest.permission#PACKAGE_USAGE_STATS},
@@ -130,13 +131,26 @@
         }
     }
 
+    /** @hide */
+    public Bucket querySummaryForDevice(NetworkTemplate template,
+            long startTime, long endTime) throws SecurityException, RemoteException {
+        Bucket bucket = null;
+        NetworkStats stats = new NetworkStats(mContext, template, mFlags, startTime, endTime);
+        bucket = stats.getDeviceSummaryForNetwork();
+
+        stats.close();
+        return bucket;
+    }
+
     /**
      * Query network usage statistics summaries. Result is summarised data usage for the whole
      * device. Result is a single Bucket aggregated over time, state, uid, tag, metered, and
      * roaming. This means the bucket's start and end timestamp are going to be the same as the
      * 'startTime' and 'endTime' parameters. State is going to be
      * {@link NetworkStats.Bucket#STATE_ALL}, uid {@link NetworkStats.Bucket#UID_ALL},
-     * tag {@link NetworkStats.Bucket#TAG_NONE}, metered {@link NetworkStats.Bucket#METERED_ALL},
+     * tag {@link NetworkStats.Bucket#TAG_NONE},
+     * default network {@link NetworkStats.Bucket#DEFAULT_NETWORK_ALL},
+     * metered {@link NetworkStats.Bucket#METERED_ALL},
      * and roaming {@link NetworkStats.Bucket#ROAMING_ALL}.
      *
      * @param networkType As defined in {@link ConnectivityManager}, e.g.
@@ -160,12 +174,7 @@
             return null;
         }
 
-        Bucket bucket = null;
-        NetworkStats stats = new NetworkStats(mContext, template, mFlags, startTime, endTime);
-        bucket = stats.getDeviceSummaryForNetwork();
-
-        stats.close();
-        return bucket;
+        return querySummaryForDevice(template, startTime, endTime);
     }
 
     /**
@@ -209,10 +218,10 @@
     /**
      * Query network usage statistics summaries. Result filtered to include only uids belonging to
      * calling user. Result is aggregated over time, hence all buckets will have the same start and
-     * end timestamps. Not aggregated over state, uid, metered, or roaming. This means buckets'
-     * start and end timestamps are going to be the same as the 'startTime' and 'endTime'
-     * parameters. State, uid, metered, and roaming are going to vary, and tag is going to be the
-     * same.
+     * end timestamps. Not aggregated over state, uid, default network, metered, or roaming. This
+     * means buckets' start and end timestamps are going to be the same as the 'startTime' and
+     * 'endTime' parameters. State, uid, metered, and roaming are going to vary, and tag is going to
+     * be the same.
      *
      * @param networkType As defined in {@link ConnectivityManager}, e.g.
      *            {@link ConnectivityManager#TYPE_MOBILE}, {@link ConnectivityManager#TYPE_WIFI}
@@ -258,9 +267,10 @@
      * belonging to calling user. Result is aggregated over state but not aggregated over time.
      * This means buckets' start and end timestamps are going to be between 'startTime' and
      * 'endTime' parameters. State is going to be {@link NetworkStats.Bucket#STATE_ALL}, uid the
-     * same as the 'uid' parameter and tag the same as 'tag' parameter. metered is going to be
-     * {@link NetworkStats.Bucket#METERED_ALL}, and roaming is going to be
-     * {@link NetworkStats.Bucket#ROAMING_ALL}.
+     * same as the 'uid' parameter and tag the same as 'tag' parameter.
+     * defaultNetwork is going to be {@link NetworkStats.Bucket#DEFAULT_NETWORK_ALL},
+     * metered is going to be {@link NetworkStats.Bucket#METERED_ALL}, and
+     * roaming is going to be {@link NetworkStats.Bucket#ROAMING_ALL}.
      * <p>Only includes buckets that atomically occur in the inclusive time range. Doesn't
      * interpolate across partial buckets. Since bucket length is in the order of hours, this
      * method cannot be used to measure data usage on a fine grained time scale.
@@ -301,9 +311,10 @@
      * metered, nor roaming. This means buckets' start and end timestamps are going to be between
      * 'startTime' and 'endTime' parameters. State is going to be
      * {@link NetworkStats.Bucket#STATE_ALL}, uid will vary,
-     * tag {@link NetworkStats.Bucket#TAG_NONE}, metered is going to be
-     * {@link NetworkStats.Bucket#METERED_ALL}, and roaming is going to be
-     * {@link NetworkStats.Bucket#ROAMING_ALL}.
+     * tag {@link NetworkStats.Bucket#TAG_NONE},
+     * default network is going to be {@link NetworkStats.Bucket#DEFAULT_NETWORK_ALL},
+     * metered is going to be {@link NetworkStats.Bucket#METERED_ALL},
+     * and roaming is going to be {@link NetworkStats.Bucket#ROAMING_ALL}.
      * <p>Only includes buckets that atomically occur in the inclusive time range. Doesn't
      * interpolate across partial buckets. Since bucket length is in the order of hours, this
      * method cannot be used to measure data usage on a fine grained time scale.
@@ -335,6 +346,37 @@
         return result;
     }
 
+    /** @hide */
+    public void registerUsageCallback(NetworkTemplate template, int networkType,
+            long thresholdBytes, UsageCallback callback, @Nullable Handler handler) {
+        checkNotNull(callback, "UsageCallback cannot be null");
+
+        final Looper looper;
+        if (handler == null) {
+            looper = Looper.myLooper();
+        } else {
+            looper = handler.getLooper();
+        }
+
+        DataUsageRequest request = new DataUsageRequest(DataUsageRequest.REQUEST_ID_UNSET,
+                template, thresholdBytes);
+        try {
+            CallbackHandler callbackHandler = new CallbackHandler(looper, networkType,
+                    template.getSubscriberId(), callback);
+            callback.request = mService.registerUsageCallback(
+                    mContext.getOpPackageName(), request, new Messenger(callbackHandler),
+                    new Binder());
+            if (DBG) Log.d(TAG, "registerUsageCallback returned " + callback.request);
+
+            if (callback.request == null) {
+                Log.e(TAG, "Request from callback is null; should not happen");
+            }
+        } catch (RemoteException e) {
+            if (DBG) Log.d(TAG, "Remote exception when registering callback");
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
     /**
      * Registers to receive notifications about data usage on specified networks.
      *
@@ -363,15 +405,7 @@
      */
     public void registerUsageCallback(int networkType, String subscriberId, long thresholdBytes,
             UsageCallback callback, @Nullable Handler handler) {
-        checkNotNull(callback, "UsageCallback cannot be null");
-
-        final Looper looper;
-        if (handler == null) {
-            looper = Looper.myLooper();
-        } else {
-            looper = handler.getLooper();
-        }
-
+        NetworkTemplate template = createTemplate(networkType, subscriberId);
         if (DBG) {
             Log.d(TAG, "registerUsageCallback called with: {"
                 + " networkType=" + networkType
@@ -379,25 +413,7 @@
                 + " thresholdBytes=" + thresholdBytes
                 + " }");
         }
-
-        NetworkTemplate template = createTemplate(networkType, subscriberId);
-        DataUsageRequest request = new DataUsageRequest(DataUsageRequest.REQUEST_ID_UNSET,
-                template, thresholdBytes);
-        try {
-            CallbackHandler callbackHandler = new CallbackHandler(looper, networkType,
-                    subscriberId, callback);
-            callback.request = mService.registerUsageCallback(
-                    mContext.getOpPackageName(), request, new Messenger(callbackHandler),
-                    new Binder());
-            if (DBG) Log.d(TAG, "registerUsageCallback returned " + callback.request);
-
-            if (callback.request == null) {
-                Log.e(TAG, "Request from callback is null; should not happen");
-            }
-        } catch (RemoteException e) {
-            if (DBG) Log.d(TAG, "Remote exception when registering callback");
-            throw e.rethrowFromSystemServer();
-        }
+        registerUsageCallback(template, networkType, thresholdBytes, callback, handler);
     }
 
     /**
diff --git a/core/java/android/app/usage/UsageStatsManagerInternal.java b/core/java/android/app/usage/UsageStatsManagerInternal.java
index 5cd0981..bd978e3 100644
--- a/core/java/android/app/usage/UsageStatsManagerInternal.java
+++ b/core/java/android/app/usage/UsageStatsManagerInternal.java
@@ -176,6 +176,12 @@
     public abstract void setActiveAdminApps(Set<String> adminApps, int userId);
 
     /**
+     * Called by DevicePolicyManagerService during boot to inform that admin data is loaded and
+     * pushed to UsageStatsService.
+     */
+    public abstract void onAdminDataAvailable();
+
+    /**
      * Return usage stats.
      *
      * @param obfuscateInstantApps whether instant app package names need to be obfuscated in the
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index 265f7c7..f69aab01 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -3024,7 +3024,8 @@
             //@hide: INCIDENT_SERVICE,
             //@hide: STATS_COMPANION_SERVICE,
             COMPANION_DEVICE_SERVICE,
-            CROSS_PROFILE_APPS_SERVICE
+            CROSS_PROFILE_APPS_SERVICE,
+            //@hide: SYSTEM_UPDATE_SERVICE,
     })
     @Retention(RetentionPolicy.SOURCE)
     public @interface ServiceName {}
@@ -3242,6 +3243,17 @@
 
     /**
      * Use with {@link #getSystemService(String)} to retrieve a
+     * {@link android.os.SystemUpdateManager} for accessing the system update
+     * manager service.
+     *
+     * @see #getSystemService(String)
+     * @hide
+     */
+    @SystemApi
+    public static final String SYSTEM_UPDATE_SERVICE = "system_update";
+
+    /**
+     * Use with {@link #getSystemService(String)} to retrieve a
      * {@link android.view.WindowManager} for accessing the system's window
      * manager.
      *
diff --git a/core/java/android/content/pm/ApplicationInfo.java b/core/java/android/content/pm/ApplicationInfo.java
index 15e119b..84cbdb4 100644
--- a/core/java/android/content/pm/ApplicationInfo.java
+++ b/core/java/android/content/pm/ApplicationInfo.java
@@ -26,7 +26,6 @@
 import android.content.pm.PackageManager.NameNotFoundException;
 import android.content.res.Resources;
 import android.graphics.drawable.Drawable;
-import android.os.Build;
 import android.os.Environment;
 import android.os.Parcel;
 import android.os.Parcelable;
@@ -1593,11 +1592,6 @@
         return (privateFlags & ApplicationInfo.PRIVATE_FLAG_VENDOR) != 0;
     }
 
-    /** @hide */
-    public boolean isTargetingDeprecatedSdkVersion() {
-        return targetSdkVersion < Build.VERSION.MIN_SUPPORTED_TARGET_SDK_INT;
-    }
-
     /**
      * Returns whether or not this application was installed as a virtual preload.
      */
diff --git a/core/java/android/content/pm/dex/DexMetadataHelper.java b/core/java/android/content/pm/dex/DexMetadataHelper.java
index c5f1c85..5d10b88 100644
--- a/core/java/android/content/pm/dex/DexMetadataHelper.java
+++ b/core/java/android/content/pm/dex/DexMetadataHelper.java
@@ -107,8 +107,8 @@
      * For each code path (.apk) the method checks if a matching dex metadata file (.dm) exists.
      * If it does it adds the pair to the returned map.
      *
-     * Note that this method will do a strict
-     * matching based on the extension ('foo.dm' will only match 'foo.apk').
+     * Note that this method will do a loose
+     * matching based on the extension ('foo.dm' will match 'foo.apk' or 'foo').
      *
      * This should only be used for code paths extracted from a package structure after the naming
      * was enforced in the installer.
@@ -118,7 +118,7 @@
         ArrayMap<String, String> result = new ArrayMap<>();
         for (int i = codePaths.size() - 1; i >= 0; i--) {
             String codePath = codePaths.get(i);
-            String dexMetadataPath = buildDexMetadataPathForApk(codePath);
+            String dexMetadataPath = buildDexMetadataPathForFile(new File(codePath));
 
             if (Files.exists(Paths.get(dexMetadataPath))) {
                 result.put(codePath, dexMetadataPath);
diff --git a/core/java/android/hardware/HardwareBuffer.java b/core/java/android/hardware/HardwareBuffer.java
index 7866b52..9aa3f40 100644
--- a/core/java/android/hardware/HardwareBuffer.java
+++ b/core/java/android/hardware/HardwareBuffer.java
@@ -25,11 +25,11 @@
 import dalvik.annotation.optimization.FastNative;
 import dalvik.system.CloseGuard;
 
+import libcore.util.NativeAllocationRegistry;
+
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 
-import libcore.util.NativeAllocationRegistry;
-
 /**
  * HardwareBuffer wraps a native <code>AHardwareBuffer</code> object, which is a low-level object
  * representing a memory buffer accessible by various hardware units. HardwareBuffer allows sharing
@@ -42,18 +42,25 @@
 public final class HardwareBuffer implements Parcelable, AutoCloseable {
     /** @hide */
     @Retention(RetentionPolicy.SOURCE)
-    @IntDef(prefix = { "RGB", "BLOB" }, value = {
+    @IntDef(prefix = { "RGB", "BLOB", "D_", "DS_", "S_" }, value = {
             RGBA_8888,
             RGBA_FP16,
             RGBA_1010102,
             RGBX_8888,
             RGB_888,
             RGB_565,
-            BLOB
+            BLOB,
+            D_16,
+            D_24,
+            DS_24UI8,
+            D_FP32,
+            DS_FP32UI8,
+            S_UI8,
     })
     public @interface Format {
     }
 
+    @Format
     /** Format: 8 bits each red, green, blue, alpha */
     public static final int RGBA_8888    = 1;
     /** Format: 8 bits each red, green, blue, alpha, alpha is always 0xFF */
@@ -68,6 +75,18 @@
     public static final int RGBA_1010102 = 0x2b;
     /** Format: opaque format used for raw data transfer; must have a height of 1 */
     public static final int BLOB         = 0x21;
+    /** Format: 16 bits depth */
+    public static final int D_16         = 0x30;
+    /** Format: 24 bits depth */
+    public static final int D_24         = 0x31;
+    /** Format: 24 bits depth, 8 bits stencil */
+    public static final int DS_24UI8     = 0x32;
+    /** Format: 32 bits depth */
+    public static final int D_FP32       = 0x33;
+    /** Format: 32 bits depth, 8 bits stencil */
+    public static final int DS_FP32UI8   = 0x34;
+    /** Format: 8 bits stencil */
+    public static final int S_UI8        = 0x35;
 
     // Note: do not rename, this field is used by native code
     private long mNativeObject;
@@ -82,9 +101,11 @@
     @LongDef(flag = true, value = {USAGE_CPU_READ_RARELY, USAGE_CPU_READ_OFTEN,
             USAGE_CPU_WRITE_RARELY, USAGE_CPU_WRITE_OFTEN, USAGE_GPU_SAMPLED_IMAGE,
             USAGE_GPU_COLOR_OUTPUT, USAGE_PROTECTED_CONTENT, USAGE_VIDEO_ENCODE,
-            USAGE_GPU_DATA_BUFFER, USAGE_SENSOR_DIRECT_DATA})
+            USAGE_GPU_DATA_BUFFER, USAGE_SENSOR_DIRECT_DATA, USAGE_GPU_CUBE_MAP,
+            USAGE_GPU_MIPMAP_COMPLETE})
     public @interface Usage {};
 
+    @Usage
     /** Usage: The buffer will sometimes be read by the CPU */
     public static final long USAGE_CPU_READ_RARELY       = 2;
     /** Usage: The buffer will often be read by the CPU */
@@ -107,6 +128,10 @@
     public static final long USAGE_SENSOR_DIRECT_DATA     = 1 << 23;
     /** Usage: The buffer will be used as a shader storage or uniform buffer object */
     public static final long USAGE_GPU_DATA_BUFFER        = 1 << 24;
+    /** Usage: The buffer will be used as a cube map texture */
+    public static final long USAGE_GPU_CUBE_MAP           = 1 << 25;
+    /** Usage: The buffer contains a complete mipmap hierarchy */
+    public static final long USAGE_GPU_MIPMAP_COMPLETE    = 1 << 26;
 
     // The approximate size of a native AHardwareBuffer object.
     private static final long NATIVE_HARDWARE_BUFFER_SIZE = 232;
@@ -118,15 +143,9 @@
      *
      * @param width The width in pixels of the buffer
      * @param height The height in pixels of the buffer
-     * @param format The format of each pixel, one of {@link #RGBA_8888}, {@link #RGBA_FP16},
-     * {@link #RGBX_8888}, {@link #RGB_565}, {@link #RGB_888}, {@link #RGBA_1010102}, {@link #BLOB}
+     * @param format The @Format of each pixel
      * @param layers The number of layers in the buffer
-     * @param usage Flags describing how the buffer will be used, one of
-     *     {@link #USAGE_CPU_READ_RARELY}, {@link #USAGE_CPU_READ_OFTEN},
-     *     {@link #USAGE_CPU_WRITE_RARELY}, {@link #USAGE_CPU_WRITE_OFTEN},
-     *     {@link #USAGE_GPU_SAMPLED_IMAGE}, {@link #USAGE_GPU_COLOR_OUTPUT},
-     *     {@link #USAGE_GPU_DATA_BUFFER}, {@link #USAGE_PROTECTED_CONTENT},
-     *     {@link #USAGE_SENSOR_DIRECT_DATA}, {@link #USAGE_VIDEO_ENCODE}
+     * @param usage The @Usage flags describing how the buffer will be used
      * @return A <code>HardwareBuffer</code> instance if successful, or throws an
      *     IllegalArgumentException if the dimensions passed are invalid (either zero, negative, or
      *     too large to allocate), if the format is not supported, if the requested number of layers
@@ -154,7 +173,7 @@
         if (nativeObject == 0) {
             throw new IllegalArgumentException("Unable to create a HardwareBuffer, either the " +
                     "dimensions passed were too large, too many image layers were requested, " +
-                    "or an invalid set of usage flags was passed");
+                    "or an invalid set of usage flags or invalid format was passed");
         }
         return new HardwareBuffer(nativeObject);
     }
@@ -206,8 +225,7 @@
     }
 
     /**
-     * Returns the format of this buffer, one of {@link #RGBA_8888}, {@link #RGBA_FP16},
-     * {@link #RGBX_8888}, {@link #RGB_565}, {@link #RGB_888}, {@link #RGBA_1010102}, {@link #BLOB}.
+     * Returns the @Format of this buffer.
      */
     @Format
     public int getFormat() {
@@ -338,6 +356,12 @@
             case RGB_565:
             case RGB_888:
             case BLOB:
+            case D_16:
+            case D_24:
+            case DS_24UI8:
+            case D_FP32:
+            case DS_FP32UI8:
+            case S_UI8:
                 return true;
         }
         return false;
diff --git a/core/java/android/hardware/camera2/CameraCharacteristics.java b/core/java/android/hardware/camera2/CameraCharacteristics.java
index 1201ef4..96d043c 100644
--- a/core/java/android/hardware/camera2/CameraCharacteristics.java
+++ b/core/java/android/hardware/camera2/CameraCharacteristics.java
@@ -22,9 +22,11 @@
 import android.hardware.camera2.impl.PublicKey;
 import android.hardware.camera2.impl.SyntheticKey;
 import android.hardware.camera2.params.SessionConfiguration;
+import android.hardware.camera2.utils.ArrayUtils;
 import android.hardware.camera2.utils.TypeReference;
 import android.util.Rational;
 
+import java.util.Arrays;
 import java.util.Collections;
 import java.util.List;
 
@@ -171,6 +173,7 @@
     private List<CameraCharacteristics.Key<?>> mKeys;
     private List<CaptureRequest.Key<?>> mAvailableRequestKeys;
     private List<CaptureRequest.Key<?>> mAvailableSessionKeys;
+    private List<CaptureRequest.Key<?>> mAvailablePhysicalRequestKeys;
     private List<CaptureResult.Key<?>> mAvailableResultKeys;
 
     /**
@@ -314,6 +317,45 @@
     }
 
     /**
+     * <p>Returns a subset of {@link #getAvailableCaptureRequestKeys} keys that can
+     * be overriden for physical devices backing a logical multi-camera.</p>
+     *
+     * <p>This is a subset of android.request.availableRequestKeys which contains a list
+     * of keys that can be overriden using {@link CaptureRequest.Builder#setPhysicalCameraKey }.
+     * The respective value of such request key can be obtained by calling
+     * {@link CaptureRequest.Builder#getPhysicalCameraKey }. Capture requests that contain
+     * individual physical device requests must be built via
+     * {@link android.hardware.camera2.CameraDevice#createCaptureRequest(int, Set)}.
+     * Such extended capture requests can be passed only to
+     * {@link CameraCaptureSession#capture } or {@link CameraCaptureSession#captureBurst } and
+     * not to {@link CameraCaptureSession#setRepeatingRequest } or
+     * {@link CameraCaptureSession#setRepeatingBurst }.</p>
+     *
+     * <p>The list returned is not modifiable, so any attempts to modify it will throw
+     * a {@code UnsupportedOperationException}.</p>
+     *
+     * <p>Each key is only listed once in the list. The order of the keys is undefined.</p>
+     *
+     * @return List of keys that can be overriden in individual physical device requests.
+     * In case the camera device doesn't support such keys the list can be null.
+     */
+    @SuppressWarnings({"unchecked"})
+    public List<CaptureRequest.Key<?>> getAvailablePhysicalCameraRequestKeys() {
+        if (mAvailableSessionKeys == null) {
+            Object crKey = CaptureRequest.Key.class;
+            Class<CaptureRequest.Key<?>> crKeyTyped = (Class<CaptureRequest.Key<?>>)crKey;
+
+            int[] filterTags = get(REQUEST_AVAILABLE_PHYSICAL_CAMERA_REQUEST_KEYS);
+            if (filterTags == null) {
+                return null;
+            }
+            mAvailablePhysicalRequestKeys =
+                    getAvailableKeyList(CaptureRequest.class, crKeyTyped, filterTags);
+        }
+        return mAvailablePhysicalRequestKeys;
+    }
+
+    /**
      * Returns the list of keys supported by this {@link CameraDevice} for querying
      * with a {@link CaptureRequest}.
      *
@@ -407,6 +449,47 @@
         return Collections.unmodifiableList(staticKeyList);
     }
 
+    /**
+     * Returns the list of physical camera ids that this logical {@link CameraDevice} is
+     * made up of.
+     *
+     * <p>A camera device is a logical camera if it has
+     * REQUEST_AVAILABLE_CAPABILITIES_LOGICAL_MULTI_CAMERA capability. If the camera device
+     * doesn't have the capability, the return value will be an empty list. </p>
+     *
+     * <p>The list returned is not modifiable, so any attempts to modify it will throw
+     * a {@code UnsupportedOperationException}.</p>
+     *
+     * <p>Each physical camera id is only listed once in the list. The order of the keys
+     * is undefined.</p>
+     *
+     * @return List of physical camera ids for this logical camera device.
+     */
+    @NonNull
+    public List<String> getPhysicalCameraIds() {
+        int[] availableCapabilities = get(REQUEST_AVAILABLE_CAPABILITIES);
+        if (availableCapabilities == null) {
+            throw new AssertionError("android.request.availableCapabilities must be non-null "
+                        + "in the characteristics");
+        }
+
+        if (!ArrayUtils.contains(availableCapabilities,
+                REQUEST_AVAILABLE_CAPABILITIES_LOGICAL_MULTI_CAMERA)) {
+            return Collections.emptyList();
+        }
+        byte[] physicalCamIds = get(LOGICAL_MULTI_CAMERA_PHYSICAL_IDS);
+
+        String physicalCamIdString = null;
+        try {
+            physicalCamIdString = new String(physicalCamIds, "UTF-8");
+        } catch (java.io.UnsupportedEncodingException e) {
+            throw new AssertionError("android.logicalCam.physicalIds must be UTF-8 string");
+        }
+        String[] physicalCameraIdList = physicalCamIdString.split("\0");
+
+        return Collections.unmodifiableList(Arrays.asList(physicalCameraIdList));
+    }
+
     /*@O~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~
      * The key entries below this point are generated from metadata
      * definitions in /system/media/camera/docs. Do not modify by hand or
@@ -1579,6 +1662,7 @@
      *   <li>{@link #REQUEST_AVAILABLE_CAPABILITIES_DEPTH_OUTPUT DEPTH_OUTPUT}</li>
      *   <li>{@link #REQUEST_AVAILABLE_CAPABILITIES_CONSTRAINED_HIGH_SPEED_VIDEO CONSTRAINED_HIGH_SPEED_VIDEO}</li>
      *   <li>{@link #REQUEST_AVAILABLE_CAPABILITIES_MOTION_TRACKING MOTION_TRACKING}</li>
+     *   <li>{@link #REQUEST_AVAILABLE_CAPABILITIES_LOGICAL_MULTI_CAMERA LOGICAL_MULTI_CAMERA}</li>
      * </ul></p>
      * <p>This key is available on all devices.</p>
      *
@@ -1594,6 +1678,7 @@
      * @see #REQUEST_AVAILABLE_CAPABILITIES_DEPTH_OUTPUT
      * @see #REQUEST_AVAILABLE_CAPABILITIES_CONSTRAINED_HIGH_SPEED_VIDEO
      * @see #REQUEST_AVAILABLE_CAPABILITIES_MOTION_TRACKING
+     * @see #REQUEST_AVAILABLE_CAPABILITIES_LOGICAL_MULTI_CAMERA
      */
     @PublicKey
     public static final Key<int[]> REQUEST_AVAILABLE_CAPABILITIES =
@@ -1701,6 +1786,30 @@
             new Key<int[]>("android.request.availableSessionKeys", int[].class);
 
     /**
+     * <p>A subset of the available request keys that can be overriden for
+     * physical devices backing a logical multi-camera.</p>
+     * <p>This is a subset of android.request.availableRequestKeys which contains a list
+     * of keys that can be overriden using {@link CaptureRequest.Builder#setPhysicalCameraKey }.
+     * The respective value of such request key can be obtained by calling
+     * {@link CaptureRequest.Builder#getPhysicalCameraKey }. Capture requests that contain
+     * individual physical device requests must be built via
+     * {@link android.hardware.camera2.CameraDevice#createCaptureRequest(int, Set)}.
+     * Such extended capture requests can be passed only to
+     * {@link CameraCaptureSession#capture } or {@link CameraCaptureSession#captureBurst } and
+     * not to {@link CameraCaptureSession#setRepeatingRequest } or
+     * {@link CameraCaptureSession#setRepeatingBurst }.</p>
+     * <p><b>Optional</b> - This value may be {@code null} on some devices.</p>
+     * <p><b>Limited capability</b> -
+     * Present on all camera devices that report being at least {@link CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED HARDWARE_LEVEL_LIMITED} devices in the
+     * {@link CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL android.info.supportedHardwareLevel} key</p>
+     *
+     * @see CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL
+     * @hide
+     */
+    public static final Key<int[]> REQUEST_AVAILABLE_PHYSICAL_CAMERA_REQUEST_KEYS =
+            new Key<int[]>("android.request.availablePhysicalCameraRequestKeys", int[].class);
+
+    /**
      * <p>The list of image formats that are supported by this
      * camera device for output streams.</p>
      * <p>All camera devices will support JPEG and YUV_420_888 formats.</p>
@@ -2870,6 +2979,21 @@
             new Key<int[]>("android.statistics.info.availableLensShadingMapModes", int[].class);
 
     /**
+     * <p>List of OIS data output modes for {@link CaptureRequest#STATISTICS_OIS_DATA_MODE android.statistics.oisDataMode} that
+     * are supported by this camera device.</p>
+     * <p>If no OIS data output is available for this camera device, this key will
+     * contain only OFF.</p>
+     * <p><b>Range of valid values:</b><br>
+     * Any value listed in {@link CaptureRequest#STATISTICS_OIS_DATA_MODE android.statistics.oisDataMode}</p>
+     * <p><b>Optional</b> - This value may be {@code null} on some devices.</p>
+     *
+     * @see CaptureRequest#STATISTICS_OIS_DATA_MODE
+     */
+    @PublicKey
+    public static final Key<int[]> STATISTICS_INFO_AVAILABLE_OIS_DATA_MODES =
+            new Key<int[]>("android.statistics.info.availableOisDataModes", int[].class);
+
+    /**
      * <p>Maximum number of supported points in the
      * tonemap curve that can be used for {@link CaptureRequest#TONEMAP_CURVE android.tonemap.curve}.</p>
      * <p>If the actual number of points provided by the application (in {@link CaptureRequest#TONEMAP_CURVE android.tonemap.curve}*) is
@@ -2978,6 +3102,7 @@
      *   <li>{@link #INFO_SUPPORTED_HARDWARE_LEVEL_FULL FULL}</li>
      *   <li>{@link #INFO_SUPPORTED_HARDWARE_LEVEL_LEGACY LEGACY}</li>
      *   <li>{@link #INFO_SUPPORTED_HARDWARE_LEVEL_3 3}</li>
+     *   <li>{@link #INFO_SUPPORTED_HARDWARE_LEVEL_EXTERNAL EXTERNAL}</li>
      * </ul></p>
      * <p>This key is available on all devices.</p>
      *
@@ -2991,6 +3116,7 @@
      * @see #INFO_SUPPORTED_HARDWARE_LEVEL_FULL
      * @see #INFO_SUPPORTED_HARDWARE_LEVEL_LEGACY
      * @see #INFO_SUPPORTED_HARDWARE_LEVEL_3
+     * @see #INFO_SUPPORTED_HARDWARE_LEVEL_EXTERNAL
      */
     @PublicKey
     public static final Key<Integer> INFO_SUPPORTED_HARDWARE_LEVEL =
@@ -3167,6 +3293,54 @@
     public static final Key<Boolean> DEPTH_DEPTH_IS_EXCLUSIVE =
             new Key<Boolean>("android.depth.depthIsExclusive", boolean.class);
 
+    /**
+     * <p>String containing the ids of the underlying physical cameras.</p>
+     * <p>For a logical camera, this is concatenation of all underlying physical camera ids.
+     * The null terminator for physical camera id must be preserved so that the whole string
+     * can be tokenized using '\0' to generate list of physical camera ids.</p>
+     * <p>For example, if the physical camera ids of the logical camera are "2" and "3", the
+     * value of this tag will be ['2', '\0', '3', '\0'].</p>
+     * <p>The number of physical camera ids must be no less than 2.</p>
+     * <p><b>Units</b>: UTF-8 null-terminated string</p>
+     * <p><b>Optional</b> - This value may be {@code null} on some devices.</p>
+     * <p><b>Limited capability</b> -
+     * Present on all camera devices that report being at least {@link CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED HARDWARE_LEVEL_LIMITED} devices in the
+     * {@link CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL android.info.supportedHardwareLevel} key</p>
+     *
+     * @see CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL
+     * @hide
+     */
+    public static final Key<byte[]> LOGICAL_MULTI_CAMERA_PHYSICAL_IDS =
+            new Key<byte[]>("android.logicalMultiCamera.physicalIds", byte[].class);
+
+    /**
+     * <p>The accuracy of frame timestamp synchronization between physical cameras</p>
+     * <p>The accuracy of the frame timestamp synchronization determines the physical cameras'
+     * ability to start exposure at the same time. If the sensorSyncType is CALIBRATED,
+     * the physical camera sensors usually run in master-slave mode so that their shutter
+     * time is synchronized. For APPROXIMATE sensorSyncType, the camera sensors usually run in
+     * master-master mode, and there could be offset between their start of exposure.</p>
+     * <p>In both cases, all images generated for a particular capture request still carry the same
+     * timestamps, so that they can be used to look up the matching frame number and
+     * onCaptureStarted callback.</p>
+     * <p><b>Possible values:</b>
+     * <ul>
+     *   <li>{@link #LOGICAL_MULTI_CAMERA_SENSOR_SYNC_TYPE_APPROXIMATE APPROXIMATE}</li>
+     *   <li>{@link #LOGICAL_MULTI_CAMERA_SENSOR_SYNC_TYPE_CALIBRATED CALIBRATED}</li>
+     * </ul></p>
+     * <p><b>Optional</b> - This value may be {@code null} on some devices.</p>
+     * <p><b>Limited capability</b> -
+     * Present on all camera devices that report being at least {@link CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED HARDWARE_LEVEL_LIMITED} devices in the
+     * {@link CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL android.info.supportedHardwareLevel} key</p>
+     *
+     * @see CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL
+     * @see #LOGICAL_MULTI_CAMERA_SENSOR_SYNC_TYPE_APPROXIMATE
+     * @see #LOGICAL_MULTI_CAMERA_SENSOR_SYNC_TYPE_CALIBRATED
+     */
+    @PublicKey
+    public static final Key<Integer> LOGICAL_MULTI_CAMERA_SENSOR_SYNC_TYPE =
+            new Key<Integer>("android.logicalMultiCamera.sensorSyncType", int.class);
+
     /*~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~
      * End generated code
      *~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~O@*/
diff --git a/core/java/android/hardware/camera2/CameraDevice.java b/core/java/android/hardware/camera2/CameraDevice.java
index 639795a..40ee834 100644
--- a/core/java/android/hardware/camera2/CameraDevice.java
+++ b/core/java/android/hardware/camera2/CameraDevice.java
@@ -31,6 +31,7 @@
 import android.view.Surface;
 
 import java.util.List;
+import java.util.Set;
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 
@@ -910,6 +911,47 @@
             throws CameraAccessException;
 
     /**
+     * <p>Create a {@link CaptureRequest.Builder} for new capture requests,
+     * initialized with template for a target use case. This methods allows
+     * clients to pass physical camera ids which can be used to customize the
+     * request for a specific physical camera. The settings are chosen
+     * to be the best options for the specific logical camera device. If
+     * additional physical camera ids are passed, then they will also use the
+     * same settings template. Requests containing individual physical camera
+     * settings can be passed only to {@link CameraCaptureSession#capture} or
+     * {@link CameraCaptureSession#captureBurst} and not to
+     * {@link CameraCaptureSession#setRepeatingRequest} or
+     * {@link CameraCaptureSession#setRepeatingBurst}</p>
+     *
+     * @param templateType An enumeration selecting the use case for this request. Not all template
+     * types are supported on every device. See the documentation for each template type for
+     * details.
+     * @param physicalCameraIdSet A set of physical camera ids that can be used to customize
+     *                            the request for a specific physical camera.
+     * @return a builder for a capture request, initialized with default
+     * settings for that template, and no output streams
+     *
+     * @throws IllegalArgumentException if the templateType is not supported by
+     * this device, or one of the physical id arguments matches with logical camera id.
+     * @throws CameraAccessException if the camera device is no longer connected or has
+     *                               encountered a fatal error
+     * @throws IllegalStateException if the camera device has been closed
+     *
+     * @see #TEMPLATE_PREVIEW
+     * @see #TEMPLATE_RECORD
+     * @see #TEMPLATE_STILL_CAPTURE
+     * @see #TEMPLATE_VIDEO_SNAPSHOT
+     * @see #TEMPLATE_MANUAL
+     * @see CaptureRequest.Builder#setKey
+     * @see CaptureRequest.Builder#getKey
+     */
+    @NonNull
+    public CaptureRequest.Builder createCaptureRequest(@RequestTemplate int templateType,
+            Set<String> physicalCameraIdSet) throws CameraAccessException {
+        throw new UnsupportedOperationException("Subclasses must override this method");
+    }
+
+    /**
      * <p>Create a {@link CaptureRequest.Builder} for a new reprocess {@link CaptureRequest} from a
      * {@link TotalCaptureResult}.
      *
diff --git a/core/java/android/hardware/camera2/CameraManager.java b/core/java/android/hardware/camera2/CameraManager.java
index 90bf896..a2bc91e 100644
--- a/core/java/android/hardware/camera2/CameraManager.java
+++ b/core/java/android/hardware/camera2/CameraManager.java
@@ -996,7 +996,12 @@
                 return;
             }
 
-            Integer oldStatus = mDeviceStatus.put(id, status);
+            Integer oldStatus;
+            if (status == ICameraServiceListener.STATUS_NOT_PRESENT) {
+                oldStatus = mDeviceStatus.remove(id);
+            } else {
+                oldStatus = mDeviceStatus.put(id, status);
+            }
 
             if (oldStatus != null && oldStatus == status) {
                 if (DEBUG) {
diff --git a/core/java/android/hardware/camera2/CameraMetadata.java b/core/java/android/hardware/camera2/CameraMetadata.java
index 2294ec5..e7c8961 100644
--- a/core/java/android/hardware/camera2/CameraMetadata.java
+++ b/core/java/android/hardware/camera2/CameraMetadata.java
@@ -845,6 +845,53 @@
      */
     public static final int REQUEST_AVAILABLE_CAPABILITIES_MOTION_TRACKING = 10;
 
+    /**
+     * <p>The camera device is a logical camera backed by two or more physical cameras that are
+     * also exposed to the application.</p>
+     * <p>This capability requires the camera device to support the following:</p>
+     * <ul>
+     * <li>This camera device must list the following static metadata entries in {@link android.hardware.camera2.CameraCharacteristics }:<ul>
+     * <li>android.logicalMultiCamera.physicalIds</li>
+     * <li>{@link CameraCharacteristics#LOGICAL_MULTI_CAMERA_SENSOR_SYNC_TYPE android.logicalMultiCamera.sensorSyncType}</li>
+     * </ul>
+     * </li>
+     * <li>The underlying physical cameras' static metadata must list the following entries,
+     *   so that the application can correlate pixels from the physical streams:<ul>
+     * <li>{@link CameraCharacteristics#LENS_POSE_REFERENCE android.lens.poseReference}</li>
+     * <li>{@link CameraCharacteristics#LENS_POSE_ROTATION android.lens.poseRotation}</li>
+     * <li>{@link CameraCharacteristics#LENS_POSE_TRANSLATION android.lens.poseTranslation}</li>
+     * <li>{@link CameraCharacteristics#LENS_INTRINSIC_CALIBRATION android.lens.intrinsicCalibration}</li>
+     * <li>{@link CameraCharacteristics#LENS_RADIAL_DISTORTION android.lens.radialDistortion}</li>
+     * </ul>
+     * </li>
+     * <li>The logical camera device must be LIMITED or higher device.</li>
+     * </ul>
+     * <p>Both the logical camera device and its underlying physical devices support the
+     * mandatory stream combinations required for their device levels.</p>
+     * <p>Additionally, for each guaranteed stream combination, the logical camera supports:</p>
+     * <ul>
+     * <li>Replacing one logical {@link android.graphics.ImageFormat#YUV_420_888 YUV_420_888}
+     *   or raw stream with two physical streams of the same size and format, each from a
+     *   separate physical camera, given that the size and format are supported by both
+     *   physical cameras.</li>
+     * <li>Adding two raw streams, each from one physical camera, if the logical camera doesn't
+     *   advertise RAW capability, but the underlying physical cameras do. This is usually
+     *   the case when the physical cameras have different sensor sizes.</li>
+     * </ul>
+     * <p>Using physical streams in place of a logical stream of the same size and format will
+     * not slow down the frame rate of the capture, as long as the minimum frame duration
+     * of the physical and logical streams are the same.</p>
+     *
+     * @see CameraCharacteristics#LENS_INTRINSIC_CALIBRATION
+     * @see CameraCharacteristics#LENS_POSE_REFERENCE
+     * @see CameraCharacteristics#LENS_POSE_ROTATION
+     * @see CameraCharacteristics#LENS_POSE_TRANSLATION
+     * @see CameraCharacteristics#LENS_RADIAL_DISTORTION
+     * @see CameraCharacteristics#LOGICAL_MULTI_CAMERA_SENSOR_SYNC_TYPE
+     * @see CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES
+     */
+    public static final int REQUEST_AVAILABLE_CAPABILITIES_LOGICAL_MULTI_CAMERA = 11;
+
     //
     // Enumeration values for CameraCharacteristics#SCALER_CROPPING_TYPE
     //
@@ -1134,6 +1181,38 @@
      */
     public static final int INFO_SUPPORTED_HARDWARE_LEVEL_3 = 3;
 
+    /**
+     * <p>This camera device is backed by an external camera connected to this Android device.</p>
+     * <p>The device has capability identical to a LIMITED level device, with the following
+     * exceptions:</p>
+     * <ul>
+     * <li>The device may not report lens/sensor related information such as<ul>
+     * <li>{@link CaptureRequest#LENS_FOCAL_LENGTH android.lens.focalLength}</li>
+     * <li>{@link CameraCharacteristics#LENS_INFO_HYPERFOCAL_DISTANCE android.lens.info.hyperfocalDistance}</li>
+     * <li>{@link CameraCharacteristics#SENSOR_INFO_PHYSICAL_SIZE android.sensor.info.physicalSize}</li>
+     * <li>{@link CameraCharacteristics#SENSOR_INFO_WHITE_LEVEL android.sensor.info.whiteLevel}</li>
+     * <li>{@link CameraCharacteristics#SENSOR_BLACK_LEVEL_PATTERN android.sensor.blackLevelPattern}</li>
+     * <li>{@link CameraCharacteristics#SENSOR_INFO_COLOR_FILTER_ARRANGEMENT android.sensor.info.colorFilterArrangement}</li>
+     * <li>{@link CaptureResult#SENSOR_ROLLING_SHUTTER_SKEW android.sensor.rollingShutterSkew}</li>
+     * </ul>
+     * </li>
+     * <li>The device will report 0 for {@link CameraCharacteristics#SENSOR_ORIENTATION android.sensor.orientation}</li>
+     * <li>The device has less guarantee on stable framerate, as the framerate partly depends
+     *   on the external camera being used.</li>
+     * </ul>
+     *
+     * @see CaptureRequest#LENS_FOCAL_LENGTH
+     * @see CameraCharacteristics#LENS_INFO_HYPERFOCAL_DISTANCE
+     * @see CameraCharacteristics#SENSOR_BLACK_LEVEL_PATTERN
+     * @see CameraCharacteristics#SENSOR_INFO_COLOR_FILTER_ARRANGEMENT
+     * @see CameraCharacteristics#SENSOR_INFO_PHYSICAL_SIZE
+     * @see CameraCharacteristics#SENSOR_INFO_WHITE_LEVEL
+     * @see CameraCharacteristics#SENSOR_ORIENTATION
+     * @see CaptureResult#SENSOR_ROLLING_SHUTTER_SKEW
+     * @see CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL
+     */
+    public static final int INFO_SUPPORTED_HARDWARE_LEVEL_EXTERNAL = 4;
+
     //
     // Enumeration values for CameraCharacteristics#SYNC_MAX_LATENCY
     //
@@ -1160,6 +1239,26 @@
     public static final int SYNC_MAX_LATENCY_UNKNOWN = -1;
 
     //
+    // Enumeration values for CameraCharacteristics#LOGICAL_MULTI_CAMERA_SENSOR_SYNC_TYPE
+    //
+
+    /**
+     * <p>A software mechanism is used to synchronize between the physical cameras. As a result,
+     * the timestamp of an image from a physical stream is only an approximation of the
+     * image sensor start-of-exposure time.</p>
+     * @see CameraCharacteristics#LOGICAL_MULTI_CAMERA_SENSOR_SYNC_TYPE
+     */
+    public static final int LOGICAL_MULTI_CAMERA_SENSOR_SYNC_TYPE_APPROXIMATE = 0;
+
+    /**
+     * <p>The camera device supports frame timestamp synchronization at the hardware level,
+     * and the timestamp of a physical stream image accurately reflects its
+     * start-of-exposure time.</p>
+     * @see CameraCharacteristics#LOGICAL_MULTI_CAMERA_SENSOR_SYNC_TYPE
+     */
+    public static final int LOGICAL_MULTI_CAMERA_SENSOR_SYNC_TYPE_CALIBRATED = 1;
+
+    //
     // Enumeration values for CaptureRequest#COLOR_CORRECTION_MODE
     //
 
@@ -2566,6 +2665,22 @@
     public static final int STATISTICS_LENS_SHADING_MAP_MODE_ON = 1;
 
     //
+    // Enumeration values for CaptureRequest#STATISTICS_OIS_DATA_MODE
+    //
+
+    /**
+     * <p>Do not include OIS data in the capture result.</p>
+     * @see CaptureRequest#STATISTICS_OIS_DATA_MODE
+     */
+    public static final int STATISTICS_OIS_DATA_MODE_OFF = 0;
+
+    /**
+     * <p>Include OIS data in the capture result.</p>
+     * @see CaptureRequest#STATISTICS_OIS_DATA_MODE
+     */
+    public static final int STATISTICS_OIS_DATA_MODE_ON = 1;
+
+    //
     // Enumeration values for CaptureRequest#TONEMAP_MODE
     //
 
diff --git a/core/java/android/hardware/camera2/CaptureRequest.java b/core/java/android/hardware/camera2/CaptureRequest.java
index ce75fa52..481b764 100644
--- a/core/java/android/hardware/camera2/CaptureRequest.java
+++ b/core/java/android/hardware/camera2/CaptureRequest.java
@@ -33,9 +33,11 @@
 
 import java.util.Collection;
 import java.util.Collections;
+import java.util.HashMap;
 import java.util.List;
+import java.util.Map;
 import java.util.Objects;
-
+import java.util.Set;
 
 /**
  * <p>An immutable package of settings and outputs needed to capture a single
@@ -219,7 +221,11 @@
 
     private static final ArraySet<Surface> mEmptySurfaceSet = new ArraySet<Surface>();
 
-    private final CameraMetadataNative mSettings;
+    private String mLogicalCameraId;
+    private CameraMetadataNative mLogicalCameraSettings;
+    private final HashMap<String, CameraMetadataNative> mPhysicalCameraSettings =
+            new HashMap<String, CameraMetadataNative>();
+
     private boolean mIsReprocess;
     // If this request is part of constrained high speed request list that was created by
     // {@link android.hardware.camera2.CameraConstrainedHighSpeedCaptureSession#createHighSpeedRequestList}
@@ -236,8 +242,6 @@
      * Used by Binder to unparcel this object only.
      */
     private CaptureRequest() {
-        mSettings = new CameraMetadataNative();
-        setNativeInstance(mSettings);
         mIsReprocess = false;
         mReprocessableSessionId = CameraCaptureSession.SESSION_ID_NONE;
     }
@@ -249,8 +253,14 @@
      */
     @SuppressWarnings("unchecked")
     private CaptureRequest(CaptureRequest source) {
-        mSettings = new CameraMetadataNative(source.mSettings);
-        setNativeInstance(mSettings);
+        mLogicalCameraId = new String(source.mLogicalCameraId);
+        for (Map.Entry<String, CameraMetadataNative> entry :
+                source.mPhysicalCameraSettings.entrySet()) {
+            mPhysicalCameraSettings.put(new String(entry.getKey()),
+                    new CameraMetadataNative(entry.getValue()));
+        }
+        mLogicalCameraSettings = mPhysicalCameraSettings.get(mLogicalCameraId);
+        setNativeInstance(mLogicalCameraSettings);
         mSurfaceSet.addAll(source.mSurfaceSet);
         mIsReprocess = source.mIsReprocess;
         mIsPartOfCHSRequestList = source.mIsPartOfCHSRequestList;
@@ -272,16 +282,35 @@
      *                               reprocess capture request to the same session where
      *                               the {@link TotalCaptureResult}, used to create the reprocess
      *                               capture, came from.
+     * @param logicalCameraId Camera Id of the actively open camera that instantiates the
+     *                        Builder.
+     *
+     * @param physicalCameraIdSet A set of physical camera ids that can be used to customize
+     *                            the request for a specific physical camera.
      *
      * @throws IllegalArgumentException If creating a reprocess capture request with an invalid
-     *                                  reprocessableSessionId.
+     *                                  reprocessableSessionId, or multiple physical cameras.
      *
      * @see CameraDevice#createReprocessCaptureRequest
      */
     private CaptureRequest(CameraMetadataNative settings, boolean isReprocess,
-            int reprocessableSessionId) {
-        mSettings = CameraMetadataNative.move(settings);
-        setNativeInstance(mSettings);
+            int reprocessableSessionId, String logicalCameraId, Set<String> physicalCameraIdSet) {
+        if ((physicalCameraIdSet != null) && isReprocess) {
+            throw new IllegalArgumentException("Create a reprocess capture request with " +
+                    "with more than one physical camera is not supported!");
+        }
+
+        mLogicalCameraId = logicalCameraId;
+        mLogicalCameraSettings = CameraMetadataNative.move(settings);
+        mPhysicalCameraSettings.put(mLogicalCameraId, mLogicalCameraSettings);
+        if (physicalCameraIdSet != null) {
+            for (String physicalId : physicalCameraIdSet) {
+                mPhysicalCameraSettings.put(physicalId, new CameraMetadataNative(
+                            mLogicalCameraSettings));
+            }
+        }
+
+        setNativeInstance(mLogicalCameraSettings);
         mIsReprocess = isReprocess;
         if (isReprocess) {
             if (reprocessableSessionId == CameraCaptureSession.SESSION_ID_NONE) {
@@ -309,7 +338,7 @@
      */
     @Nullable
     public <T> T get(Key<T> key) {
-        return mSettings.get(key);
+        return mLogicalCameraSettings.get(key);
     }
 
     /**
@@ -319,7 +348,7 @@
     @SuppressWarnings("unchecked")
     @Override
     protected <T> T getProtected(Key<?> key) {
-        return (T) mSettings.get(key);
+        return (T) mLogicalCameraSettings.get(key);
     }
 
     /**
@@ -403,7 +432,7 @@
      * @hide
      */
     public CameraMetadataNative getNativeCopy() {
-        return new CameraMetadataNative(mSettings);
+        return new CameraMetadataNative(mLogicalCameraSettings);
     }
 
     /**
@@ -444,14 +473,16 @@
         return other != null
                 && Objects.equals(mUserTag, other.mUserTag)
                 && mSurfaceSet.equals(other.mSurfaceSet)
-                && mSettings.equals(other.mSettings)
+                && mPhysicalCameraSettings.equals(other.mPhysicalCameraSettings)
+                && mLogicalCameraId.equals(other.mLogicalCameraId)
+                && mLogicalCameraSettings.equals(other.mLogicalCameraSettings)
                 && mIsReprocess == other.mIsReprocess
                 && mReprocessableSessionId == other.mReprocessableSessionId;
     }
 
     @Override
     public int hashCode() {
-        return HashCodeHelpers.hashCodeGeneric(mSettings, mSurfaceSet, mUserTag);
+        return HashCodeHelpers.hashCodeGeneric(mPhysicalCameraSettings, mSurfaceSet, mUserTag);
     }
 
     public static final Parcelable.Creator<CaptureRequest> CREATOR =
@@ -479,8 +510,25 @@
      * @hide
      */
     private void readFromParcel(Parcel in) {
-        mSettings.readFromParcel(in);
-        setNativeInstance(mSettings);
+        int physicalCameraCount = in.readInt();
+        if (physicalCameraCount <= 0) {
+            throw new RuntimeException("Physical camera count" + physicalCameraCount +
+                    " should always be positive");
+        }
+
+        //Always start with the logical camera id
+        mLogicalCameraId = in.readString();
+        mLogicalCameraSettings = new CameraMetadataNative();
+        mLogicalCameraSettings.readFromParcel(in);
+        setNativeInstance(mLogicalCameraSettings);
+        mPhysicalCameraSettings.put(mLogicalCameraId, mLogicalCameraSettings);
+        for (int i = 1; i < physicalCameraCount; i++) {
+            String physicalId = in.readString();
+            CameraMetadataNative physicalCameraSettings = new CameraMetadataNative();
+            physicalCameraSettings.readFromParcel(in);
+            mPhysicalCameraSettings.put(physicalId, physicalCameraSettings);
+        }
+
         mIsReprocess = (in.readInt() == 0) ? false : true;
         mReprocessableSessionId = CameraCaptureSession.SESSION_ID_NONE;
 
@@ -509,7 +557,19 @@
 
     @Override
     public void writeToParcel(Parcel dest, int flags) {
-        mSettings.writeToParcel(dest, flags);
+        int physicalCameraCount = mPhysicalCameraSettings.size();
+        dest.writeInt(physicalCameraCount);
+        //Logical camera id and settings always come first.
+        dest.writeString(mLogicalCameraId);
+        mLogicalCameraSettings.writeToParcel(dest, flags);
+        for (Map.Entry<String, CameraMetadataNative> entry : mPhysicalCameraSettings.entrySet()) {
+            if (entry.getKey().equals(mLogicalCameraId)) {
+                continue;
+            }
+            dest.writeString(entry.getKey());
+            entry.getValue().writeToParcel(dest, flags);
+        }
+
         dest.writeInt(mIsReprocess ? 1 : 0);
 
         synchronized (mSurfacesLock) {
@@ -542,6 +602,14 @@
     }
 
     /**
+     * Retrieves the logical camera id.
+     * @hide
+     */
+    public String getLogicalCameraId() {
+        return mLogicalCameraId;
+    }
+
+    /**
      * @hide
      */
     public void convertSurfaceToStreamId(
@@ -633,14 +701,20 @@
          *                               submits a reprocess capture request to the same session
          *                               where the {@link TotalCaptureResult}, used to create the
          *                               reprocess capture, came from.
+         * @param logicalCameraId Camera Id of the actively open camera that instantiates the
+         *                        Builder.
+         * @param physicalCameraIdSet A set of physical camera ids that can be used to customize
+         *                            the request for a specific physical camera.
          *
          * @throws IllegalArgumentException If creating a reprocess capture request with an invalid
          *                                  reprocessableSessionId.
          * @hide
          */
         public Builder(CameraMetadataNative template, boolean reprocess,
-                int reprocessableSessionId) {
-            mRequest = new CaptureRequest(template, reprocess, reprocessableSessionId);
+                int reprocessableSessionId, String logicalCameraId,
+                Set<String> physicalCameraIdSet) {
+            mRequest = new CaptureRequest(template, reprocess, reprocessableSessionId,
+                    logicalCameraId, physicalCameraIdSet);
         }
 
         /**
@@ -682,7 +756,7 @@
          * type to the key.
          */
         public <T> void set(@NonNull Key<T> key, T value) {
-            mRequest.mSettings.set(key, value);
+            mRequest.mLogicalCameraSettings.set(key, value);
         }
 
         /**
@@ -696,7 +770,71 @@
          */
         @Nullable
         public <T> T get(Key<T> key) {
-            return mRequest.mSettings.get(key);
+            return mRequest.mLogicalCameraSettings.get(key);
+        }
+
+        /**
+         * Set a capture request field to a value. The field definitions can be
+         * found in {@link CaptureRequest}.
+         *
+         * <p>Setting a field to {@code null} will remove that field from the capture request.
+         * Unless the field is optional, removing it will likely produce an error from the camera
+         * device when the request is submitted.</p>
+         *
+         *<p>This method can be called for logical camera devices, which are devices that have
+         * REQUEST_AVAILABLE_CAPABILITIES_LOGICAL_MULTI_CAMERA capability and calls to
+         * {@link CameraCharacteristics#getPhysicalCameraIds} return a non-empty list of
+         * physical devices that are backing the logical camera. The camera Id included in the
+         * 'physicalCameraId' argument selects an individual physical device that will receive
+         * the customized capture request field.</p>
+         *
+         * @throws IllegalArgumentException if the physical camera id is not valid
+         *
+         * @param key The metadata field to write.
+         * @param value The value to set the field to, which must be of a matching
+         * @param physicalCameraId A valid physical camera Id. The valid camera Ids can be obtained
+         *                         via calls to {@link CameraCharacteristics#getPhysicalCameraIds}.
+         * @return The builder object.
+         * type to the key.
+         */
+        public <T> Builder setPhysicalCameraKey(@NonNull Key<T> key, T value,
+                @NonNull String physicalCameraId) {
+            if (!mRequest.mPhysicalCameraSettings.containsKey(physicalCameraId)) {
+                throw new IllegalArgumentException("Physical camera id: " + physicalCameraId +
+                        " is not valid!");
+            }
+
+            mRequest.mPhysicalCameraSettings.get(physicalCameraId).set(key, value);
+
+            return this;
+        }
+
+        /**
+         * Get a capture request field value for a specific physical camera Id. The field
+         * definitions can be found in {@link CaptureRequest}.
+         *
+         *<p>This method can be called for logical camera devices, which are devices that have
+         * REQUEST_AVAILABLE_CAPABILITIES_LOGICAL_MULTI_CAMERA capability and calls to
+         * {@link CameraCharacteristics#getPhysicalCameraIds} return a non-empty list of
+         * physical devices that are backing the logical camera. The camera Id included in the
+         * 'physicalCameraId' argument selects an individual physical device and returns
+         * its specific capture request field.</p>
+         *
+         * @throws IllegalArgumentException if the key or physical camera id were not valid
+         *
+         * @param key The metadata field to read.
+         * @param physicalCameraId A valid physical camera Id. The valid camera Ids can be obtained
+         *                         via calls to {@link CameraCharacteristics#getPhysicalCameraIds}.
+         * @return The value of that key, or {@code null} if the field is not set.
+         */
+        @Nullable
+        public <T> T getPhysicalCameraKey(Key<T> key,@NonNull String physicalCameraId) {
+            if (!mRequest.mPhysicalCameraSettings.containsKey(physicalCameraId)) {
+                throw new IllegalArgumentException("Physical camera id: " + physicalCameraId +
+                        " is not valid!");
+            }
+
+            return mRequest.mPhysicalCameraSettings.get(physicalCameraId).get(key);
         }
 
         /**
@@ -748,7 +886,7 @@
          * @hide
          */
         public boolean isEmpty() {
-            return mRequest.mSettings.isEmpty();
+            return mRequest.mLogicalCameraSettings.isEmpty();
         }
 
     }
@@ -2619,6 +2757,29 @@
             new Key<Integer>("android.statistics.lensShadingMapMode", int.class);
 
     /**
+     * <p>Whether the camera device outputs the OIS data in output
+     * result metadata.</p>
+     * <p>When set to ON,
+     * {@link CaptureResult#STATISTICS_OIS_TIMESTAMPS android.statistics.oisTimestamps}, android.statistics.oisShiftPixelX,
+     * android.statistics.oisShiftPixelY will provide OIS data in the output result metadata.</p>
+     * <p><b>Possible values:</b>
+     * <ul>
+     *   <li>{@link #STATISTICS_OIS_DATA_MODE_OFF OFF}</li>
+     *   <li>{@link #STATISTICS_OIS_DATA_MODE_ON ON}</li>
+     * </ul></p>
+     * <p><b>Available values for this device:</b><br>
+     * android.Statistics.info.availableOisDataModes</p>
+     * <p><b>Optional</b> - This value may be {@code null} on some devices.</p>
+     *
+     * @see CaptureResult#STATISTICS_OIS_TIMESTAMPS
+     * @see #STATISTICS_OIS_DATA_MODE_OFF
+     * @see #STATISTICS_OIS_DATA_MODE_ON
+     */
+    @PublicKey
+    public static final Key<Integer> STATISTICS_OIS_DATA_MODE =
+            new Key<Integer>("android.statistics.oisDataMode", int.class);
+
+    /**
      * <p>Tonemapping / contrast / gamma curve for the blue
      * channel, to use when {@link CaptureRequest#TONEMAP_MODE android.tonemap.mode} is
      * CONTRAST_CURVE.</p>
diff --git a/core/java/android/hardware/camera2/CaptureResult.java b/core/java/android/hardware/camera2/CaptureResult.java
index 237a92d..d730fa8 100644
--- a/core/java/android/hardware/camera2/CaptureResult.java
+++ b/core/java/android/hardware/camera2/CaptureResult.java
@@ -2203,8 +2203,6 @@
      * significant illumination change, this value will be set to DETECTED for a single capture
      * result. Otherwise the value will be NOT_DETECTED. The threshold for detection is similar
      * to what would trigger a new passive focus scan to begin in CONTINUOUS autofocus modes.</p>
-     * <p>afSceneChange may be DETECTED only if afMode is AF_MODE_CONTINUOUS_VIDEO or
-     * AF_MODE_CONTINUOUS_PICTURE. In other AF modes, afSceneChange must be NOT_DETECTED.</p>
      * <p>This key will be available if the camera device advertises this key via {@link android.hardware.camera2.CameraCharacteristics#getAvailableCaptureResultKeys }.</p>
      * <p><b>Possible values:</b>
      * <ul>
@@ -3911,6 +3909,76 @@
             new Key<Integer>("android.statistics.lensShadingMapMode", int.class);
 
     /**
+     * <p>Whether the camera device outputs the OIS data in output
+     * result metadata.</p>
+     * <p>When set to ON,
+     * {@link CaptureResult#STATISTICS_OIS_TIMESTAMPS android.statistics.oisTimestamps}, android.statistics.oisShiftPixelX,
+     * android.statistics.oisShiftPixelY will provide OIS data in the output result metadata.</p>
+     * <p><b>Possible values:</b>
+     * <ul>
+     *   <li>{@link #STATISTICS_OIS_DATA_MODE_OFF OFF}</li>
+     *   <li>{@link #STATISTICS_OIS_DATA_MODE_ON ON}</li>
+     * </ul></p>
+     * <p><b>Available values for this device:</b><br>
+     * android.Statistics.info.availableOisDataModes</p>
+     * <p><b>Optional</b> - This value may be {@code null} on some devices.</p>
+     *
+     * @see CaptureResult#STATISTICS_OIS_TIMESTAMPS
+     * @see #STATISTICS_OIS_DATA_MODE_OFF
+     * @see #STATISTICS_OIS_DATA_MODE_ON
+     */
+    @PublicKey
+    public static final Key<Integer> STATISTICS_OIS_DATA_MODE =
+            new Key<Integer>("android.statistics.oisDataMode", int.class);
+
+    /**
+     * <p>An array of timestamps of OIS samples, in nanoseconds.</p>
+     * <p>The array contains the timestamps of OIS samples. The timestamps are in the same
+     * timebase as and comparable to {@link CaptureResult#SENSOR_TIMESTAMP android.sensor.timestamp}.</p>
+     * <p><b>Units</b>: nanoseconds</p>
+     * <p><b>Optional</b> - This value may be {@code null} on some devices.</p>
+     *
+     * @see CaptureResult#SENSOR_TIMESTAMP
+     */
+    @PublicKey
+    public static final Key<long[]> STATISTICS_OIS_TIMESTAMPS =
+            new Key<long[]>("android.statistics.oisTimestamps", long[].class);
+
+    /**
+     * <p>An array of shifts of OIS samples, in x direction.</p>
+     * <p>The array contains the amount of shifts in x direction, in pixels, based on OIS samples.
+     * A positive value is a shift from left to right in active array coordinate system. For
+     * example, if the optical center is (1000, 500) in active array coordinates, an shift of
+     * (3, 0) puts the new optical center at (1003, 500).</p>
+     * <p>The number of shifts must match the number of timestamps in
+     * {@link CaptureResult#STATISTICS_OIS_TIMESTAMPS android.statistics.oisTimestamps}.</p>
+     * <p><b>Units</b>: Pixels in active array.</p>
+     * <p><b>Optional</b> - This value may be {@code null} on some devices.</p>
+     *
+     * @see CaptureResult#STATISTICS_OIS_TIMESTAMPS
+     */
+    @PublicKey
+    public static final Key<float[]> STATISTICS_OIS_X_SHIFTS =
+            new Key<float[]>("android.statistics.oisXShifts", float[].class);
+
+    /**
+     * <p>An array of shifts of OIS samples, in y direction.</p>
+     * <p>The array contains the amount of shifts in y direction, in pixels, based on OIS samples.
+     * A positive value is a shift from top to bottom in active array coordinate system. For
+     * example, if the optical center is (1000, 500) in active array coordinates, an shift of
+     * (0, 5) puts the new optical center at (1000, 505).</p>
+     * <p>The number of shifts must match the number of timestamps in
+     * {@link CaptureResult#STATISTICS_OIS_TIMESTAMPS android.statistics.oisTimestamps}.</p>
+     * <p><b>Units</b>: Pixels in active array.</p>
+     * <p><b>Optional</b> - This value may be {@code null} on some devices.</p>
+     *
+     * @see CaptureResult#STATISTICS_OIS_TIMESTAMPS
+     */
+    @PublicKey
+    public static final Key<float[]> STATISTICS_OIS_Y_SHIFTS =
+            new Key<float[]>("android.statistics.oisYShifts", float[].class);
+
+    /**
      * <p>Tonemapping / contrast / gamma curve for the blue
      * channel, to use when {@link CaptureRequest#TONEMAP_MODE android.tonemap.mode} is
      * CONTRAST_CURVE.</p>
diff --git a/core/java/android/hardware/camera2/impl/CameraConstrainedHighSpeedCaptureSessionImpl.java b/core/java/android/hardware/camera2/impl/CameraConstrainedHighSpeedCaptureSessionImpl.java
index 8c4dbfa..06c2c25 100644
--- a/core/java/android/hardware/camera2/impl/CameraConstrainedHighSpeedCaptureSessionImpl.java
+++ b/core/java/android/hardware/camera2/impl/CameraConstrainedHighSpeedCaptureSessionImpl.java
@@ -94,8 +94,8 @@
         // Note that after this step, the requestMetadata is mutated (swapped) and can not be used
         // for next request builder creation.
         CaptureRequest.Builder singleTargetRequestBuilder = new CaptureRequest.Builder(
-                requestMetadata, /*reprocess*/false, CameraCaptureSession.SESSION_ID_NONE);
-
+                requestMetadata, /*reprocess*/false, CameraCaptureSession.SESSION_ID_NONE,
+                request.getLogicalCameraId(), /*physicalCameraIdSet*/ null);
         // Carry over userTag, as native metadata doesn't have this field.
         singleTargetRequestBuilder.setTag(request.getTag());
 
@@ -120,7 +120,8 @@
             // CaptureRequest.Builder creation.
             requestMetadata = new CameraMetadataNative(request.getNativeCopy());
             doubleTargetRequestBuilder = new CaptureRequest.Builder(
-                    requestMetadata, /*reprocess*/false, CameraCaptureSession.SESSION_ID_NONE);
+                    requestMetadata, /*reprocess*/false, CameraCaptureSession.SESSION_ID_NONE,
+                    request.getLogicalCameraId(), /*physicalCameraIdSet*/null);
             doubleTargetRequestBuilder.setTag(request.getTag());
             doubleTargetRequestBuilder.set(CaptureRequest.CONTROL_CAPTURE_INTENT,
                     CaptureRequest.CONTROL_CAPTURE_INTENT_VIDEO_RECORD);
diff --git a/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java b/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
index f1ffb89..cab9d70 100644
--- a/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
+++ b/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
@@ -16,27 +16,25 @@
 
 package android.hardware.camera2.impl;
 
-import static android.hardware.camera2.CameraAccessException.CAMERA_IN_USE;
+import static com.android.internal.util.function.pooled.PooledLambda.obtainRunnable;
 
-import android.graphics.ImageFormat;
+import android.hardware.ICameraService;
 import android.hardware.camera2.CameraAccessException;
 import android.hardware.camera2.CameraCaptureSession;
 import android.hardware.camera2.CameraCharacteristics;
 import android.hardware.camera2.CameraDevice;
+import android.hardware.camera2.CaptureFailure;
 import android.hardware.camera2.CaptureRequest;
 import android.hardware.camera2.CaptureResult;
-import android.hardware.camera2.CaptureFailure;
 import android.hardware.camera2.ICameraDeviceCallbacks;
 import android.hardware.camera2.ICameraDeviceUser;
 import android.hardware.camera2.TotalCaptureResult;
 import android.hardware.camera2.params.InputConfiguration;
 import android.hardware.camera2.params.OutputConfiguration;
-import android.hardware.camera2.params.ReprocessFormatsMap;
 import android.hardware.camera2.params.SessionConfiguration;
 import android.hardware.camera2.params.StreamConfigurationMap;
 import android.hardware.camera2.utils.SubmitInfo;
 import android.hardware.camera2.utils.SurfaceUtils;
-import android.hardware.ICameraService;
 import android.os.Build;
 import android.os.Handler;
 import android.os.IBinder;
@@ -51,16 +49,15 @@
 
 import java.util.AbstractMap.SimpleEntry;
 import java.util.ArrayList;
-import java.util.Arrays;
 import java.util.Collection;
-import java.util.Collections;
-import java.util.concurrent.atomic.AtomicBoolean;
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.Iterator;
-import java.util.List;
 import java.util.LinkedList;
+import java.util.List;
+import java.util.Set;
 import java.util.TreeMap;
+import java.util.concurrent.atomic.AtomicBoolean;
 
 /**
  * HAL2.1+ implementation of CameraDevice. Use CameraManager#open to instantiate
@@ -715,6 +712,38 @@
     }
 
     @Override
+    public CaptureRequest.Builder createCaptureRequest(int templateType,
+            Set<String> physicalCameraIdSet)
+            throws CameraAccessException {
+        synchronized(mInterfaceLock) {
+            checkIfCameraClosedOrInError();
+
+            for (String physicalId : physicalCameraIdSet) {
+                if (physicalId == getId()) {
+                    throw new IllegalStateException("Physical id matches the logical id!");
+                }
+            }
+
+            CameraMetadataNative templatedRequest = null;
+
+            templatedRequest = mRemoteDevice.createDefaultRequest(templateType);
+
+            // If app target SDK is older than O, or it's not a still capture template, enableZsl
+            // must be false in the default request.
+            if (mAppTargetSdkVersion < Build.VERSION_CODES.O ||
+                    templateType != TEMPLATE_STILL_CAPTURE) {
+                overrideEnableZsl(templatedRequest, false);
+            }
+
+            CaptureRequest.Builder builder = new CaptureRequest.Builder(
+                    templatedRequest, /*reprocess*/false, CameraCaptureSession.SESSION_ID_NONE,
+                    getId(), physicalCameraIdSet);
+
+            return builder;
+        }
+    }
+
+    @Override
     public CaptureRequest.Builder createCaptureRequest(int templateType)
             throws CameraAccessException {
         synchronized(mInterfaceLock) {
@@ -732,7 +761,8 @@
             }
 
             CaptureRequest.Builder builder = new CaptureRequest.Builder(
-                    templatedRequest, /*reprocess*/false, CameraCaptureSession.SESSION_ID_NONE);
+                    templatedRequest, /*reprocess*/false, CameraCaptureSession.SESSION_ID_NONE,
+                    getId(), /*physicalCameraIdSet*/ null);
 
             return builder;
         }
@@ -748,7 +778,7 @@
                     CameraMetadataNative(inputResult.getNativeCopy());
 
             return new CaptureRequest.Builder(resultMetadata, /*reprocess*/true,
-                    inputResult.getSessionId());
+                    inputResult.getSessionId(), getId(), /*physicalCameraIdSet*/ null);
         }
     }
 
@@ -958,7 +988,8 @@
         // callback is valid
         handler = checkHandler(handler, callback);
 
-        // Make sure that there all requests have at least 1 surface; all surfaces are non-null
+        // Make sure that there all requests have at least 1 surface; all surfaces are non-null;
+        // the surface isn't a physical stream surface for reprocessing request
         for (CaptureRequest request : requestList) {
             if (request.getTargets().isEmpty()) {
                 throw new IllegalArgumentException(
@@ -969,7 +1000,20 @@
                 if (surface == null) {
                     throw new IllegalArgumentException("Null Surface targets are not allowed");
                 }
+
+                if (!request.isReprocess()) {
+                    continue;
+                }
+                for (int i = 0; i < mConfiguredOutputs.size(); i++) {
+                    OutputConfiguration configuration = mConfiguredOutputs.valueAt(i);
+                    if (configuration.isForPhysicalCamera()
+                            && configuration.getSurfaces().contains(surface)) {
+                        throw new IllegalArgumentException(
+                                "Reprocess request on physical stream is not allowed");
+                    }
+                }
             }
+
         }
 
         synchronized(mInterfaceLock) {
@@ -1798,34 +1842,36 @@
                     case ERROR_CAMERA_DISCONNECTED:
                         CameraDeviceImpl.this.mDeviceHandler.post(mCallOnDisconnected);
                         break;
-                    default:
-                        Log.e(TAG, "Unknown error from camera device: " + errorCode);
-                        // no break
-                    case ERROR_CAMERA_DEVICE:
-                    case ERROR_CAMERA_SERVICE:
-                        mInError = true;
-                        final int publicErrorCode = (errorCode == ERROR_CAMERA_DEVICE) ?
-                                StateCallback.ERROR_CAMERA_DEVICE :
-                                StateCallback.ERROR_CAMERA_SERVICE;
-                        Runnable r = new Runnable() {
-                            @Override
-                            public void run() {
-                                if (!CameraDeviceImpl.this.isClosed()) {
-                                    mDeviceCallback.onError(CameraDeviceImpl.this, publicErrorCode);
-                                }
-                            }
-                        };
-                        CameraDeviceImpl.this.mDeviceHandler.post(r);
-                        break;
                     case ERROR_CAMERA_REQUEST:
                     case ERROR_CAMERA_RESULT:
                     case ERROR_CAMERA_BUFFER:
                         onCaptureErrorLocked(errorCode, resultExtras);
                         break;
+                    case ERROR_CAMERA_DEVICE:
+                        scheduleNotifyError(StateCallback.ERROR_CAMERA_DEVICE);
+                        break;
+                    case ERROR_CAMERA_DISABLED:
+                        scheduleNotifyError(StateCallback.ERROR_CAMERA_DISABLED);
+                        break;
+                    default:
+                        Log.e(TAG, "Unknown error from camera device: " + errorCode);
+                        scheduleNotifyError(StateCallback.ERROR_CAMERA_SERVICE);
                 }
             }
         }
 
+        private void scheduleNotifyError(int code) {
+            mInError = true;
+            CameraDeviceImpl.this.mDeviceHandler.post(obtainRunnable(
+                    CameraDeviceCallbacks::notifyError, this, code));
+        }
+
+        private void notifyError(int code) {
+            if (!CameraDeviceImpl.this.isClosed()) {
+                mDeviceCallback.onError(CameraDeviceImpl.this, code);
+            }
+        }
+
         @Override
         public void onRepeatingRequestError(long lastFrameNumber, int repeatingRequestId) {
             if (DEBUG) {
diff --git a/core/java/android/hardware/camera2/params/OutputConfiguration.java b/core/java/android/hardware/camera2/params/OutputConfiguration.java
index a85b5f7..f47cd66 100644
--- a/core/java/android/hardware/camera2/params/OutputConfiguration.java
+++ b/core/java/android/hardware/camera2/params/OutputConfiguration.java
@@ -31,13 +31,12 @@
 import android.util.Size;
 import android.view.Surface;
 
-import java.util.Arrays;
-import java.util.List;
-import java.util.Collections;
-import java.util.ArrayList;
-
 import static com.android.internal.util.Preconditions.*;
 
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
 /**
  * A class for describing camera output, which contains a {@link Surface} and its specific
  * configuration for creating capture session.
@@ -266,6 +265,7 @@
         mConfiguredGenerationId = surface.getGenerationId();
         mIsDeferredConfig = false;
         mIsShared = false;
+        mPhysicalCameraId = null;
     }
 
     /**
@@ -319,6 +319,7 @@
         mConfiguredGenerationId = 0;
         mIsDeferredConfig = true;
         mIsShared = false;
+        mPhysicalCameraId = null;
     }
 
     /**
@@ -348,8 +349,9 @@
      * </ol>
      *
      * <p>To enable surface sharing, this function must be called before {@link
-     * CameraDevice#createCaptureSessionByOutputConfigurations}. Calling this function after {@link
-     * CameraDevice#createCaptureSessionByOutputConfigurations} has no effect.</p>
+     * CameraDevice#createCaptureSessionByOutputConfigurations} or {@link
+     * CameraDevice#createReprocessableCaptureSessionByConfigurations}. Calling this function after
+     * {@link CameraDevice#createCaptureSessionByOutputConfigurations} has no effect.</p>
      *
      * <p>Up to {@link #getMaxSharedSurfaceCount} surfaces can be shared for an OutputConfiguration.
      * The supported surfaces for sharing must be of type SurfaceTexture, SurfaceView,
@@ -360,6 +362,44 @@
     }
 
     /**
+     * Set the id of the physical camera for this OutputConfiguration
+     *
+     * <p>In the case one logical camera is made up of multiple physical cameras, it could be
+     * desirable for the camera application to request streams from individual physical cameras.
+     * This call achieves it by mapping the OutputConfiguration to the physical camera id.</p>
+     *
+     * <p>The valid physical camera id can be queried by {@link
+     * android.hardware.camera2.CameraCharacteristics#getPhysicalCameraIds}.
+     * </p>
+     *
+     * <p>Passing in a null physicalCameraId means that the OutputConfiguration is for a logical
+     * stream.</p>
+     *
+     * <p>This function must be called before {@link
+     * CameraDevice#createCaptureSessionByOutputConfigurations} or {@link
+     * CameraDevice#createReprocessableCaptureSessionByConfigurations}. Calling this function
+     * after {@link CameraDevice#createCaptureSessionByOutputConfigurations} or {@link
+     * CameraDevice#createReprocessableCaptureSessionByConfigurations} has no effect.</p>
+     *
+     * <p>The surface belonging to a physical camera OutputConfiguration must not be used as input
+     * or output of a reprocessing request. </p>
+     */
+    public void setPhysicalCameraId(@Nullable String physicalCameraId) {
+        mPhysicalCameraId = physicalCameraId;
+    }
+
+    /**
+     * Check if this configuration is for a physical camera.
+     *
+     * <p>This returns true if the output configuration was for a physical camera making up a
+     * logical multi camera via {@link OutputConfiguration#setPhysicalCameraId}.</p>
+     * @hide
+     */
+    public boolean isForPhysicalCamera() {
+        return (mPhysicalCameraId != null);
+    }
+
+    /**
      * Check if this configuration has deferred configuration.
      *
      * <p>This will return true if the output configuration was constructed with surface deferred by
@@ -487,6 +527,7 @@
         this.mConfiguredGenerationId = other.mConfiguredGenerationId;
         this.mIsDeferredConfig = other.mIsDeferredConfig;
         this.mIsShared = other.mIsShared;
+        this.mPhysicalCameraId = other.mPhysicalCameraId;
     }
 
     /**
@@ -502,6 +543,7 @@
         boolean isShared = source.readInt() == 1;
         ArrayList<Surface> surfaces = new ArrayList<Surface>();
         source.readTypedList(surfaces, Surface.CREATOR);
+        String physicalCameraId = source.readString();
 
         checkArgumentInRange(rotation, ROTATION_0, ROTATION_270, "Rotation constant");
 
@@ -524,6 +566,7 @@
                     StreamConfigurationMap.imageFormatToDataspace(ImageFormat.PRIVATE);
             mConfiguredGenerationId = 0;
         }
+        mPhysicalCameraId = physicalCameraId;
     }
 
     /**
@@ -622,6 +665,7 @@
         dest.writeInt(mIsDeferredConfig ? 1 : 0);
         dest.writeInt(mIsShared ? 1 : 0);
         dest.writeTypedList(mSurfaces);
+        dest.writeString(mPhysicalCameraId);
     }
 
     /**
@@ -675,13 +719,15 @@
         if (mIsDeferredConfig) {
             return HashCodeHelpers.hashCode(
                     mRotation, mConfiguredSize.hashCode(), mConfiguredFormat, mConfiguredDataspace,
-                    mSurfaceGroupId, mSurfaceType, mIsShared ? 1 : 0);
+                    mSurfaceGroupId, mSurfaceType, mIsShared ? 1 : 0,
+                    mPhysicalCameraId == null ? 0 : mPhysicalCameraId.hashCode());
         }
 
         return HashCodeHelpers.hashCode(
                 mRotation, mSurfaces.hashCode(), mConfiguredGenerationId,
                 mConfiguredSize.hashCode(), mConfiguredFormat,
-                mConfiguredDataspace, mSurfaceGroupId, mIsShared ? 1 : 0);
+                mConfiguredDataspace, mSurfaceGroupId, mIsShared ? 1 : 0,
+                mPhysicalCameraId == null ? 0 : mPhysicalCameraId.hashCode());
     }
 
     private static final String TAG = "OutputConfiguration";
@@ -701,4 +747,6 @@
     private final boolean mIsDeferredConfig;
     // Flag indicating if this config has shared surfaces
     private boolean mIsShared;
+    // The physical camera id that this output configuration is for.
+    private String mPhysicalCameraId;
 }
diff --git a/core/java/android/hardware/radio/ProgramSelector.java b/core/java/android/hardware/radio/ProgramSelector.java
index 3556751..0cf7605 100644
--- a/core/java/android/hardware/radio/ProgramSelector.java
+++ b/core/java/android/hardware/radio/ProgramSelector.java
@@ -59,24 +59,56 @@
  */
 @SystemApi
 public final class ProgramSelector implements Parcelable {
+    /** Invalid program type.
+     * @deprecated use {@link ProgramIdentifier} instead
+     */
+    @Deprecated
     public static final int PROGRAM_TYPE_INVALID = 0;
-    /** Analogue AM radio (with or without RDS). */
+    /** Analogue AM radio (with or without RDS).
+     * @deprecated use {@link ProgramIdentifier} instead
+     */
+    @Deprecated
     public static final int PROGRAM_TYPE_AM = 1;
-    /** analogue FM radio (with or without RDS). */
+    /** analogue FM radio (with or without RDS).
+     * @deprecated use {@link ProgramIdentifier} instead
+     */
+    @Deprecated
     public static final int PROGRAM_TYPE_FM = 2;
-    /** AM HD Radio. */
+    /** AM HD Radio.
+     * @deprecated use {@link ProgramIdentifier} instead
+     */
+    @Deprecated
     public static final int PROGRAM_TYPE_AM_HD = 3;
-    /** FM HD Radio. */
+    /** FM HD Radio.
+     * @deprecated use {@link ProgramIdentifier} instead
+     */
+    @Deprecated
     public static final int PROGRAM_TYPE_FM_HD = 4;
-    /** Digital audio broadcasting. */
+    /** Digital audio broadcasting.
+     * @deprecated use {@link ProgramIdentifier} instead
+     */
+    @Deprecated
     public static final int PROGRAM_TYPE_DAB = 5;
-    /** Digital Radio Mondiale. */
+    /** Digital Radio Mondiale.
+     * @deprecated use {@link ProgramIdentifier} instead
+     */
+    @Deprecated
     public static final int PROGRAM_TYPE_DRMO = 6;
-    /** SiriusXM Satellite Radio. */
+    /** SiriusXM Satellite Radio.
+     * @deprecated use {@link ProgramIdentifier} instead
+     */
+    @Deprecated
     public static final int PROGRAM_TYPE_SXM = 7;
-    /** Vendor-specific, not synced across devices. */
+    /** Vendor-specific, not synced across devices.
+     * @deprecated use {@link ProgramIdentifier} instead
+     */
+    @Deprecated
     public static final int PROGRAM_TYPE_VENDOR_START = 1000;
+    /** @deprecated use {@link ProgramIdentifier} instead */
+    @Deprecated
     public static final int PROGRAM_TYPE_VENDOR_END = 1999;
+    /** @deprecated use {@link ProgramIdentifier} instead */
+    @Deprecated
     @IntDef(prefix = { "PROGRAM_TYPE_" }, value = {
         PROGRAM_TYPE_INVALID,
         PROGRAM_TYPE_AM,
@@ -112,18 +144,46 @@
      *
      * The subchannel index is 0-based (where 0 is MPS and 1..7 are SPS),
      * as opposed to HD Radio standard (where it's 1-based).
+     *
+     * @deprecated use IDENTIFIER_TYPE_HD_STATION_ID_EXT instead
      */
+    @Deprecated
     public static final int IDENTIFIER_TYPE_HD_SUBCHANNEL = 4;
     /**
-     * 24bit compound primary identifier for DAB.
+     * 64bit additional identifier for HD Radio.
+     *
+     * Due to Station ID abuse, some HD_STATION_ID_EXT identifiers may be not
+     * globally unique. To provide a best-effort solution, a short version of
+     * station name may be carried as additional identifier and may be used
+     * by the tuner hardware to double-check tuning.
+     *
+     * The name is limited to the first 8 A-Z0-9 characters (lowercase letters
+     * must be converted to uppercase). Encoded in little-endian ASCII:
+     * the first character of the name is the LSB.
+     *
+     * For example: "Abc" is encoded as 0x434241.
+     */
+    public static final int IDENTIFIER_TYPE_HD_STATION_NAME = 10004;
+    /**
+     * @see {@link IDENTIFIER_TYPE_DAB_SID_EXT}
+     */
+    public static final int IDENTIFIER_TYPE_DAB_SIDECC = 5;
+    /**
+     * 28bit compound primary identifier for Digital Audio Broadcasting.
      *
      * Consists of (from the LSB):
      * - 16bit: SId;
-     * - 8bit: ECC code.
+     * - 8bit: ECC code;
+     * - 4bit: SCIdS.
+     *
+     * SCIdS (Service Component Identifier within the Service) value
+     * of 0 represents the main service, while 1 and above represents
+     * secondary services.
+     *
      * The remaining bits should be set to zeros when writing on the chip side
      * and ignored when read.
      */
-    public static final int IDENTIFIER_TYPE_DAB_SIDECC = 5;
+    public static final int IDENTIFIER_TYPE_DAB_SID_EXT = IDENTIFIER_TYPE_DAB_SIDECC;
     /** 16bit */
     public static final int IDENTIFIER_TYPE_DAB_ENSEMBLE = 6;
     /** 12bit */
@@ -134,7 +194,11 @@
     public static final int IDENTIFIER_TYPE_DRMO_SERVICE_ID = 9;
     /** kHz */
     public static final int IDENTIFIER_TYPE_DRMO_FREQUENCY = 10;
-    /** 1: AM, 2:FM */
+    /**
+     * 1: AM, 2:FM
+     * @deprecated use {@link IDENTIFIER_TYPE_DRMO_FREQUENCY} instead
+     */
+    @Deprecated
     public static final int IDENTIFIER_TYPE_DRMO_MODULATION = 11;
     /** 32bit */
     public static final int IDENTIFIER_TYPE_SXM_SERVICE_ID = 12;
@@ -148,14 +212,29 @@
      * type between VENDOR_START and VENDOR_END (eg. identifier type 1015 must
      * not be used in any program type other than 1015).
      */
-    public static final int IDENTIFIER_TYPE_VENDOR_PRIMARY_START = PROGRAM_TYPE_VENDOR_START;
-    public static final int IDENTIFIER_TYPE_VENDOR_PRIMARY_END = PROGRAM_TYPE_VENDOR_END;
+    public static final int IDENTIFIER_TYPE_VENDOR_START = PROGRAM_TYPE_VENDOR_START;
+    /**
+     * @see {@link IDENTIFIER_TYPE_VENDOR_START}
+     */
+    public static final int IDENTIFIER_TYPE_VENDOR_END = PROGRAM_TYPE_VENDOR_END;
+    /**
+     * @deprecated use {@link IDENTIFIER_TYPE_VENDOR_START} instead
+     */
+    @Deprecated
+    public static final int IDENTIFIER_TYPE_VENDOR_PRIMARY_START = IDENTIFIER_TYPE_VENDOR_START;
+    /**
+     * @deprecated use {@link IDENTIFIER_TYPE_VENDOR_END} instead
+     */
+    @Deprecated
+    public static final int IDENTIFIER_TYPE_VENDOR_PRIMARY_END = IDENTIFIER_TYPE_VENDOR_END;
     @IntDef(prefix = { "IDENTIFIER_TYPE_" }, value = {
         IDENTIFIER_TYPE_INVALID,
         IDENTIFIER_TYPE_AMFM_FREQUENCY,
         IDENTIFIER_TYPE_RDS_PI,
         IDENTIFIER_TYPE_HD_STATION_ID_EXT,
         IDENTIFIER_TYPE_HD_SUBCHANNEL,
+        IDENTIFIER_TYPE_HD_STATION_NAME,
+        IDENTIFIER_TYPE_DAB_SID_EXT,
         IDENTIFIER_TYPE_DAB_SIDECC,
         IDENTIFIER_TYPE_DAB_ENSEMBLE,
         IDENTIFIER_TYPE_DAB_SCID,
@@ -166,7 +245,7 @@
         IDENTIFIER_TYPE_SXM_SERVICE_ID,
         IDENTIFIER_TYPE_SXM_CHANNEL,
     })
-    @IntRange(from = IDENTIFIER_TYPE_VENDOR_PRIMARY_START, to = IDENTIFIER_TYPE_VENDOR_PRIMARY_END)
+    @IntRange(from = IDENTIFIER_TYPE_VENDOR_START, to = IDENTIFIER_TYPE_VENDOR_END)
     @Retention(RetentionPolicy.SOURCE)
     public @interface IdentifierType {}
 
@@ -205,7 +284,9 @@
      * Type of a radio technology.
      *
      * @return program type.
+     * @deprecated use {@link getPrimaryId} instead
      */
+    @Deprecated
     public @ProgramType int getProgramType() {
         return mProgramType;
     }
@@ -273,7 +354,10 @@
      * preserving elements order.
      *
      * @return an array of vendor identifiers, must not be modified.
+     * @deprecated for HAL 1.x compatibility;
+     *             HAL 2.x uses standard primary/secondary lists for vendor IDs
      */
+    @Deprecated
     public @NonNull long[] getVendorIds() {
         return mVendorIds;
     }
@@ -427,6 +511,10 @@
         private final long mValue;
 
         public Identifier(@IdentifierType int type, long value) {
+            if (type == IDENTIFIER_TYPE_HD_STATION_NAME) {
+                // see getType
+                type = IDENTIFIER_TYPE_HD_SUBCHANNEL;
+            }
             mType = type;
             mValue = value;
         }
@@ -437,6 +525,13 @@
          * @return type of an identifier.
          */
         public @IdentifierType int getType() {
+            if (mType == IDENTIFIER_TYPE_HD_SUBCHANNEL && mValue > 10) {
+                /* HD_SUBCHANNEL and HD_STATION_NAME use the same identifier type, but they differ
+                 * in possible values: sub channel is 0-7, station name is greater than ASCII space
+                 * code (32).
+                 */
+                return IDENTIFIER_TYPE_HD_STATION_NAME;
+            }
             return mType;
         }
 
diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java
index 11d338d..166342d 100644
--- a/core/java/android/net/ConnectivityManager.java
+++ b/core/java/android/net/ConnectivityManager.java
@@ -3763,4 +3763,20 @@
             throw e.rethrowFromSystemServer();
         }
     }
+
+    /**
+     * The network watchlist is a list of domains and IP addresses that are associated with
+     * potentially harmful apps. This method returns the hash of the watchlist currently
+     * used by the system.
+     *
+     * @return Hash of network watchlist config file. Null if config does not exist.
+     */
+    public byte[] getNetworkWatchlistConfigHash() {
+        try {
+            return mService.getNetworkWatchlistConfigHash();
+        } catch (RemoteException e) {
+            Log.e(TAG, "Unable to get watchlist config hash");
+            throw e.rethrowFromSystemServer();
+        }
+    }
 }
diff --git a/core/java/android/net/IConnectivityManager.aidl b/core/java/android/net/IConnectivityManager.aidl
index a6fe738..ce95b60 100644
--- a/core/java/android/net/IConnectivityManager.aidl
+++ b/core/java/android/net/IConnectivityManager.aidl
@@ -180,4 +180,6 @@
     void stopKeepalive(in Network network, int slot);
 
     String getCaptivePortalServerUrl();
+
+    byte[] getNetworkWatchlistConfigHash();
 }
diff --git a/core/java/android/net/IIpSecService.aidl b/core/java/android/net/IIpSecService.aidl
index 790c80b..eeb30e2 100644
--- a/core/java/android/net/IIpSecService.aidl
+++ b/core/java/android/net/IIpSecService.aidl
@@ -39,9 +39,9 @@
 
     void closeUdpEncapsulationSocket(int resourceId);
 
-    IpSecTransformResponse createTransportModeTransform(in IpSecConfig c, in IBinder binder);
+    IpSecTransformResponse createTransform(in IpSecConfig c, in IBinder binder);
 
-    void deleteTransportModeTransform(int transformId);
+    void deleteTransform(int transformId);
 
     void applyTransportModeTransform(in ParcelFileDescriptor socket, int direction, int transformId);
 
diff --git a/core/java/android/net/IpSecManager.java b/core/java/android/net/IpSecManager.java
index 2cda58c..f04f03f6 100644
--- a/core/java/android/net/IpSecManager.java
+++ b/core/java/android/net/IpSecManager.java
@@ -19,6 +19,7 @@
 
 import android.annotation.IntDef;
 import android.annotation.NonNull;
+import android.annotation.SystemApi;
 import android.annotation.SystemService;
 import android.annotation.TestApi;
 import android.content.Context;
@@ -625,6 +626,133 @@
     }
 
     /**
+     * This class represents an IpSecTunnelInterface
+     *
+     * <p>IpSecTunnelInterface objects track tunnel interfaces that serve as
+     * local endpoints for IPsec tunnels.
+     *
+     * <p>Creating an IpSecTunnelInterface creates a device to which IpSecTransforms may be
+     * applied to provide IPsec security to packets sent through the tunnel. While a tunnel
+     * cannot be used in standalone mode within Android, the higher layers may use the tunnel
+     * to create Network objects which are accessible to the Android system.
+     * @hide
+     */
+    @SystemApi
+    public static final class IpSecTunnelInterface implements AutoCloseable {
+        private final IIpSecService mService;
+        private final InetAddress mRemoteAddress;
+        private final InetAddress mLocalAddress;
+        private final Network mUnderlyingNetwork;
+        private final CloseGuard mCloseGuard = CloseGuard.get();
+        private String mInterfaceName;
+        private int mResourceId = INVALID_RESOURCE_ID;
+
+        /** Get the underlying SPI held by this object. */
+        public String getInterfaceName() {
+            return mInterfaceName;
+        }
+
+        /**
+         * Add an address to the IpSecTunnelInterface
+         *
+         * <p>Add an address which may be used as the local inner address for
+         * tunneled traffic.
+         *
+         * @param address the local address for traffic inside the tunnel
+         * @throws IOException if the address could not be added
+         * @hide
+         */
+        public void addAddress(LinkAddress address) throws IOException {
+        }
+
+        /**
+         * Remove an address from the IpSecTunnelInterface
+         *
+         * <p>Remove an address which was previously added to the IpSecTunnelInterface
+         *
+         * @param address to be removed
+         * @throws IOException if the address could not be removed
+         * @hide
+         */
+        public void removeAddress(LinkAddress address) throws IOException {
+        }
+
+        private IpSecTunnelInterface(@NonNull IIpSecService service,
+                @NonNull InetAddress localAddress, @NonNull InetAddress remoteAddress,
+                @NonNull Network underlyingNetwork)
+                throws ResourceUnavailableException, IOException {
+            mService = service;
+            mLocalAddress = localAddress;
+            mRemoteAddress = remoteAddress;
+            mUnderlyingNetwork = underlyingNetwork;
+            // TODO: Call IpSecService
+        }
+
+        /**
+         * Delete an IpSecTunnelInterface
+         *
+         * <p>Calling close will deallocate the IpSecTunnelInterface and all of its system
+         * resources. Any packets bound for this interface either inbound or outbound will
+         * all be lost.
+         */
+        @Override
+        public void close() {
+            // try {
+            // TODO: Call IpSecService
+            mResourceId = INVALID_RESOURCE_ID;
+            // } catch (RemoteException e) {
+            //    throw e.rethrowFromSystemServer();
+            // }
+            mCloseGuard.close();
+        }
+
+        /** Check that the Interface was closed properly. */
+        @Override
+        protected void finalize() throws Throwable {
+            if (mCloseGuard != null) {
+                mCloseGuard.warnIfOpen();
+            }
+            close();
+        }
+    }
+
+    /**
+     * Create a new IpSecTunnelInterface as a local endpoint for tunneled IPsec traffic.
+     *
+     * @param localAddress The local addres of the tunnel
+     * @param remoteAddress The local addres of the tunnel
+     * @param underlyingNetwork the {@link Network} that will carry traffic for this tunnel.
+     *        This network should almost certainly be a network such as WiFi with an L2 address.
+     * @return a new {@link IpSecManager#IpSecTunnelInterface} with the specified properties
+     * @throws IOException indicating that the socket could not be opened or bound
+     * @throws ResourceUnavailableException indicating that too many encapsulation sockets are open
+     * @hide
+     */
+    @SystemApi
+    public IpSecTunnelInterface createIpSecTunnelInterface(@NonNull InetAddress localAddress,
+            @NonNull InetAddress remoteAddress, @NonNull Network underlyingNetwork)
+            throws ResourceUnavailableException, IOException {
+        return new IpSecTunnelInterface(mService, localAddress, remoteAddress, underlyingNetwork);
+    }
+
+    /**
+     * Apply a transform to the IpSecTunnelInterface
+     *
+     * @param tunnel The {@link IpSecManager#IpSecTunnelInterface} that will use the supplied
+     *        transform.
+     * @param direction the direction, {@link DIRECTION_OUT} or {@link #DIRECTION_IN} in which
+     *        the transform will be used.
+     * @param transform an {@link IpSecTransform} created in tunnel mode
+     * @throws IOException indicating that the transform could not be applied due to a lower
+     *         layer failure.
+     * @hide
+     */
+    @SystemApi
+    void applyTunnelModeTransform(IpSecTunnelInterface tunnel, int direction,
+            IpSecTransform transform) throws IOException {
+        // TODO: call IpSecService
+    }
+    /**
      * Construct an instance of IpSecManager within an application context.
      *
      * @param context the application context for this manager
diff --git a/core/java/android/net/IpSecTransform.java b/core/java/android/net/IpSecTransform.java
index 7b9b483..37e2c4f 100644
--- a/core/java/android/net/IpSecTransform.java
+++ b/core/java/android/net/IpSecTransform.java
@@ -124,8 +124,7 @@
         synchronized (this) {
             try {
                 IIpSecService svc = getIpSecService();
-                IpSecTransformResponse result =
-                        svc.createTransportModeTransform(mConfig, new Binder());
+                IpSecTransformResponse result = svc.createTransform(mConfig, new Binder());
                 int status = result.status;
                 checkResultStatus(status);
                 mResourceId = result.resourceId;
@@ -170,7 +169,7 @@
              * still want to clear out the transform.
              */
             IIpSecService svc = getIpSecService();
-            svc.deleteTransportModeTransform(mResourceId);
+            svc.deleteTransform(mResourceId);
             stopKeepalive();
         } catch (RemoteException e) {
             throw e.rethrowAsRuntimeException();
@@ -300,21 +299,6 @@
         }
 
         /**
-         * Set the {@link Network} which will carry tunneled traffic.
-         *
-         * <p>Restricts the transformed traffic to a particular {@link Network}. This is required
-         * for tunnel mode, otherwise tunneled traffic would be sent on the default network.
-         *
-         * @hide
-         */
-        @SystemApi
-        public IpSecTransform.Builder setUnderlyingNetwork(@NonNull Network net) {
-            Preconditions.checkNotNull(net);
-            mConfig.setNetwork(net);
-            return this;
-        }
-
-        /**
          * Add UDP encapsulation to an IPv4 transform.
          *
          * <p>This allows IPsec traffic to pass through a NAT.
@@ -415,6 +399,7 @@
          * @throws IOException indicating other errors
          * @hide
          */
+        @SystemApi
         public IpSecTransform buildTunnelModeTransform(
                 @NonNull InetAddress sourceAddress,
                 @NonNull IpSecManager.SecurityParameterIndex spi)
diff --git a/core/java/android/net/NetworkIdentity.java b/core/java/android/net/NetworkIdentity.java
index fd118f3..ce2de85 100644
--- a/core/java/android/net/NetworkIdentity.java
+++ b/core/java/android/net/NetworkIdentity.java
@@ -130,6 +130,7 @@
         proto.write(NetworkIdentityProto.NETWORK_ID, mNetworkId);
         proto.write(NetworkIdentityProto.ROAMING, mRoaming);
         proto.write(NetworkIdentityProto.METERED, mMetered);
+        proto.write(NetworkIdentityProto.DEFAULT_NETWORK, mDefaultNetwork);
 
         proto.end(start);
     }
diff --git a/core/java/android/net/NetworkStats.java b/core/java/android/net/NetworkStats.java
index a85f80e..01b2b39 100644
--- a/core/java/android/net/NetworkStats.java
+++ b/core/java/android/net/NetworkStats.java
@@ -160,13 +160,6 @@
                     rxBytes, rxPackets, txBytes, txPackets, operations);
         }
 
-        // TODO: fix the the telephony code to pass DEFAULT_NETWORK_YES and remove this constructor.
-        public Entry(String iface, int uid, int set, int tag, int metered, int roaming,
-                 long rxBytes, long rxPackets, long txBytes, long txPackets, long operations) {
-            this(iface, uid, set, tag, metered, roaming, DEFAULT_NETWORK_YES, rxBytes, rxPackets,
-                    txBytes, txPackets, operations);
-        }
-
         public Entry(String iface, int uid, int set, int tag, int metered, int roaming,
                  int defaultNetwork, long rxBytes, long rxPackets, long txBytes, long txPackets,
                  long operations) {
diff --git a/core/java/android/net/NetworkTemplate.java b/core/java/android/net/NetworkTemplate.java
index b307c5d..8efd39a 100644
--- a/core/java/android/net/NetworkTemplate.java
+++ b/core/java/android/net/NetworkTemplate.java
@@ -24,6 +24,15 @@
 import static android.net.ConnectivityManager.TYPE_WIFI_P2P;
 import static android.net.ConnectivityManager.TYPE_WIMAX;
 import static android.net.NetworkIdentity.COMBINE_SUBTYPE_ENABLED;
+import static android.net.NetworkStats.DEFAULT_NETWORK_ALL;
+import static android.net.NetworkStats.DEFAULT_NETWORK_NO;
+import static android.net.NetworkStats.DEFAULT_NETWORK_YES;
+import static android.net.NetworkStats.METERED_ALL;
+import static android.net.NetworkStats.METERED_NO;
+import static android.net.NetworkStats.METERED_YES;
+import static android.net.NetworkStats.ROAMING_ALL;
+import static android.net.NetworkStats.ROAMING_NO;
+import static android.net.NetworkStats.ROAMING_YES;
 import static android.net.wifi.WifiInfo.removeDoubleQuotes;
 import static android.telephony.TelephonyManager.NETWORK_CLASS_2_G;
 import static android.telephony.TelephonyManager.NETWORK_CLASS_3_G;
@@ -191,16 +200,30 @@
 
     private final String mNetworkId;
 
+    // Matches for the NetworkStats constants METERED_*, ROAMING_* and DEFAULT_NETWORK_*.
+    private final int mMetered;
+    private final int mRoaming;
+    private final int mDefaultNetwork;
+
     public NetworkTemplate(int matchRule, String subscriberId, String networkId) {
         this(matchRule, subscriberId, new String[] { subscriberId }, networkId);
     }
 
     public NetworkTemplate(int matchRule, String subscriberId, String[] matchSubscriberIds,
             String networkId) {
+        this(matchRule, subscriberId, matchSubscriberIds, networkId, METERED_ALL, ROAMING_ALL,
+                DEFAULT_NETWORK_ALL);
+    }
+
+    public NetworkTemplate(int matchRule, String subscriberId, String[] matchSubscriberIds,
+            String networkId, int metered, int roaming, int defaultNetwork) {
         mMatchRule = matchRule;
         mSubscriberId = subscriberId;
         mMatchSubscriberIds = matchSubscriberIds;
         mNetworkId = networkId;
+        mMetered = metered;
+        mRoaming = roaming;
+        mDefaultNetwork = defaultNetwork;
 
         if (!isKnownMatchRule(matchRule)) {
             Log.e(TAG, "Unknown network template rule " + matchRule
@@ -213,6 +236,9 @@
         mSubscriberId = in.readString();
         mMatchSubscriberIds = in.createStringArray();
         mNetworkId = in.readString();
+        mMetered = in.readInt();
+        mRoaming = in.readInt();
+        mDefaultNetwork = in.readInt();
     }
 
     @Override
@@ -221,6 +247,9 @@
         dest.writeString(mSubscriberId);
         dest.writeStringArray(mMatchSubscriberIds);
         dest.writeString(mNetworkId);
+        dest.writeInt(mMetered);
+        dest.writeInt(mRoaming);
+        dest.writeInt(mDefaultNetwork);
     }
 
     @Override
@@ -243,12 +272,23 @@
         if (mNetworkId != null) {
             builder.append(", networkId=").append(mNetworkId);
         }
+        if (mMetered != METERED_ALL) {
+            builder.append(", metered=").append(NetworkStats.meteredToString(mMetered));
+        }
+        if (mRoaming != ROAMING_ALL) {
+            builder.append(", roaming=").append(NetworkStats.roamingToString(mRoaming));
+        }
+        if (mDefaultNetwork != DEFAULT_NETWORK_ALL) {
+            builder.append(", defaultNetwork=").append(NetworkStats.defaultNetworkToString(
+                    mDefaultNetwork));
+        }
         return builder.toString();
     }
 
     @Override
     public int hashCode() {
-        return Objects.hash(mMatchRule, mSubscriberId, mNetworkId);
+        return Objects.hash(mMatchRule, mSubscriberId, mNetworkId, mMetered, mRoaming,
+                mDefaultNetwork);
     }
 
     @Override
@@ -257,7 +297,10 @@
             final NetworkTemplate other = (NetworkTemplate) obj;
             return mMatchRule == other.mMatchRule
                     && Objects.equals(mSubscriberId, other.mSubscriberId)
-                    && Objects.equals(mNetworkId, other.mNetworkId);
+                    && Objects.equals(mNetworkId, other.mNetworkId)
+                    && mMetered == other.mMetered
+                    && mRoaming == other.mRoaming
+                    && mDefaultNetwork == other.mDefaultNetwork;
         }
         return false;
     }
@@ -300,6 +343,10 @@
      * Test if given {@link NetworkIdentity} matches this template.
      */
     public boolean matches(NetworkIdentity ident) {
+        if (!matchesMetered(ident)) return false;
+        if (!matchesRoaming(ident)) return false;
+        if (!matchesDefaultNetwork(ident)) return false;
+
         switch (mMatchRule) {
             case MATCH_MOBILE_ALL:
                 return matchesMobile(ident);
@@ -326,6 +373,24 @@
         }
     }
 
+    private boolean matchesMetered(NetworkIdentity ident) {
+        return (mMetered == METERED_ALL)
+            || (mMetered == METERED_YES && ident.mMetered)
+            || (mMetered == METERED_NO && !ident.mMetered);
+    }
+
+    private boolean matchesRoaming(NetworkIdentity ident) {
+        return (mRoaming == ROAMING_ALL)
+            || (mRoaming == ROAMING_YES && ident.mRoaming)
+            || (mRoaming == ROAMING_NO && !ident.mRoaming);
+    }
+
+    private boolean matchesDefaultNetwork(NetworkIdentity ident) {
+        return (mDefaultNetwork == DEFAULT_NETWORK_ALL)
+            || (mDefaultNetwork == DEFAULT_NETWORK_YES && ident.mDefaultNetwork)
+            || (mDefaultNetwork == DEFAULT_NETWORK_NO && !ident.mDefaultNetwork);
+    }
+
     public boolean matchesSubscriberId(String subscriberId) {
         return ArrayUtils.contains(mMatchSubscriberIds, subscriberId);
     }
diff --git a/core/java/android/net/NetworkWatchlistManager.java b/core/java/android/net/NetworkWatchlistManager.java
index 5425bf5..49047d3 100644
--- a/core/java/android/net/NetworkWatchlistManager.java
+++ b/core/java/android/net/NetworkWatchlistManager.java
@@ -86,4 +86,16 @@
             e.rethrowFromSystemServer();
         }
     }
+
+    /**
+     * Get Network Watchlist config file hash.
+     */
+    public byte[] getWatchlistConfigHash() {
+        try {
+            return mNetworkWatchlistManager.getWatchlistConfigHash();
+        } catch (RemoteException e) {
+            Log.e(TAG, "Unable to get watchlist config hash");
+            throw e.rethrowFromSystemServer();
+        }
+    }
 }
diff --git a/core/java/android/os/BatteryStats.java b/core/java/android/os/BatteryStats.java
index dc271d8..03a8dba 100644
--- a/core/java/android/os/BatteryStats.java
+++ b/core/java/android/os/BatteryStats.java
@@ -35,6 +35,7 @@
 import android.view.Display;
 
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.location.gnssmetrics.GnssMetrics;
 import com.android.internal.os.BatterySipper;
 import com.android.internal.os.BatteryStatsHelper;
 
@@ -335,6 +336,9 @@
     private final StringBuilder mFormatBuilder = new StringBuilder(32);
     private final Formatter mFormatter = new Formatter(mFormatBuilder);
 
+    private static final String CELLULAR_CONTROLLER_NAME = "Cellular";
+    private static final String WIFI_CONTROLLER_NAME = "WiFi";
+
     /**
      * Indicates times spent by the uid at each cpu frequency in all process states.
      *
@@ -412,6 +416,13 @@
 
         /**
          * @return a non-null {@link LongCounter} representing time spent (milliseconds) in the
+         * scan state.
+         */
+        public abstract LongCounter getScanTimeCounter();
+
+
+        /**
+         * @return a non-null {@link LongCounter} representing time spent (milliseconds) in the
          * receive state.
          */
         public abstract LongCounter getRxTimeCounter();
@@ -683,6 +694,14 @@
 
         public abstract long[] getCpuFreqTimes(int which);
         public abstract long[] getScreenOffCpuFreqTimes(int which);
+        /**
+         * Returns cpu active time of an uid.
+         */
+        public abstract long getCpuActiveTime();
+        /**
+         * Returns cpu times of an uid on each cluster
+         */
+        public abstract long[] getCpuClusterTimes();
 
         /**
          * Returns cpu times of an uid at a particular process state.
@@ -1500,6 +1519,10 @@
         public static final int STATE2_WIFI_SIGNAL_STRENGTH_SHIFT = 4;
         public static final int STATE2_WIFI_SIGNAL_STRENGTH_MASK =
                 0x7 << STATE2_WIFI_SIGNAL_STRENGTH_SHIFT;
+        // Values for NUM_GPS_SIGNAL_QUALITY_LEVELS
+        public static final int STATE2_GPS_SIGNAL_QUALITY_SHIFT = 7;
+        public static final int STATE2_GPS_SIGNAL_QUALITY_MASK =
+            0x1 << STATE2_GPS_SIGNAL_QUALITY_SHIFT;
 
         public static final int STATE2_POWER_SAVE_FLAG = 1<<31;
         public static final int STATE2_VIDEO_ON_FLAG = 1<<30;
@@ -2088,6 +2111,23 @@
      */
     public abstract int getNumConnectivityChange(int which);
 
+
+    /**
+     * Returns the time in microseconds that the phone has been running with
+     * the given GPS signal quality level
+     *
+     * {@hide}
+     */
+    public abstract long getGpsSignalQualityTime(int strengthBin,
+        long elapsedRealtimeUs, int which);
+
+    /**
+     * Returns the GPS battery drain in mA-ms
+     *
+     * {@hide}
+     */
+    public abstract long getGpsBatteryDrainMaMs();
+
     /**
      * Returns the time in microseconds that the phone has been on while the device was
      * running on battery.
@@ -2312,6 +2352,9 @@
                 WIFI_SUPPL_STATE_NAMES, WIFI_SUPPL_STATE_SHORT_NAMES),
         new BitDescription(HistoryItem.STATE2_CAMERA_FLAG, "camera", "ca"),
         new BitDescription(HistoryItem.STATE2_BLUETOOTH_SCAN_FLAG, "ble_scan", "bles"),
+        new BitDescription(HistoryItem.STATE2_GPS_SIGNAL_QUALITY_MASK,
+            HistoryItem.STATE2_GPS_SIGNAL_QUALITY_SHIFT, "gps_signal_quality", "Gss",
+            new String[] { "poor", "good"}, new String[] { "poor", "good"}),
     };
 
     public static final String[] HISTORY_EVENT_NAMES = new String[] {
@@ -2366,6 +2409,14 @@
     public abstract long getWifiOnTime(long elapsedRealtimeUs, int which);
 
     /**
+     * Returns the time in microseconds that wifi has been active while the device was
+     * running on battery.
+     *
+     * {@hide}
+     */
+    public abstract long getWifiActiveTime(long elapsedRealtimeUs, int which);
+
+    /**
      * Returns the time in microseconds that wifi has been on and the driver has
      * been in the running state while the device was running on battery.
      *
@@ -3312,6 +3363,20 @@
         final long sleepTimeMs
             = totalControllerActivityTimeMs - (idleTimeMs + rxTimeMs + totalTxTimeMs);
 
+        if (controllerName.equals(WIFI_CONTROLLER_NAME)) {
+            final long scanTimeMs = counter.getScanTimeCounter().getCountLocked(which);
+            sb.setLength(0);
+            sb.append(prefix);
+            sb.append("     ");
+            sb.append(controllerName);
+            sb.append(" Scan time:  ");
+            formatTimeMs(sb, scanTimeMs);
+            sb.append("(");
+            sb.append(formatRatioLocked(scanTimeMs, totalControllerActivityTimeMs));
+            sb.append(")");
+            pw.println(sb.toString());
+        }
+
         sb.setLength(0);
         sb.append(prefix);
         sb.append("     ");
@@ -3353,7 +3418,7 @@
 
         String [] powerLevel;
         switch(controllerName) {
-            case "Cellular":
+            case CELLULAR_CONTROLLER_NAME:
                 powerLevel = new String[] {
                     "   less than 0dBm: ",
                     "   0dBm to 8dBm: ",
@@ -4641,7 +4706,7 @@
         if (!didOne) sb.append(" (no activity)");
         pw.println(sb.toString());
 
-        printControllerActivity(pw, sb, prefix, "Cellular",
+        printControllerActivity(pw, sb, prefix, CELLULAR_CONTROLLER_NAME,
             getModemControllerActivity(), which);
 
         pw.print(prefix);
@@ -4650,6 +4715,16 @@
         sb.append("  Wifi Statistics:");
         pw.println(sb.toString());
 
+        pw.print(prefix);
+        sb.setLength(0);
+        sb.append(prefix);
+        sb.append("     Wifi kernel active time: ");
+        final long wifiActiveTime = getWifiActiveTime(rawRealtime, which);
+        formatTimeMs(sb, wifiActiveTime / 1000);
+        sb.append("("); sb.append(formatRatioLocked(wifiActiveTime, whichBatteryRealtime));
+        sb.append(")");
+        pw.println(sb.toString());
+
         pw.print("     Wifi data received: "); pw.println(formatBytesLocked(wifiRxTotalBytes));
         pw.print("     Wifi data sent: "); pw.println(formatBytesLocked(wifiTxTotalBytes));
         pw.print("     Wifi packets received: "); pw.println(wifiRxTotalPackets);
@@ -4727,7 +4802,45 @@
         if (!didOne) sb.append(" (no activity)");
         pw.println(sb.toString());
 
-        printControllerActivity(pw, sb, prefix, "WiFi", getWifiControllerActivity(), which);
+        printControllerActivity(pw, sb, prefix, WIFI_CONTROLLER_NAME,
+            getWifiControllerActivity(), which);
+
+        pw.print(prefix);
+        sb.setLength(0);
+        sb.append(prefix);
+        sb.append("  GPS Statistics:");
+        pw.println(sb.toString());
+
+        sb.setLength(0);
+        sb.append(prefix);
+        sb.append("     GPS signal quality (Top 4 Average CN0):");
+        final String[] gpsSignalQualityDescription = new String[]{
+            "poor (less than 20 dBHz): ",
+            "good (greater than 20 dBHz): "};
+        final int numGpsSignalQualityBins = Math.min(GnssMetrics.NUM_GPS_SIGNAL_QUALITY_LEVELS,
+            gpsSignalQualityDescription.length);
+        for (int i=0; i<numGpsSignalQualityBins; i++) {
+            final long time = getGpsSignalQualityTime(i, rawRealtime, which);
+            sb.append("\n    ");
+            sb.append(prefix);
+            sb.append("  ");
+            sb.append(gpsSignalQualityDescription[i]);
+            formatTimeMs(sb, time/1000);
+            sb.append("(");
+            sb.append(formatRatioLocked(time, whichBatteryRealtime));
+            sb.append(") ");
+        }
+        pw.println(sb.toString());
+
+        final long gpsBatteryDrainMaMs = getGpsBatteryDrainMaMs();
+        if (gpsBatteryDrainMaMs > 0) {
+            pw.print(prefix);
+            sb.setLength(0);
+            sb.append(prefix);
+            sb.append("     Battery Drain (mAh): ");
+            sb.append(Double.toString(((double) gpsBatteryDrainMaMs)/(3600 * 1000)));
+            pw.println(sb.toString());
+        }
 
         pw.print(prefix);
         sb.setLength(0);
@@ -5168,8 +5281,8 @@
                 pw.println(sb.toString());
             }
 
-            printControllerActivityIfInteresting(pw, sb, prefix + "  ", "Modem",
-                    u.getModemControllerActivity(), which);
+            printControllerActivityIfInteresting(pw, sb, prefix + "  ",
+                CELLULAR_CONTROLLER_NAME, u.getModemControllerActivity(), which);
 
             if (wifiRxBytes > 0 || wifiTxBytes > 0 || wifiRxPackets > 0 || wifiTxPackets > 0) {
                 pw.print(prefix); pw.print("    Wi-Fi network: ");
@@ -5223,7 +5336,7 @@
                 pw.println(sb.toString());
             }
 
-            printControllerActivityIfInteresting(pw, sb, prefix + "  ", "WiFi",
+            printControllerActivityIfInteresting(pw, sb, prefix + "  ", WIFI_CONTROLLER_NAME,
                     u.getWifiControllerActivity(), which);
 
             if (btRxBytes > 0 || btTxBytes > 0) {
diff --git a/core/java/android/os/Debug.java b/core/java/android/os/Debug.java
index 848ab88..33e8c3e 100644
--- a/core/java/android/os/Debug.java
+++ b/core/java/android/os/Debug.java
@@ -2352,22 +2352,28 @@
     }
 
     /**
-     * Attach a library as a jvmti agent to the current runtime.
+     * Attach a library as a jvmti agent to the current runtime, with the given classloader
+     * determining the library search path.
+     * <p>
+     * Note: agents may only be attached to debuggable apps. Otherwise, this function will
+     * throw a SecurityException.
      *
-     * @param library library containing the agent
-     * @param options options passed to the agent
+     * @param library the library containing the agent.
+     * @param options the options passed to the agent.
+     * @param classLoader the classloader determining the library search path.
      *
-     * @throws IOException If the agent could not be attached
+     * @throws IOException if the agent could not be attached.
+     * @throws SecurityException if the app is not debuggable.
      */
-    public static void attachJvmtiAgent(@NonNull String library, @Nullable String options)
-            throws IOException {
+    public static void attachJvmtiAgent(@NonNull String library, @Nullable String options,
+            @Nullable ClassLoader classLoader) throws IOException {
         Preconditions.checkNotNull(library);
         Preconditions.checkArgument(!library.contains("="));
 
         if (options == null) {
-            VMDebug.attachAgent(library);
+            VMDebug.attachAgent(library, classLoader);
         } else {
-            VMDebug.attachAgent(library + "=" + options);
+            VMDebug.attachAgent(library + "=" + options, classLoader);
         }
     }
 }
diff --git a/core/java/android/os/IPermissionController.aidl b/core/java/android/os/IPermissionController.aidl
index 5e8590a..3de953a 100644
--- a/core/java/android/os/IPermissionController.aidl
+++ b/core/java/android/os/IPermissionController.aidl
@@ -22,4 +22,5 @@
     boolean checkPermission(String permission, int pid, int uid);
     String[] getPackagesForUid(int uid);
     boolean isRuntimePermission(String permission);
+    int getPackageUid(String packageName, int flags);
 }
diff --git a/core/java/android/os/ISystemUpdateManager.aidl b/core/java/android/os/ISystemUpdateManager.aidl
new file mode 100644
index 0000000..f7f5079
--- /dev/null
+++ b/core/java/android/os/ISystemUpdateManager.aidl
@@ -0,0 +1,27 @@
+/* //device/java/android/android/os/ISystemUpdateInfo.aidl
+**
+** Copyright 2018, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+package android.os;
+
+import android.os.Bundle;
+import android.os.PersistableBundle;
+
+/** @hide */
+interface ISystemUpdateManager {
+    Bundle retrieveSystemUpdateInfo();
+    void updateSystemUpdateInfo(in PersistableBundle data);
+}
diff --git a/core/java/android/os/Process.java b/core/java/android/os/Process.java
index e6cf5e6..7654e9b 100644
--- a/core/java/android/os/Process.java
+++ b/core/java/android/os/Process.java
@@ -151,6 +151,12 @@
      */
     public static final int OTA_UPDATE_UID = 1061;
 
+    /**
+     * Defines the UID used for incidentd.
+     * @hide
+     */
+    public static final int INCIDENTD_UID = 1067;
+
     /** {@hide} */
     public static final int NOBODY_UID = 9999;
 
diff --git a/core/java/android/os/RecoverySystem.java b/core/java/android/os/RecoverySystem.java
index 673a8ba..3e8e885 100644
--- a/core/java/android/os/RecoverySystem.java
+++ b/core/java/android/os/RecoverySystem.java
@@ -61,6 +61,7 @@
 import java.util.Locale;
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicBoolean;
 import java.util.zip.ZipEntry;
 import java.util.zip.ZipFile;
 import java.util.zip.ZipInputStream;
@@ -101,6 +102,9 @@
     private static final String ACTION_EUICC_FACTORY_RESET =
             "com.android.internal.action.EUICC_FACTORY_RESET";
 
+    /** used in {@link #wipeEuiccData} as package name of callback intent */
+    private static final String PACKAGE_NAME_WIPING_EUICC_DATA_CALLBACK = "android";
+
     /**
      * The recovery image uses this file to identify the location (i.e. blocks)
      * of an OTA package on the /data partition. The block map file is
@@ -751,7 +755,9 @@
         // Block until the ordered broadcast has completed.
         condition.block();
 
-        wipeEuiccData(context, wipeEuicc);
+        if (wipeEuicc) {
+            wipeEuiccData(context, PACKAGE_NAME_WIPING_EUICC_DATA_CALLBACK);
+        }
 
         String shutdownArg = null;
         if (shutdown) {
@@ -767,19 +773,27 @@
         bootCommand(context, shutdownArg, "--wipe_data", reasonArg, localeArg);
     }
 
-    private static void wipeEuiccData(Context context, final boolean isWipeEuicc) {
+    /**
+     * Returns whether wipe Euicc data successfully or not.
+     *
+     * @param packageName the package name of the caller app.
+     *
+     * @hide
+     */
+    public static boolean wipeEuiccData(Context context, final String packageName) {
         ContentResolver cr = context.getContentResolver();
         if (Settings.Global.getInt(cr, Settings.Global.EUICC_PROVISIONED, 0) == 0) {
             // If the eUICC isn't provisioned, there's no reason to either wipe or retain profiles,
             // as there's nothing to wipe nor retain.
             Log.d(TAG, "Skipping eUICC wipe/retain as it is not provisioned");
-            return;
+            return true;
         }
 
         EuiccManager euiccManager = (EuiccManager) context.getSystemService(
                 Context.EUICC_SERVICE);
         if (euiccManager != null && euiccManager.isEnabled()) {
             CountDownLatch euiccFactoryResetLatch = new CountDownLatch(1);
+            final AtomicBoolean wipingSucceeded = new AtomicBoolean(false);
 
             BroadcastReceiver euiccWipeFinishReceiver = new BroadcastReceiver() {
                 @Override
@@ -788,19 +802,11 @@
                         if (getResultCode() != EuiccManager.EMBEDDED_SUBSCRIPTION_RESULT_OK) {
                             int detailedCode = intent.getIntExtra(
                                     EuiccManager.EXTRA_EMBEDDED_SUBSCRIPTION_DETAILED_CODE, 0);
-                            if (isWipeEuicc) {
-                                Log.e(TAG, "Error wiping euicc data, Detailed code = "
-                                        + detailedCode);
-                            } else {
-                                Log.e(TAG, "Error retaining euicc data, Detailed code = "
-                                        + detailedCode);
-                            }
+                            Log.e(TAG, "Error wiping euicc data, Detailed code = "
+                                    + detailedCode);
                         } else {
-                            if (isWipeEuicc) {
-                                Log.d(TAG, "Successfully wiped euicc data.");
-                            } else {
-                                Log.d(TAG, "Successfully retained euicc data.");
-                            }
+                            Log.d(TAG, "Successfully wiped euicc data.");
+                            wipingSucceeded.set(true /* newValue */);
                         }
                         euiccFactoryResetLatch.countDown();
                     }
@@ -808,7 +814,7 @@
             };
 
             Intent intent = new Intent(ACTION_EUICC_FACTORY_RESET);
-            intent.setPackage("android");
+            intent.setPackage(packageName);
             PendingIntent callbackIntent = PendingIntent.getBroadcastAsUser(
                     context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT, UserHandle.SYSTEM);
             IntentFilter filterConsent = new IntentFilter();
@@ -818,11 +824,7 @@
             Handler euiccHandler = new Handler(euiccHandlerThread.getLooper());
             context.getApplicationContext()
                     .registerReceiver(euiccWipeFinishReceiver, filterConsent, null, euiccHandler);
-            if (isWipeEuicc) {
-                euiccManager.eraseSubscriptions(callbackIntent);
-            } else {
-                euiccManager.retainSubscriptionsForFactoryReset(callbackIntent);
-            }
+            euiccManager.eraseSubscriptions(callbackIntent);
             try {
                 long waitingTimeMillis = Settings.Global.getLong(
                         context.getContentResolver(),
@@ -834,22 +836,19 @@
                     waitingTimeMillis = MAX_EUICC_FACTORY_RESET_TIMEOUT_MILLIS;
                 }
                 if (!euiccFactoryResetLatch.await(waitingTimeMillis, TimeUnit.MILLISECONDS)) {
-                    if (isWipeEuicc) {
-                        Log.e(TAG, "Timeout wiping eUICC data.");
-                    } else {
-                        Log.e(TAG, "Timeout retaining eUICC data.");
-                    }
+                    Log.e(TAG, "Timeout wiping eUICC data.");
+                    return false;
                 }
-                context.getApplicationContext().unregisterReceiver(euiccWipeFinishReceiver);
             } catch (InterruptedException e) {
                 Thread.currentThread().interrupt();
-                if (isWipeEuicc) {
-                    Log.e(TAG, "Wiping eUICC data interrupted", e);
-                } else {
-                    Log.e(TAG, "Retaining eUICC data interrupted", e);
-                }
+                Log.e(TAG, "Wiping eUICC data interrupted", e);
+                return false;
+            } finally {
+                context.getApplicationContext().unregisterReceiver(euiccWipeFinishReceiver);
             }
+            return wipingSucceeded.get();
         }
+        return false;
     }
 
     /** {@hide} */
diff --git a/core/java/android/os/SystemProperties.java b/core/java/android/os/SystemProperties.java
index 4f6d322..a9b8675 100644
--- a/core/java/android/os/SystemProperties.java
+++ b/core/java/android/os/SystemProperties.java
@@ -18,6 +18,7 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.annotation.SystemApi;
 import android.util.Log;
 import android.util.MutableInt;
 
@@ -33,6 +34,7 @@
  *
  * {@hide}
  */
+@SystemApi
 public class SystemProperties {
     private static final String TAG = "SystemProperties";
     private static final boolean TRACK_KEY_ACCESS = false;
@@ -40,9 +42,11 @@
     /**
      * Android O removed the property name length limit, but com.amazon.kindle 7.8.1.5
      * uses reflection to read this whenever text is selected (http://b/36095274).
+     * @hide
      */
     public static final int PROP_NAME_MAX = Integer.MAX_VALUE;
 
+    /** @hide */
     public static final int PROP_VALUE_MAX = 91;
 
     @GuardedBy("sChangeCallbacks")
@@ -86,8 +90,10 @@
      *
      * @param key the key to lookup
      * @return an empty string if the {@code key} isn't found
+     * @hide
      */
     @NonNull
+    @SystemApi
     public static String get(@NonNull String key) {
         if (TRACK_KEY_ACCESS) onKeyAccess(key);
         return native_get(key);
@@ -100,8 +106,10 @@
      * @param def the default value in case the property is not set or empty
      * @return if the {@code key} isn't found, return {@code def} if it isn't null, or an empty
      * string otherwise
+     * @hide
      */
     @NonNull
+    @SystemApi
     public static String get(@NonNull String key, @Nullable String def) {
         if (TRACK_KEY_ACCESS) onKeyAccess(key);
         return native_get(key, def);
@@ -114,7 +122,9 @@
      * @param def a default value to return
      * @return the key parsed as an integer, or def if the key isn't found or
      *         cannot be parsed
+     * @hide
      */
+    @SystemApi
     public static int getInt(@NonNull String key, int def) {
         if (TRACK_KEY_ACCESS) onKeyAccess(key);
         return native_get_int(key, def);
@@ -127,7 +137,9 @@
      * @param def a default value to return
      * @return the key parsed as a long, or def if the key isn't found or
      *         cannot be parsed
+     * @hide
      */
+    @SystemApi
     public static long getLong(@NonNull String key, long def) {
         if (TRACK_KEY_ACCESS) onKeyAccess(key);
         return native_get_long(key, def);
@@ -145,7 +157,9 @@
      * @param def a default value to return
      * @return the key parsed as a boolean, or def if the key isn't found or is
      *         not able to be parsed as a boolean.
+     * @hide
      */
+    @SystemApi
     public static boolean getBoolean(@NonNull String key, boolean def) {
         if (TRACK_KEY_ACCESS) onKeyAccess(key);
         return native_get_boolean(key, def);
@@ -155,6 +169,7 @@
      * Set the value for the given {@code key} to {@code val}.
      *
      * @throws IllegalArgumentException if the {@code val} exceeds 91 characters
+     * @hide
      */
     public static void set(@NonNull String key, @Nullable String val) {
         if (val != null && !val.startsWith("ro.") && val.length() > PROP_VALUE_MAX) {
@@ -170,6 +185,7 @@
      *
      * @param callback The {@link Runnable} that should be executed when a system property
      * changes.
+     * @hide
      */
     public static void addChangeCallback(@NonNull Runnable callback) {
         synchronized (sChangeCallbacks) {
@@ -194,10 +210,14 @@
         }
     }
 
-    /*
+    /**
      * Notifies listeners that a system property has changed
+     * @hide
      */
     public static void reportSyspropChanged() {
         native_report_sysprop_change();
     }
+
+    private SystemProperties() {
+    }
 }
diff --git a/core/java/android/os/SystemUpdateManager.java b/core/java/android/os/SystemUpdateManager.java
new file mode 100644
index 0000000..ce3e2259
--- /dev/null
+++ b/core/java/android/os/SystemUpdateManager.java
@@ -0,0 +1,152 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.os;
+
+import static com.android.internal.util.Preconditions.checkNotNull;
+
+import android.annotation.RequiresPermission;
+import android.annotation.SystemApi;
+import android.annotation.SystemService;
+import android.content.Context;
+
+/**
+ * Allows querying and posting system update information.
+ *
+ * {@hide}
+ */
+@SystemApi
+@SystemService(Context.SYSTEM_UPDATE_SERVICE)
+public class SystemUpdateManager {
+    private static final String TAG = "SystemUpdateManager";
+
+    /** The status key of the system update info, expecting an int value. */
+    @SystemApi
+    public static final String KEY_STATUS = "status";
+
+    /** The title of the current update, expecting a String value. */
+    @SystemApi
+    public static final String KEY_TITLE = "title";
+
+    /** Whether it is a security update, expecting a boolean value. */
+    @SystemApi
+    public static final String KEY_IS_SECURITY_UPDATE = "is_security_update";
+
+    /** The build fingerprint after installing the current update, expecting a String value. */
+    @SystemApi
+    public static final String KEY_TARGET_BUILD_FINGERPRINT = "target_build_fingerprint";
+
+    /** The security patch level after installing the current update, expecting a String value. */
+    @SystemApi
+    public static final String KEY_TARGET_SECURITY_PATCH_LEVEL = "target_security_patch_level";
+
+    /**
+     * The KEY_STATUS value that indicates there's no update status info available.
+     */
+    @SystemApi
+    public static final int STATUS_UNKNOWN = 0;
+
+    /**
+     * The KEY_STATUS value that indicates there's no pending update.
+     */
+    @SystemApi
+    public static final int STATUS_IDLE = 1;
+
+    /**
+     * The KEY_STATUS value that indicates an update is available for download, but pending user
+     * approval to start.
+     */
+    @SystemApi
+    public static final int STATUS_WAITING_DOWNLOAD = 2;
+
+    /**
+     * The KEY_STATUS value that indicates an update is in progress (i.e. downloading or installing
+     * has started).
+     */
+    @SystemApi
+    public static final int STATUS_IN_PROGRESS = 3;
+
+    /**
+     * The KEY_STATUS value that indicates an update is available for install.
+     */
+    @SystemApi
+    public static final int STATUS_WAITING_INSTALL = 4;
+
+    /**
+     * The KEY_STATUS value that indicates an update will be installed after a reboot. This applies
+     * to both of A/B and non-A/B OTAs.
+     */
+    @SystemApi
+    public static final int STATUS_WAITING_REBOOT = 5;
+
+    private final ISystemUpdateManager mService;
+
+    /** @hide */
+    public SystemUpdateManager(ISystemUpdateManager service) {
+        mService = checkNotNull(service, "missing ISystemUpdateManager");
+    }
+
+    /**
+     * Queries the current pending system update info.
+     *
+     * <p>Requires the {@link android.Manifest.permission#READ_SYSTEM_UPDATE_INFO} or
+     * {@link android.Manifest.permission#RECOVERY} permission.
+     *
+     * @return A {@code Bundle} that contains the pending system update information in key-value
+     * pairs.
+     *
+     * @throws SecurityException if the caller is not allowed to read the info.
+     */
+    @SystemApi
+    @RequiresPermission(anyOf = {
+            android.Manifest.permission.READ_SYSTEM_UPDATE_INFO,
+            android.Manifest.permission.RECOVERY,
+    })
+    public Bundle retrieveSystemUpdateInfo() {
+        try {
+            return mService.retrieveSystemUpdateInfo();
+        } catch (RemoteException re) {
+            throw re.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Allows a system updater to publish the pending update info.
+     *
+     * <p>The reported info will not persist across reboots. Because only the reporting updater
+     * understands the criteria to determine a successful/failed update.
+     *
+     * <p>Requires the {@link android.Manifest.permission#RECOVERY} permission.
+     *
+     * @param infoBundle The {@code PersistableBundle} that contains the system update information,
+     * such as the current update status. {@link #KEY_STATUS} is required in the bundle.
+     *
+     * @throws IllegalArgumentException if @link #KEY_STATUS} does not exist.
+     * @throws SecurityException if the caller is not allowed to update the info.
+     */
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.RECOVERY)
+    public void updateSystemUpdateInfo(PersistableBundle infoBundle) {
+        if (infoBundle == null || !infoBundle.containsKey(KEY_STATUS)) {
+            throw new IllegalArgumentException("Missing status in the bundle");
+        }
+        try {
+            mService.updateSystemUpdateInfo(infoBundle);
+        } catch (RemoteException re) {
+            throw re.rethrowFromSystemServer();
+        }
+    }
+}
diff --git a/core/java/android/os/connectivity/GpsBatteryStats.aidl b/core/java/android/os/connectivity/GpsBatteryStats.aidl
new file mode 100644
index 0000000..7b96d1a
--- /dev/null
+++ b/core/java/android/os/connectivity/GpsBatteryStats.aidl
@@ -0,0 +1,20 @@
+/*
+ * Copyright (C) 2016 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.os.connectivity;
+
+/** {@hide} */
+parcelable GpsBatteryStats;
\ No newline at end of file
diff --git a/core/java/android/os/connectivity/GpsBatteryStats.java b/core/java/android/os/connectivity/GpsBatteryStats.java
new file mode 100644
index 0000000..f2ac5ef
--- /dev/null
+++ b/core/java/android/os/connectivity/GpsBatteryStats.java
@@ -0,0 +1,108 @@
+/*
+ * Copyright (C) 2016 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.os.connectivity;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import com.android.internal.location.gnssmetrics.GnssMetrics;
+
+import java.util.Arrays;
+
+/**
+ * API for GPS power stats
+ *
+ * @hide
+ */
+public final class GpsBatteryStats implements Parcelable {
+
+  private long mLoggingDurationMs;
+  private long mEnergyConsumedMaMs;
+  private long[] mTimeInGpsSignalQualityLevel;
+
+  public static final Parcelable.Creator<GpsBatteryStats> CREATOR = new
+      Parcelable.Creator<GpsBatteryStats>() {
+        public GpsBatteryStats createFromParcel(Parcel in) {
+          return new GpsBatteryStats(in);
+        }
+
+        public GpsBatteryStats[] newArray(int size) {
+          return new GpsBatteryStats[size];
+        }
+      };
+
+  public GpsBatteryStats() {
+    initialize();
+  }
+
+  @Override
+  public void writeToParcel(Parcel out, int flags) {
+    out.writeLong(mLoggingDurationMs);
+    out.writeLong(mEnergyConsumedMaMs);
+    out.writeLongArray(mTimeInGpsSignalQualityLevel);
+  }
+
+  public void readFromParcel(Parcel in) {
+    mLoggingDurationMs = in.readLong();
+    mEnergyConsumedMaMs = in.readLong();
+    in.readLongArray(mTimeInGpsSignalQualityLevel);
+  }
+
+  public long getLoggingDurationMs() {
+    return mLoggingDurationMs;
+  }
+
+  public long getEnergyConsumedMaMs() {
+    return mEnergyConsumedMaMs;
+  }
+
+  public long[] getTimeInGpsSignalQualityLevel() {
+    return mTimeInGpsSignalQualityLevel;
+  }
+
+  public void setLoggingDurationMs(long t) {
+    mLoggingDurationMs = t;
+    return;
+  }
+
+  public void setEnergyConsumedMaMs(long e) {
+    mEnergyConsumedMaMs = e;
+    return;
+  }
+
+  public void setTimeInGpsSignalQualityLevel(long[] t) {
+    mTimeInGpsSignalQualityLevel = Arrays.copyOfRange(t, 0,
+        Math.min(t.length, GnssMetrics.NUM_GPS_SIGNAL_QUALITY_LEVELS));
+    return;
+  }
+
+  @Override
+  public int describeContents() {
+    return 0;
+  }
+
+  private GpsBatteryStats(Parcel in) {
+    initialize();
+    readFromParcel(in);
+  }
+
+  private void initialize() {
+    mLoggingDurationMs = 0;
+    mEnergyConsumedMaMs = 0;
+    mTimeInGpsSignalQualityLevel = new long[GnssMetrics.NUM_GPS_SIGNAL_QUALITY_LEVELS];
+    return;
+  }
+}
\ No newline at end of file
diff --git a/core/java/android/os/connectivity/WifiBatteryStats.aidl b/core/java/android/os/connectivity/WifiBatteryStats.aidl
new file mode 100644
index 0000000..12ac738
--- /dev/null
+++ b/core/java/android/os/connectivity/WifiBatteryStats.aidl
@@ -0,0 +1,20 @@
+/*
+ * Copyright (C) 2016 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.os.connectivity;
+
+/** {@hide} */
+parcelable WifiBatteryStats;
\ No newline at end of file
diff --git a/core/java/android/os/connectivity/WifiBatteryStats.java b/core/java/android/os/connectivity/WifiBatteryStats.java
new file mode 100644
index 0000000..e5341ee
--- /dev/null
+++ b/core/java/android/os/connectivity/WifiBatteryStats.java
@@ -0,0 +1,279 @@
+/*
+ * Copyright (C) 2016 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.os.connectivity;
+
+import android.os.BatteryStats;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.util.Arrays;
+
+/**
+ * API for Wifi power stats
+ *
+ * @hide
+ */
+public final class WifiBatteryStats implements Parcelable {
+
+  private long mLoggingDurationMs;
+  private long mKernelActiveTimeMs;
+  private long mNumPacketsTx;
+  private long mNumBytesTx;
+  private long mNumPacketsRx;
+  private long mNumBytesRx;
+  private long mSleepTimeMs;
+  private long mScanTimeMs;
+  private long mIdleTimeMs;
+  private long mRxTimeMs;
+  private long mTxTimeMs;
+  private long mEnergyConsumedMaMs;
+  private long mNumAppScanRequest;
+  private long[] mTimeInStateMs;
+  private long[] mTimeInSupplicantStateMs;
+  private long[] mTimeInRxSignalStrengthLevelMs;
+
+  public static final Parcelable.Creator<WifiBatteryStats> CREATOR = new
+      Parcelable.Creator<WifiBatteryStats>() {
+        public WifiBatteryStats createFromParcel(Parcel in) {
+          return new WifiBatteryStats(in);
+        }
+
+        public WifiBatteryStats[] newArray(int size) {
+          return new WifiBatteryStats[size];
+        }
+      };
+
+  public WifiBatteryStats() {
+    initialize();
+  }
+
+  public void writeToParcel(Parcel out, int flags) {
+    out.writeLong(mLoggingDurationMs);
+    out.writeLong(mKernelActiveTimeMs);
+    out.writeLong(mNumPacketsTx);
+    out.writeLong(mNumBytesTx);
+    out.writeLong(mNumPacketsRx);
+    out.writeLong(mNumBytesRx);
+    out.writeLong(mSleepTimeMs);
+    out.writeLong(mScanTimeMs);
+    out.writeLong(mIdleTimeMs);
+    out.writeLong(mRxTimeMs);
+    out.writeLong(mTxTimeMs);
+    out.writeLong(mEnergyConsumedMaMs);
+    out.writeLong(mNumAppScanRequest);
+    out.writeLongArray(mTimeInStateMs);
+    out.writeLongArray(mTimeInRxSignalStrengthLevelMs);
+    out.writeLongArray(mTimeInSupplicantStateMs);
+  }
+
+  public void readFromParcel(Parcel in) {
+    mLoggingDurationMs = in.readLong();
+    mKernelActiveTimeMs = in.readLong();
+    mNumPacketsTx = in.readLong();
+    mNumBytesTx = in.readLong();
+    mNumPacketsRx = in.readLong();
+    mNumBytesRx = in.readLong();
+    mSleepTimeMs = in.readLong();
+    mScanTimeMs = in.readLong();
+    mIdleTimeMs = in.readLong();
+    mRxTimeMs = in.readLong();
+    mTxTimeMs = in.readLong();
+    mEnergyConsumedMaMs = in.readLong();
+    mNumAppScanRequest = in.readLong();
+    in.readLongArray(mTimeInStateMs);
+    in.readLongArray(mTimeInRxSignalStrengthLevelMs);
+    in.readLongArray(mTimeInSupplicantStateMs);
+  }
+
+  public long getLoggingDurationMs() {
+    return mLoggingDurationMs;
+  }
+
+  public long getKernelActiveTimeMs() {
+    return mKernelActiveTimeMs;
+  }
+
+  public long getNumPacketsTx() {
+    return mNumPacketsTx;
+  }
+
+  public long getNumBytesTx() {
+    return mNumBytesTx;
+  }
+
+  public long getNumPacketsRx() {
+    return mNumPacketsRx;
+  }
+
+  public long getNumBytesRx() {
+    return mNumBytesRx;
+  }
+
+  public long getSleepTimeMs() {
+    return mSleepTimeMs;
+  }
+
+  public long getScanTimeMs() {
+    return mScanTimeMs;
+  }
+
+  public long getIdleTimeMs() {
+    return mIdleTimeMs;
+  }
+
+  public long getRxTimeMs() {
+    return mRxTimeMs;
+  }
+
+  public long getTxTimeMs() {
+    return mTxTimeMs;
+  }
+
+  public long getEnergyConsumedMaMs() {
+    return mEnergyConsumedMaMs;
+  }
+
+  public long getNumAppScanRequest() {
+    return mNumAppScanRequest;
+  }
+
+  public long[] getTimeInStateMs() {
+    return mTimeInStateMs;
+  }
+
+  public long[] getTimeInRxSignalStrengthLevelMs() {
+    return mTimeInRxSignalStrengthLevelMs;
+  }
+
+  public long[] getTimeInSupplicantStateMs() {
+    return mTimeInSupplicantStateMs;
+  }
+
+  public void setLoggingDurationMs(long t) {
+    mLoggingDurationMs = t;
+    return;
+  }
+
+  public void setKernelActiveTimeMs(long t) {
+    mKernelActiveTimeMs = t;
+    return;
+  }
+
+  public void setNumPacketsTx(long n) {
+    mNumPacketsTx = n;
+    return;
+  }
+
+  public void setNumBytesTx(long b) {
+    mNumBytesTx = b;
+    return;
+  }
+
+  public void setNumPacketsRx(long n) {
+    mNumPacketsRx = n;
+    return;
+  }
+
+  public void setNumBytesRx(long b) {
+    mNumBytesRx = b;
+    return;
+  }
+
+  public void setSleepTimeMs(long t) {
+    mSleepTimeMs = t;
+    return;
+  }
+
+  public void setScanTimeMs(long t) {
+    mScanTimeMs = t;
+    return;
+  }
+
+  public void setIdleTimeMs(long t) {
+    mIdleTimeMs = t;
+    return;
+  }
+
+  public void setRxTimeMs(long t) {
+    mRxTimeMs = t;
+    return;
+  }
+
+  public void setTxTimeMs(long t) {
+    mTxTimeMs = t;
+    return;
+  }
+
+  public void setEnergyConsumedMaMs(long e) {
+    mEnergyConsumedMaMs = e;
+    return;
+  }
+
+  public void setNumAppScanRequest(long n) {
+    mNumAppScanRequest = n;
+    return;
+  }
+
+  public void setTimeInStateMs(long[] t) {
+    mTimeInStateMs = Arrays.copyOfRange(t, 0,
+        Math.min(t.length, BatteryStats.NUM_WIFI_STATES));
+    return;
+  }
+
+  public void setTimeInRxSignalStrengthLevelMs(long[] t) {
+    mTimeInRxSignalStrengthLevelMs = Arrays.copyOfRange(t, 0,
+        Math.min(t.length, BatteryStats.NUM_WIFI_SIGNAL_STRENGTH_BINS));
+    return;
+  }
+
+  public void setTimeInSupplicantStateMs(long[] t) {
+    mTimeInSupplicantStateMs = Arrays.copyOfRange(
+        t, 0, Math.min(t.length, BatteryStats.NUM_WIFI_SUPPL_STATES));
+    return;
+  }
+
+  public int describeContents() {
+    return 0;
+  }
+
+  private WifiBatteryStats(Parcel in) {
+    initialize();
+    readFromParcel(in);
+  }
+
+  private void initialize() {
+    mLoggingDurationMs = 0;
+    mKernelActiveTimeMs = 0;
+    mNumPacketsTx = 0;
+    mNumBytesTx = 0;
+    mNumPacketsRx = 0;
+    mNumBytesRx = 0;
+    mSleepTimeMs = 0;
+    mScanTimeMs = 0;
+    mIdleTimeMs = 0;
+    mRxTimeMs = 0;
+    mTxTimeMs = 0;
+    mEnergyConsumedMaMs = 0;
+    mNumAppScanRequest = 0;
+    mTimeInStateMs = new long[BatteryStats.NUM_WIFI_STATES];
+    Arrays.fill(mTimeInStateMs, 0);
+    mTimeInRxSignalStrengthLevelMs = new long[BatteryStats.NUM_WIFI_SIGNAL_STRENGTH_BINS];
+    Arrays.fill(mTimeInRxSignalStrengthLevelMs, 0);
+    mTimeInSupplicantStateMs = new long[BatteryStats.NUM_WIFI_SUPPL_STATES];
+    Arrays.fill(mTimeInSupplicantStateMs, 0);
+    return;
+  }
+}
\ No newline at end of file
diff --git a/core/java/android/privacy/internal/rappor/RapporEncoder.java b/core/java/android/privacy/internal/rappor/RapporEncoder.java
index 2eca4c98..9ac2b3e 100644
--- a/core/java/android/privacy/internal/rappor/RapporEncoder.java
+++ b/core/java/android/privacy/internal/rappor/RapporEncoder.java
@@ -33,7 +33,6 @@
 public class RapporEncoder implements DifferentialPrivacyEncoder {
 
     // Hard-coded seed and secret for insecure encoder
-    private static final long INSECURE_RANDOM_SEED = 0x12345678L;
     private static final byte[] INSECURE_SECRET = new byte[]{
             (byte) 0xD7, (byte) 0x68, (byte) 0x99, (byte) 0x93,
             (byte) 0x94, (byte) 0x13, (byte) 0x53, (byte) 0x54,
@@ -66,8 +65,8 @@
             // Use SecureRandom as random generator.
             random = sSecureRandom;
         } else {
-            // Hard-coded random generator, to have deterministic result.
-            random = new Random(INSECURE_RANDOM_SEED);
+            // To have deterministic result by hard coding encoder id as seed.
+            random = new Random((long) config.mEncoderId.hashCode());
             userSecret = INSECURE_SECRET;
         }
         mEncoder = new Encoder(random, null, null,
diff --git a/core/java/android/provider/AlarmClock.java b/core/java/android/provider/AlarmClock.java
index 2169457..7ad9e01 100644
--- a/core/java/android/provider/AlarmClock.java
+++ b/core/java/android/provider/AlarmClock.java
@@ -154,9 +154,12 @@
     public static final String ACTION_SET_TIMER = "android.intent.action.SET_TIMER";
 
     /**
-     * Activity Action: Dismiss timers.
+     * Activity Action: Dismiss a timer.
      * <p>
-     * Dismiss all currently expired timers. If there are no expired timers, then this is a no-op.
+     * The timer to dismiss should be specified using the Intent's data URI, which represents a
+     * deeplink to the timer.
+     * </p><p>
+     * If no data URI is provided, dismiss all expired timers.
      * </p>
      */
     @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
diff --git a/core/java/android/provider/CallLog.java b/core/java/android/provider/CallLog.java
index 60df467..c6c8d9d 100644
--- a/core/java/android/provider/CallLog.java
+++ b/core/java/android/provider/CallLog.java
@@ -223,14 +223,13 @@
         /** Call was WIFI call. */
         public static final int FEATURES_WIFI = 1 << 3;
 
-        /** Call was on RTT at some point */
-        public static final int FEATURES_RTT = 1 << 4;
-
         /**
          * Indicates the call underwent Assisted Dialing.
-         * @hide
          */
-        public static final Integer FEATURES_ASSISTED_DIALING_USED = 0x10;
+        public static final int FEATURES_ASSISTED_DIALING_USED = 1 << 4;
+
+        /** Call was on RTT at some point */
+        public static final int FEATURES_RTT = 1 << 5;
 
         /**
          * The phone number as the user entered it.
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 3a4a52a..0e323f8 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -16,10 +16,12 @@
 
 package android.provider;
 
+import static android.provider.SettingsValidators.ANY_INTEGER_VALIDATOR;
 import static android.provider.SettingsValidators.ANY_STRING_VALIDATOR;
 import static android.provider.SettingsValidators.BOOLEAN_VALIDATOR;
 import static android.provider.SettingsValidators.COMPONENT_NAME_VALIDATOR;
 import static android.provider.SettingsValidators.LENIENT_IP_ADDRESS_VALIDATOR;
+import static android.provider.SettingsValidators.LOCALE_VALIDATOR;
 import static android.provider.SettingsValidators.NON_NEGATIVE_INTEGER_VALIDATOR;
 import static android.provider.SettingsValidators.PACKAGE_NAME_VALIDATOR;
 import static android.provider.SettingsValidators.URI_VALIDATOR;
@@ -4008,6 +4010,9 @@
          * Keys we no longer back up under the current schema, but want to continue to
          * process when restoring historical backup datasets.
          *
+         * All settings in {@link LEGACY_RESTORE_SETTINGS} array *must* have a non-null validator,
+         * otherwise they won't be restored.
+         *
          * @hide
          */
         public static final String[] LEGACY_RESTORE_SETTINGS = {
@@ -4116,6 +4121,9 @@
         /**
          * These are all public system settings
          *
+         * All settings in {@link SETTINGS_TO_BACKUP} array *must* have a non-null validator,
+         * otherwise they won't be restored.
+         *
          * @hide
          */
         public static final Map<String, Validator> VALIDATORS = new ArrayMap<>();
@@ -4391,8 +4399,8 @@
         public static final String WIFI_NETWORKS_AVAILABLE_NOTIFICATION_ON =
                 Global.WIFI_NETWORKS_AVAILABLE_NOTIFICATION_ON;
 
-       private static final Validator WIFI_NETWORKS_AVAILABLE_NOTIFICATION_ON_VALIDATOR =
-               BOOLEAN_VALIDATOR;
+        private static final Validator WIFI_NETWORKS_AVAILABLE_NOTIFICATION_ON_VALIDATOR =
+                BOOLEAN_VALIDATOR;
 
         /**
          * @deprecated Use
@@ -4402,6 +4410,9 @@
         public static final String WIFI_NETWORKS_AVAILABLE_REPEAT_DELAY =
                 Global.WIFI_NETWORKS_AVAILABLE_REPEAT_DELAY;
 
+        private static final Validator WIFI_NETWORKS_AVAILABLE_REPEAT_DELAY_VALIDATOR =
+                NON_NEGATIVE_INTEGER_VALIDATOR;
+
         /**
          * @deprecated Use {@link android.provider.Settings.Global#WIFI_NUM_OPEN_NETWORKS_KEPT}
          * instead
@@ -4409,6 +4420,9 @@
         @Deprecated
         public static final String WIFI_NUM_OPEN_NETWORKS_KEPT = Global.WIFI_NUM_OPEN_NETWORKS_KEPT;
 
+        private static final Validator WIFI_NUM_OPEN_NETWORKS_KEPT_VALIDATOR =
+                NON_NEGATIVE_INTEGER_VALIDATOR;
+
         /**
          * @deprecated Use {@link android.provider.Settings.Global#WIFI_ON} instead
          */
@@ -5190,6 +5204,8 @@
         @Deprecated
         public static final String ALLOW_MOCK_LOCATION = "mock_location";
 
+        private static final Validator ALLOW_MOCK_LOCATION_VALIDATOR = BOOLEAN_VALIDATOR;
+
         /**
          * On Android 8.0 (API level 26) and higher versions of the platform,
          * a 64-bit number (expressed as a hexadecimal string), unique to
@@ -5284,6 +5300,8 @@
         @TestApi
         public static final String AUTOFILL_SERVICE = "autofill_service";
 
+        private static final Validator AUTOFILL_SERVICE_VALIDATOR = COMPONENT_NAME_VALIDATOR;
+
         /**
          * Boolean indicating if Autofill supports field classification.
          *
@@ -5370,9 +5388,38 @@
          * List of input methods that are currently enabled.  This is a string
          * containing the IDs of all enabled input methods, each ID separated
          * by ':'.
+         *
+         * Format like "ime0;subtype0;subtype1;subtype2:ime1:ime2;subtype0"
+         * where imeId is ComponentName and subtype is int32.
          */
         public static final String ENABLED_INPUT_METHODS = "enabled_input_methods";
 
+        private static final Validator ENABLED_INPUT_METHODS_VALIDATOR = new Validator() {
+            @Override
+            public boolean validate(String value) {
+                if (value == null) {
+                    return false;
+                }
+                String[] inputMethods = value.split(":");
+                boolean valid = true;
+                for (String inputMethod : inputMethods) {
+                    if (inputMethod.length() == 0) {
+                        return false;
+                    }
+                    String[] subparts = inputMethod.split(";");
+                    for (String subpart : subparts) {
+                        // allow either a non negative integer or a ComponentName
+                        valid |= (NON_NEGATIVE_INTEGER_VALIDATOR.validate(subpart)
+                                || COMPONENT_NAME_VALIDATOR.validate(subpart));
+                    }
+                    if (!valid) {
+                        return false;
+                    }
+                }
+                return valid;
+            }
+        };
+
         /**
          * List of system input methods that are currently disabled.  This is a string
          * containing the IDs of all disabled input methods, each ID separated
@@ -5388,6 +5435,8 @@
          */
         public static final String SHOW_IME_WITH_HARD_KEYBOARD = "show_ime_with_hard_keyboard";
 
+        private static final Validator SHOW_IME_WITH_HARD_KEYBOARD_VALIDATOR = BOOLEAN_VALIDATOR;
+
         /**
          * Host name and port for global http proxy. Uses ':' seperator for
          * between host and port.
@@ -5677,6 +5726,8 @@
          */
         public static final String ACCESSIBILITY_ENABLED = "accessibility_enabled";
 
+        private static final Validator ACCESSIBILITY_ENABLED_VALIDATOR = BOOLEAN_VALIDATOR;
+
         /**
          * Setting specifying if the accessibility shortcut is enabled.
          * @hide
@@ -5684,6 +5735,8 @@
         public static final String ACCESSIBILITY_SHORTCUT_ENABLED =
                 "accessibility_shortcut_enabled";
 
+        private static final Validator ACCESSIBILITY_SHORTCUT_ENABLED_VALIDATOR = BOOLEAN_VALIDATOR;
+
         /**
          * Setting specifying if the accessibility shortcut is enabled.
          * @hide
@@ -5691,6 +5744,9 @@
         public static final String ACCESSIBILITY_SHORTCUT_ON_LOCK_SCREEN =
                 "accessibility_shortcut_on_lock_screen";
 
+        private static final Validator ACCESSIBILITY_SHORTCUT_ON_LOCK_SCREEN_VALIDATOR =
+                BOOLEAN_VALIDATOR;
+
         /**
          * Setting specifying if the accessibility shortcut dialog has been shown to this user.
          * @hide
@@ -5698,6 +5754,9 @@
         public static final String ACCESSIBILITY_SHORTCUT_DIALOG_SHOWN =
                 "accessibility_shortcut_dialog_shown";
 
+        private static final Validator ACCESSIBILITY_SHORTCUT_DIALOG_SHOWN_VALIDATOR =
+                BOOLEAN_VALIDATOR;
+
         /**
          * Setting specifying the accessibility service to be toggled via the accessibility
          * shortcut. Must be its flattened {@link ComponentName}.
@@ -5706,6 +5765,9 @@
         public static final String ACCESSIBILITY_SHORTCUT_TARGET_SERVICE =
                 "accessibility_shortcut_target_service";
 
+        private static final Validator ACCESSIBILITY_SHORTCUT_TARGET_SERVICE_VALIDATOR =
+                COMPONENT_NAME_VALIDATOR;
+
         /**
          * Setting specifying the accessibility service or feature to be toggled via the
          * accessibility button in the navigation bar. This is either a flattened
@@ -5716,17 +5778,32 @@
         public static final String ACCESSIBILITY_BUTTON_TARGET_COMPONENT =
                 "accessibility_button_target_component";
 
+        private static final Validator ACCESSIBILITY_BUTTON_TARGET_COMPONENT_VALIDATOR =
+                new Validator() {
+                    @Override
+                    public boolean validate(String value) {
+                        // technically either ComponentName or class name, but there's proper value
+                        // validation at callsites, so allow any non-null string
+                        return value != null;
+                    }
+                };
+
         /**
          * If touch exploration is enabled.
          */
         public static final String TOUCH_EXPLORATION_ENABLED = "touch_exploration_enabled";
 
+        private static final Validator TOUCH_EXPLORATION_ENABLED_VALIDATOR = BOOLEAN_VALIDATOR;
+
         /**
          * List of the enabled accessibility providers.
          */
         public static final String ENABLED_ACCESSIBILITY_SERVICES =
             "enabled_accessibility_services";
 
+        private static final Validator ENABLED_ACCESSIBILITY_SERVICES_VALIDATOR =
+                new SettingsValidators.ComponentNameListValidator(":");
+
         /**
          * List of the accessibility services to which the user has granted
          * permission to put the device into touch exploration mode.
@@ -5736,6 +5813,9 @@
         public static final String TOUCH_EXPLORATION_GRANTED_ACCESSIBILITY_SERVICES =
             "touch_exploration_granted_accessibility_services";
 
+        private static final Validator TOUCH_EXPLORATION_GRANTED_ACCESSIBILITY_SERVICES_VALIDATOR =
+                new SettingsValidators.ComponentNameListValidator(":");
+
         /**
          * Uri of the slice that's presented on the keyguard.
          * Defaults to a slice with the date and next alarm.
@@ -5754,6 +5834,8 @@
         @Deprecated
         public static final String ACCESSIBILITY_SPEAK_PASSWORD = "speak_password";
 
+        private static final Validator ACCESSIBILITY_SPEAK_PASSWORD_VALIDATOR = BOOLEAN_VALIDATOR;
+
         /**
          * Whether to draw text with high contrast while in accessibility mode.
          *
@@ -5762,6 +5844,9 @@
         public static final String ACCESSIBILITY_HIGH_TEXT_CONTRAST_ENABLED =
                 "high_text_contrast_enabled";
 
+        private static final Validator ACCESSIBILITY_HIGH_TEXT_CONTRAST_ENABLED_VALIDATOR =
+                BOOLEAN_VALIDATOR;
+
         /**
          * Setting that specifies whether the display magnification is enabled via a system-wide
          * triple tap gesture. Display magnifications allows the user to zoom in the display content
@@ -5774,6 +5859,9 @@
         public static final String ACCESSIBILITY_DISPLAY_MAGNIFICATION_ENABLED =
                 "accessibility_display_magnification_enabled";
 
+        private static final Validator ACCESSIBILITY_DISPLAY_MAGNIFICATION_ENABLED_VALIDATOR =
+                BOOLEAN_VALIDATOR;
+
         /**
          * Setting that specifies whether the display magnification is enabled via a shortcut
          * affordance within the system's navigation area. Display magnifications allows the user to
@@ -5785,6 +5873,9 @@
         public static final String ACCESSIBILITY_DISPLAY_MAGNIFICATION_NAVBAR_ENABLED =
                 "accessibility_display_magnification_navbar_enabled";
 
+        private static final Validator ACCESSIBILITY_DISPLAY_MAGNIFICATION_NAVBAR_ENABLED_VALIDATOR
+                = BOOLEAN_VALIDATOR;
+
         /**
          * Setting that specifies what the display magnification scale is.
          * Display magnifications allows the user to zoom in the display
@@ -5798,6 +5889,9 @@
         public static final String ACCESSIBILITY_DISPLAY_MAGNIFICATION_SCALE =
                 "accessibility_display_magnification_scale";
 
+        private static final Validator ACCESSIBILITY_DISPLAY_MAGNIFICATION_SCALE_VALIDATOR =
+                new SettingsValidators.InclusiveFloatRangeValidator(1.0f, Float.MAX_VALUE);
+
         /**
          * Unused mangnification setting
          *
@@ -5850,6 +5944,9 @@
         public static final String ACCESSIBILITY_CAPTIONING_ENABLED =
                 "accessibility_captioning_enabled";
 
+        private static final Validator ACCESSIBILITY_CAPTIONING_ENABLED_VALIDATOR =
+                BOOLEAN_VALIDATOR;
+
         /**
          * Setting that specifies the language for captions as a locale string,
          * e.g. en_US.
@@ -5860,6 +5957,8 @@
         public static final String ACCESSIBILITY_CAPTIONING_LOCALE =
                 "accessibility_captioning_locale";
 
+        private static final Validator ACCESSIBILITY_CAPTIONING_LOCALE_VALIDATOR = LOCALE_VALIDATOR;
+
         /**
          * Integer property that specifies the preset style for captions, one
          * of:
@@ -5874,6 +5973,10 @@
         public static final String ACCESSIBILITY_CAPTIONING_PRESET =
                 "accessibility_captioning_preset";
 
+        private static final Validator ACCESSIBILITY_CAPTIONING_PRESET_VALIDATOR =
+                new SettingsValidators.DiscreteValueValidator(new String[]{"-1", "0", "1", "2",
+                        "3", "4"});
+
         /**
          * Integer property that specifes the background color for captions as a
          * packed 32-bit color.
@@ -5884,6 +5987,9 @@
         public static final String ACCESSIBILITY_CAPTIONING_BACKGROUND_COLOR =
                 "accessibility_captioning_background_color";
 
+        private static final Validator ACCESSIBILITY_CAPTIONING_BACKGROUND_COLOR_VALIDATOR =
+                ANY_INTEGER_VALIDATOR;
+
         /**
          * Integer property that specifes the foreground color for captions as a
          * packed 32-bit color.
@@ -5894,6 +6000,9 @@
         public static final String ACCESSIBILITY_CAPTIONING_FOREGROUND_COLOR =
                 "accessibility_captioning_foreground_color";
 
+        private static final Validator ACCESSIBILITY_CAPTIONING_FOREGROUND_COLOR_VALIDATOR =
+                ANY_INTEGER_VALIDATOR;
+
         /**
          * Integer property that specifes the edge type for captions, one of:
          * <ul>
@@ -5908,6 +6017,9 @@
         public static final String ACCESSIBILITY_CAPTIONING_EDGE_TYPE =
                 "accessibility_captioning_edge_type";
 
+        private static final Validator ACCESSIBILITY_CAPTIONING_EDGE_TYPE_VALIDATOR =
+                new SettingsValidators.DiscreteValueValidator(new String[]{"0", "1", "2"});
+
         /**
          * Integer property that specifes the edge color for captions as a
          * packed 32-bit color.
@@ -5919,6 +6031,9 @@
         public static final String ACCESSIBILITY_CAPTIONING_EDGE_COLOR =
                 "accessibility_captioning_edge_color";
 
+        private static final Validator ACCESSIBILITY_CAPTIONING_EDGE_COLOR_VALIDATOR =
+                ANY_INTEGER_VALIDATOR;
+
         /**
          * Integer property that specifes the window color for captions as a
          * packed 32-bit color.
@@ -5929,6 +6044,9 @@
         public static final String ACCESSIBILITY_CAPTIONING_WINDOW_COLOR =
                 "accessibility_captioning_window_color";
 
+        private static final Validator ACCESSIBILITY_CAPTIONING_WINDOW_COLOR_VALIDATOR =
+                ANY_INTEGER_VALIDATOR;
+
         /**
          * String property that specifies the typeface for captions, one of:
          * <ul>
@@ -5944,6 +6062,10 @@
         public static final String ACCESSIBILITY_CAPTIONING_TYPEFACE =
                 "accessibility_captioning_typeface";
 
+        private static final Validator ACCESSIBILITY_CAPTIONING_TYPEFACE_VALIDATOR =
+                new SettingsValidators.DiscreteValueValidator(new String[]{"DEFAULT",
+                        "MONOSPACE", "SANS_SERIF", "SERIF"});
+
         /**
          * Floating point property that specifies font scaling for captions.
          *
@@ -5952,12 +6074,18 @@
         public static final String ACCESSIBILITY_CAPTIONING_FONT_SCALE =
                 "accessibility_captioning_font_scale";
 
+        private static final Validator ACCESSIBILITY_CAPTIONING_FONT_SCALE_VALIDATOR =
+                new SettingsValidators.InclusiveFloatRangeValidator(0.5f, 2.0f);
+
         /**
          * Setting that specifies whether display color inversion is enabled.
          */
         public static final String ACCESSIBILITY_DISPLAY_INVERSION_ENABLED =
                 "accessibility_display_inversion_enabled";
 
+        private static final Validator ACCESSIBILITY_DISPLAY_INVERSION_ENABLED_VALIDATOR =
+                BOOLEAN_VALIDATOR;
+
         /**
          * Setting that specifies whether display color space adjustment is
          * enabled.
@@ -5967,15 +6095,24 @@
         public static final String ACCESSIBILITY_DISPLAY_DALTONIZER_ENABLED =
                 "accessibility_display_daltonizer_enabled";
 
+        private static final Validator ACCESSIBILITY_DISPLAY_DALTONIZER_ENABLED_VALIDATOR =
+                BOOLEAN_VALIDATOR;
+
         /**
          * Integer property that specifies the type of color space adjustment to
-         * perform. Valid values are defined in AccessibilityManager.
+         * perform. Valid values are defined in AccessibilityManager:
+         * - AccessibilityManager.DALTONIZER_DISABLED = -1
+         * - AccessibilityManager.DALTONIZER_SIMULATE_MONOCHROMACY = 0
+         * - AccessibilityManager.DALTONIZER_CORRECT_DEUTERANOMALY = 12
          *
          * @hide
          */
         public static final String ACCESSIBILITY_DISPLAY_DALTONIZER =
                 "accessibility_display_daltonizer";
 
+        private static final Validator ACCESSIBILITY_DISPLAY_DALTONIZER_VALIDATOR =
+                new SettingsValidators.DiscreteValueValidator(new String[] {"-1", "0", "12"});
+
         /**
          * Setting that specifies whether automatic click when the mouse pointer stops moving is
          * enabled.
@@ -5985,6 +6122,9 @@
         public static final String ACCESSIBILITY_AUTOCLICK_ENABLED =
                 "accessibility_autoclick_enabled";
 
+        private static final Validator ACCESSIBILITY_AUTOCLICK_ENABLED_VALIDATOR =
+                BOOLEAN_VALIDATOR;
+
         /**
          * Integer setting specifying amount of time in ms the mouse pointer has to stay still
          * before performing click when {@link #ACCESSIBILITY_AUTOCLICK_ENABLED} is set.
@@ -5995,6 +6135,9 @@
         public static final String ACCESSIBILITY_AUTOCLICK_DELAY =
                 "accessibility_autoclick_delay";
 
+        private static final Validator ACCESSIBILITY_AUTOCLICK_DELAY_VALIDATOR =
+                NON_NEGATIVE_INTEGER_VALIDATOR;
+
         /**
          * Whether or not larger size icons are used for the pointer of mouse/trackpad for
          * accessibility.
@@ -6004,12 +6147,18 @@
         public static final String ACCESSIBILITY_LARGE_POINTER_ICON =
                 "accessibility_large_pointer_icon";
 
+        private static final Validator ACCESSIBILITY_LARGE_POINTER_ICON_VALIDATOR =
+                BOOLEAN_VALIDATOR;
+
         /**
          * The timeout for considering a press to be a long press in milliseconds.
          * @hide
          */
         public static final String LONG_PRESS_TIMEOUT = "long_press_timeout";
 
+        private static final Validator LONG_PRESS_TIMEOUT_VALIDATOR =
+                NON_NEGATIVE_INTEGER_VALIDATOR;
+
         /**
          * The duration in milliseconds between the first tap's up event and the second tap's
          * down event for an interaction to be considered part of the same multi-press.
@@ -6063,16 +6212,22 @@
          */
         public static final String TTS_DEFAULT_RATE = "tts_default_rate";
 
+        private static final Validator TTS_DEFAULT_RATE_VALIDATOR = NON_NEGATIVE_INTEGER_VALIDATOR;
+
         /**
          * Default text-to-speech engine pitch. 100 = 1x
          */
         public static final String TTS_DEFAULT_PITCH = "tts_default_pitch";
 
+        private static final Validator TTS_DEFAULT_PITCH_VALIDATOR = NON_NEGATIVE_INTEGER_VALIDATOR;
+
         /**
          * Default text-to-speech engine.
          */
         public static final String TTS_DEFAULT_SYNTH = "tts_default_synth";
 
+        private static final Validator TTS_DEFAULT_SYNTH_VALIDATOR = PACKAGE_NAME_VALIDATOR;
+
         /**
          * Default text-to-speech language.
          *
@@ -6120,11 +6275,33 @@
          */
         public static final String TTS_DEFAULT_LOCALE = "tts_default_locale";
 
+        private static final Validator TTS_DEFAULT_LOCALE_VALIDATOR = new Validator() {
+            @Override
+            public boolean validate(String value) {
+                if (value == null || value.length() == 0) {
+                    return false;
+                }
+                String[] ttsLocales = value.split(",");
+                boolean valid = true;
+                for (String ttsLocale : ttsLocales) {
+                    String[] parts = ttsLocale.split(":");
+                    valid |= ((parts.length == 2)
+                            && (parts[0].length() > 0)
+                            && ANY_STRING_VALIDATOR.validate(parts[0])
+                            && LOCALE_VALIDATOR.validate(parts[1]));
+                }
+                return valid;
+            }
+        };
+
         /**
          * Space delimited list of plugin packages that are enabled.
          */
         public static final String TTS_ENABLED_PLUGINS = "tts_enabled_plugins";
 
+        private static final Validator TTS_ENABLED_PLUGINS_VALIDATOR =
+                new SettingsValidators.PackageNameListValidator(" ");
+
         /**
          * @deprecated Use {@link android.provider.Settings.Global#WIFI_NETWORKS_AVAILABLE_NOTIFICATION_ON}
          * instead.
@@ -6133,8 +6310,8 @@
         public static final String WIFI_NETWORKS_AVAILABLE_NOTIFICATION_ON =
                 Global.WIFI_NETWORKS_AVAILABLE_NOTIFICATION_ON;
 
-       private static final Validator WIFI_NETWORKS_AVAILABLE_NOTIFICATION_ON_VALIDATOR =
-               BOOLEAN_VALIDATOR;
+        private static final Validator WIFI_NETWORKS_AVAILABLE_NOTIFICATION_ON_VALIDATOR =
+                BOOLEAN_VALIDATOR;
 
         /**
          * @deprecated Use {@link android.provider.Settings.Global#WIFI_NETWORKS_AVAILABLE_REPEAT_DELAY}
@@ -6144,6 +6321,9 @@
         public static final String WIFI_NETWORKS_AVAILABLE_REPEAT_DELAY =
                 Global.WIFI_NETWORKS_AVAILABLE_REPEAT_DELAY;
 
+        private static final Validator WIFI_NETWORKS_AVAILABLE_REPEAT_DELAY_VALIDATOR =
+                NON_NEGATIVE_INTEGER_VALIDATOR;
+
         /**
          * @deprecated Use {@link android.provider.Settings.Global#WIFI_NUM_OPEN_NETWORKS_KEPT}
          * instead.
@@ -6152,6 +6332,9 @@
         public static final String WIFI_NUM_OPEN_NETWORKS_KEPT =
                 Global.WIFI_NUM_OPEN_NETWORKS_KEPT;
 
+        private static final Validator WIFI_NUM_OPEN_NETWORKS_KEPT_VALIDATOR =
+                NON_NEGATIVE_INTEGER_VALIDATOR;
+
         /**
          * @deprecated Use {@link android.provider.Settings.Global#WIFI_ON}
          * instead.
@@ -6310,6 +6493,9 @@
         public static final String PREFERRED_TTY_MODE =
                 "preferred_tty_mode";
 
+        private static final Validator PREFERRED_TTY_MODE_VALIDATOR =
+                new SettingsValidators.DiscreteValueValidator(new String[]{"0", "1", "2", "3"});
+
         /**
          * Whether the enhanced voice privacy mode is enabled.
          * 0 = normal voice privacy
@@ -6318,6 +6504,8 @@
          */
         public static final String ENHANCED_VOICE_PRIVACY_ENABLED = "enhanced_voice_privacy_enabled";
 
+        private static final Validator ENHANCED_VOICE_PRIVACY_ENABLED_VALIDATOR = BOOLEAN_VALIDATOR;
+
         /**
          * Whether the TTY mode mode is enabled.
          * 0 = disabled
@@ -6326,6 +6514,8 @@
          */
         public static final String TTY_MODE_ENABLED = "tty_mode_enabled";
 
+        private static final Validator TTY_MODE_ENABLED_VALIDATOR = BOOLEAN_VALIDATOR;
+
         /**
          * Controls whether settings backup is enabled.
          * Type: int ( 0 = disabled, 1 = enabled )
@@ -6496,24 +6686,32 @@
          */
         public static final String MOUNT_PLAY_NOTIFICATION_SND = "mount_play_not_snd";
 
+        private static final Validator MOUNT_PLAY_NOTIFICATION_SND_VALIDATOR = BOOLEAN_VALIDATOR;
+
         /**
          * Whether or not UMS auto-starts on UMS host detection. (0 = false, 1 = true)
          * @hide
          */
         public static final String MOUNT_UMS_AUTOSTART = "mount_ums_autostart";
 
+        private static final Validator MOUNT_UMS_AUTOSTART_VALIDATOR = BOOLEAN_VALIDATOR;
+
         /**
          * Whether or not a notification is displayed on UMS host detection. (0 = false, 1 = true)
          * @hide
          */
         public static final String MOUNT_UMS_PROMPT = "mount_ums_prompt";
 
+        private static final Validator MOUNT_UMS_PROMPT_VALIDATOR = BOOLEAN_VALIDATOR;
+
         /**
          * Whether or not a notification is displayed while UMS is enabled. (0 = false, 1 = true)
          * @hide
          */
         public static final String MOUNT_UMS_NOTIFY_ENABLED = "mount_ums_notify_enabled";
 
+        private static final Validator MOUNT_UMS_NOTIFY_ENABLED_VALIDATOR = BOOLEAN_VALIDATOR;
+
         /**
          * If nonzero, ANRs in invisible background processes bring up a dialog.
          * Otherwise, the process will be silently killed.
@@ -6531,6 +6729,9 @@
         public static final String SHOW_FIRST_CRASH_DIALOG_DEV_OPTION =
                 "show_first_crash_dialog_dev_option";
 
+        private static final Validator SHOW_FIRST_CRASH_DIALOG_DEV_OPTION_VALIDATOR =
+                BOOLEAN_VALIDATOR;
+
         /**
          * The {@link ComponentName} string of the service to be used as the voice recognition
          * service.
@@ -6556,6 +6757,8 @@
          */
         public static final String SELECTED_SPELL_CHECKER = "selected_spell_checker";
 
+        private static final Validator SELECTED_SPELL_CHECKER_VALIDATOR = COMPONENT_NAME_VALIDATOR;
+
         /**
          * The {@link ComponentName} string of the selected subtype of the selected spell checker
          * service which is one of the services managed by the text service manager.
@@ -6565,13 +6768,18 @@
         public static final String SELECTED_SPELL_CHECKER_SUBTYPE =
                 "selected_spell_checker_subtype";
 
+        private static final Validator SELECTED_SPELL_CHECKER_SUBTYPE_VALIDATOR =
+                COMPONENT_NAME_VALIDATOR;
+
         /**
-         * The {@link ComponentName} string whether spell checker is enabled or not.
+         * Whether spell checker is enabled or not.
          *
          * @hide
          */
         public static final String SPELL_CHECKER_ENABLED = "spell_checker_enabled";
 
+        private static final Validator SPELL_CHECKER_ENABLED_VALIDATOR = BOOLEAN_VALIDATOR;
+
         /**
          * What happens when the user presses the Power button while in-call
          * and the screen is on.<br/>
@@ -6583,6 +6791,9 @@
          */
         public static final String INCALL_POWER_BUTTON_BEHAVIOR = "incall_power_button_behavior";
 
+        private static final Validator INCALL_POWER_BUTTON_BEHAVIOR_VALIDATOR =
+                new SettingsValidators.DiscreteValueValidator(new String[]{"1", "2"});
+
         /**
          * INCALL_POWER_BUTTON_BEHAVIOR value for "turn off screen".
          * @hide
@@ -6638,12 +6849,16 @@
          */
         public static final String WAKE_GESTURE_ENABLED = "wake_gesture_enabled";
 
+        private static final Validator WAKE_GESTURE_ENABLED_VALIDATOR = BOOLEAN_VALIDATOR;
+
         /**
          * Whether the device should doze if configured.
          * @hide
          */
         public static final String DOZE_ENABLED = "doze_enabled";
 
+        private static final Validator DOZE_ENABLED_VALIDATOR = BOOLEAN_VALIDATOR;
+
         /**
          * Whether doze should be always on.
          * @hide
@@ -6656,6 +6871,8 @@
          */
         public static final String DOZE_PULSE_ON_PICK_UP = "doze_pulse_on_pick_up";
 
+        private static final Validator DOZE_PULSE_ON_PICK_UP_VALIDATOR = BOOLEAN_VALIDATOR;
+
         /**
          * Whether the device should pulse on long press gesture.
          * @hide
@@ -6668,6 +6885,8 @@
          */
         public static final String DOZE_PULSE_ON_DOUBLE_TAP = "doze_pulse_on_double_tap";
 
+        private static final Validator DOZE_PULSE_ON_DOUBLE_TAP_VALIDATOR = BOOLEAN_VALIDATOR;
+
         /**
          * The current night mode that has been selected by the user.  Owned
          * and controlled by UiModeManagerService.  Constants are as per
@@ -6682,6 +6901,8 @@
          */
         public static final String SCREENSAVER_ENABLED = "screensaver_enabled";
 
+        private static final Validator SCREENSAVER_ENABLED_VALIDATOR = BOOLEAN_VALIDATOR;
+
         /**
          * The user's chosen screensaver components.
          *
@@ -6691,6 +6912,9 @@
          */
         public static final String SCREENSAVER_COMPONENTS = "screensaver_components";
 
+        private static final Validator SCREENSAVER_COMPONENTS_VALIDATOR =
+                new SettingsValidators.ComponentNameListValidator(",");
+
         /**
          * If screensavers are enabled, whether the screensaver should be automatically launched
          * when the device is inserted into a (desk) dock.
@@ -6698,6 +6922,8 @@
          */
         public static final String SCREENSAVER_ACTIVATE_ON_DOCK = "screensaver_activate_on_dock";
 
+        private static final Validator SCREENSAVER_ACTIVATE_ON_DOCK_VALIDATOR = BOOLEAN_VALIDATOR;
+
         /**
          * If screensavers are enabled, whether the screensaver should be automatically launched
          * when the screen times out when not on battery.
@@ -6705,6 +6931,8 @@
          */
         public static final String SCREENSAVER_ACTIVATE_ON_SLEEP = "screensaver_activate_on_sleep";
 
+        private static final Validator SCREENSAVER_ACTIVATE_ON_SLEEP_VALIDATOR = BOOLEAN_VALIDATOR;
+
         /**
          * If screensavers are enabled, the default screensaver component.
          * @hide
@@ -6717,6 +6945,9 @@
          */
         public static final String NFC_PAYMENT_DEFAULT_COMPONENT = "nfc_payment_default_component";
 
+        private static final Validator NFC_PAYMENT_DEFAULT_COMPONENT_VALIDATOR =
+                COMPONENT_NAME_VALIDATOR;
+
         /**
          * Whether NFC payment is handled by the foreground application or a default.
          * @hide
@@ -6814,6 +7045,9 @@
         public static final String ENABLED_NOTIFICATION_ASSISTANT =
                 "enabled_notification_assistant";
 
+        private static final Validator ENABLED_NOTIFICATION_ASSISTANT_VALIDATOR =
+                new SettingsValidators.ComponentNameListValidator(":");
+
         /**
          * Read only list of the service components that the current user has explicitly allowed to
          * see all of the user's notifications, separated by ':'.
@@ -6825,6 +7059,9 @@
         @Deprecated
         public static final String ENABLED_NOTIFICATION_LISTENERS = "enabled_notification_listeners";
 
+        private static final Validator ENABLED_NOTIFICATION_LISTENERS_VALIDATOR =
+                new SettingsValidators.ComponentNameListValidator(":");
+
         /**
          * Read only list of the packages that the current user has explicitly allowed to
          * manage do not disturb, separated by ':'.
@@ -6837,6 +7074,9 @@
         public static final String ENABLED_NOTIFICATION_POLICY_ACCESS_PACKAGES =
                 "enabled_notification_policy_access_packages";
 
+        private static final Validator ENABLED_NOTIFICATION_POLICY_ACCESS_PACKAGES_VALIDATOR =
+                new SettingsValidators.PackageNameListValidator(":");
+
         /**
          * Defines whether managed profile ringtones should be synced from it's parent profile
          * <p>
@@ -6850,6 +7090,8 @@
         @RequiresPermission(Manifest.permission.WRITE_SECURE_SETTINGS)
         public static final String SYNC_PARENT_SOUNDS = "sync_parent_sounds";
 
+        private static final Validator SYNC_PARENT_SOUNDS_VALIDATOR = BOOLEAN_VALIDATOR;
+
         /** @hide */
         public static final String IMMERSIVE_MODE_CONFIRMATIONS = "immersive_mode_confirmations";
 
@@ -6936,12 +7178,17 @@
          */
         public static final String SLEEP_TIMEOUT = "sleep_timeout";
 
+        private static final Validator SLEEP_TIMEOUT_VALIDATOR =
+                new SettingsValidators.InclusiveIntegerRangeValidator(-1, Integer.MAX_VALUE);
+
         /**
          * Controls whether double tap to wake is enabled.
          * @hide
          */
         public static final String DOUBLE_TAP_TO_WAKE = "double_tap_to_wake";
 
+        private static final Validator DOUBLE_TAP_TO_WAKE_VALIDATOR = BOOLEAN_VALIDATOR;
+
         /**
          * The current assistant component. It could be a voice interaction service,
          * or an activity that handles ACTION_ASSIST, or empty which means using the default
@@ -6958,6 +7205,8 @@
          */
         public static final String CAMERA_GESTURE_DISABLED = "camera_gesture_disabled";
 
+        private static final Validator CAMERA_GESTURE_DISABLED_VALIDATOR = BOOLEAN_VALIDATOR;
+
         /**
          * Whether the camera launch gesture to double tap the power button when the screen is off
          * should be disabled.
@@ -6967,6 +7216,9 @@
         public static final String CAMERA_DOUBLE_TAP_POWER_GESTURE_DISABLED =
                 "camera_double_tap_power_gesture_disabled";
 
+        private static final Validator CAMERA_DOUBLE_TAP_POWER_GESTURE_DISABLED_VALIDATOR =
+                BOOLEAN_VALIDATOR;
+
         /**
          * Whether the camera double twist gesture to flip between front and back mode should be
          * enabled.
@@ -6976,6 +7228,9 @@
         public static final String CAMERA_DOUBLE_TWIST_TO_FLIP_ENABLED =
                 "camera_double_twist_to_flip_enabled";
 
+        private static final Validator CAMERA_DOUBLE_TWIST_TO_FLIP_ENABLED_VALIDATOR =
+                BOOLEAN_VALIDATOR;
+
         /**
          * Whether or not the smart camera lift trigger that launches the camera when the user moves
          * the phone into a position for taking photos should be enabled.
@@ -6998,6 +7253,9 @@
          */
         public static final String ASSIST_GESTURE_ENABLED = "assist_gesture_enabled";
 
+        private static final Validator ASSIST_GESTURE_ENABLED_VALIDATOR =
+                BOOLEAN_VALIDATOR;
+
         /**
          * Sensitivity control for the assist gesture.
          *
@@ -7005,6 +7263,9 @@
          */
         public static final String ASSIST_GESTURE_SENSITIVITY = "assist_gesture_sensitivity";
 
+        private static final Validator ASSIST_GESTURE_SENSITIVITY_VALIDATOR =
+                new SettingsValidators.InclusiveFloatRangeValidator(0.0f, 1.0f);
+
         /**
          * Whether the assist gesture should silence alerts.
          *
@@ -7013,6 +7274,9 @@
         public static final String ASSIST_GESTURE_SILENCE_ALERTS_ENABLED =
                 "assist_gesture_silence_alerts_enabled";
 
+        private static final Validator ASSIST_GESTURE_SILENCE_ALERTS_ENABLED_VALIDATOR =
+                BOOLEAN_VALIDATOR;
+
         /**
          * Whether the assist gesture should wake the phone.
          *
@@ -7021,6 +7285,9 @@
         public static final String ASSIST_GESTURE_WAKE_ENABLED =
                 "assist_gesture_wake_enabled";
 
+        private static final Validator ASSIST_GESTURE_WAKE_ENABLED_VALIDATOR =
+                BOOLEAN_VALIDATOR;
+
         /**
          * Whether Assist Gesture Deferred Setup has been completed
          *
@@ -7028,6 +7295,8 @@
          */
         public static final String ASSIST_GESTURE_SETUP_COMPLETE = "assist_gesture_setup_complete";
 
+        private static final Validator ASSIST_GESTURE_SETUP_COMPLETE_VALIDATOR = BOOLEAN_VALIDATOR;
+
         /**
          * Control whether Night display is currently activated.
          * @hide
@@ -7040,6 +7309,8 @@
          */
         public static final String NIGHT_DISPLAY_AUTO_MODE = "night_display_auto_mode";
 
+        private static final Validator NIGHT_DISPLAY_AUTO_MODE_VALIDATOR = BOOLEAN_VALIDATOR;
+
         /**
          * Control the color temperature of Night Display, represented in Kelvin.
          * @hide
@@ -7047,6 +7318,9 @@
         public static final String NIGHT_DISPLAY_COLOR_TEMPERATURE =
                 "night_display_color_temperature";
 
+        private static final Validator NIGHT_DISPLAY_COLOR_TEMPERATURE_VALIDATOR =
+                NON_NEGATIVE_INTEGER_VALIDATOR;
+
         /**
          * Custom time when Night display is scheduled to activate.
          * Represented as milliseconds from midnight (e.g. 79200000 == 10pm).
@@ -7055,6 +7329,9 @@
         public static final String NIGHT_DISPLAY_CUSTOM_START_TIME =
                 "night_display_custom_start_time";
 
+        private static final Validator NIGHT_DISPLAY_CUSTOM_START_TIME_VALIDATOR =
+                NON_NEGATIVE_INTEGER_VALIDATOR;
+
         /**
          * Custom time when Night display is scheduled to deactivate.
          * Represented as milliseconds from midnight (e.g. 21600000 == 6am).
@@ -7062,6 +7339,9 @@
          */
         public static final String NIGHT_DISPLAY_CUSTOM_END_TIME = "night_display_custom_end_time";
 
+        private static final Validator NIGHT_DISPLAY_CUSTOM_END_TIME_VALIDATOR =
+                NON_NEGATIVE_INTEGER_VALIDATOR;
+
         /**
          * A String representing the LocalDateTime when Night display was last activated. Use to
          * decide whether to apply the current activated state after a reboot or user change. In
@@ -7079,6 +7359,9 @@
          */
         public static final String ENABLED_VR_LISTENERS = "enabled_vr_listeners";
 
+        private static final Validator ENABLED_VR_LISTENERS_VALIDATOR =
+                new SettingsValidators.ComponentNameListValidator(":");
+
         /**
          * Behavior of the display while in VR mode.
          *
@@ -7088,6 +7371,9 @@
          */
         public static final String VR_DISPLAY_MODE = "vr_display_mode";
 
+        private static final Validator VR_DISPLAY_MODE_VALIDATOR =
+                new SettingsValidators.DiscreteValueValidator(new String[]{"0", "1"});
+
         /**
          * Lower the display persistence while the system is in VR mode.
          *
@@ -7144,6 +7430,9 @@
         public static final String AUTOMATIC_STORAGE_MANAGER_DAYS_TO_RETAIN =
                 "automatic_storage_manager_days_to_retain";
 
+        private static final Validator AUTOMATIC_STORAGE_MANAGER_DAYS_TO_RETAIN_VALIDATOR =
+                NON_NEGATIVE_INTEGER_VALIDATOR;
+
         /**
          * Default number of days of information for the automatic storage manager to retain.
          *
@@ -7185,12 +7474,30 @@
         public static final String SYSTEM_NAVIGATION_KEYS_ENABLED =
                 "system_navigation_keys_enabled";
 
+        private static final Validator SYSTEM_NAVIGATION_KEYS_ENABLED_VALIDATOR = BOOLEAN_VALIDATOR;
+
         /**
          * Holds comma separated list of ordering of QS tiles.
          * @hide
          */
         public static final String QS_TILES = "sysui_qs_tiles";
 
+        private static final Validator QS_TILES_VALIDATOR = new Validator() {
+            @Override
+            public boolean validate(String value) {
+                if (value == null) {
+                    return false;
+                }
+                String[] tiles = value.split(",");
+                boolean valid = true;
+                for (String tile : tiles) {
+                    // tile can be any non-empty string as specified by OEM
+                    valid |= ((tile.length() > 0) && ANY_STRING_VALIDATOR.validate(tile));
+                }
+                return valid;
+            }
+        };
+
         /**
          * Specifies whether the web action API is enabled.
          *
@@ -7226,18 +7533,38 @@
          */
         public static final String NOTIFICATION_BADGING = "notification_badging";
 
+        private static final Validator NOTIFICATION_BADGING_VALIDATOR = BOOLEAN_VALIDATOR;
+
         /**
          * Comma separated list of QS tiles that have been auto-added already.
          * @hide
          */
         public static final String QS_AUTO_ADDED_TILES = "qs_auto_tiles";
 
+        private static final Validator QS_AUTO_ADDED_TILES_VALIDATOR = new Validator() {
+            @Override
+            public boolean validate(String value) {
+                if (value == null) {
+                    return false;
+                }
+                String[] tiles = value.split(",");
+                boolean valid = true;
+                for (String tile : tiles) {
+                    // tile can be any non-empty string as specified by OEM
+                    valid |= ((tile.length() > 0) && ANY_STRING_VALIDATOR.validate(tile));
+                }
+                return valid;
+            }
+        };
+
         /**
          * Whether the Lockdown button should be shown in the power menu.
          * @hide
          */
         public static final String LOCKDOWN_IN_POWER_MENU = "lockdown_in_power_menu";
 
+        private static final Validator LOCKDOWN_IN_POWER_MENU_VALIDATOR = BOOLEAN_VALIDATOR;
+
         /**
          * Backup manager behavioral parameters.
          * This is encoded as a key=value list, separated by commas. Ex:
@@ -7277,8 +7604,6 @@
         public static final String[] SETTINGS_TO_BACKUP = {
             BUGREPORT_IN_POWER_MENU,                            // moved to global
             ALLOW_MOCK_LOCATION,
-            PARENTAL_CONTROL_ENABLED,
-            PARENTAL_CONTROL_REDIRECT_URL,
             USB_MASS_STORAGE_ENABLED,                           // moved to global
             ACCESSIBILITY_DISPLAY_INVERSION_ENABLED,
             ACCESSIBILITY_DISPLAY_DALTONIZER,
@@ -7310,12 +7635,9 @@
             ACCESSIBILITY_CAPTIONING_TYPEFACE,
             ACCESSIBILITY_CAPTIONING_FONT_SCALE,
             ACCESSIBILITY_CAPTIONING_WINDOW_COLOR,
-            TTS_USE_DEFAULTS,
             TTS_DEFAULT_RATE,
             TTS_DEFAULT_PITCH,
             TTS_DEFAULT_SYNTH,
-            TTS_DEFAULT_LANG,
-            TTS_DEFAULT_COUNTRY,
             TTS_ENABLED_PLUGINS,
             TTS_DEFAULT_LOCALE,
             SHOW_IME_WITH_HARD_KEYBOARD,
@@ -7371,7 +7693,158 @@
             SHOW_FIRST_CRASH_DIALOG_DEV_OPTION,
         };
 
-        /** @hide */
+        /**
+         * All settings in {@link SETTINGS_TO_BACKUP} array *must* have a non-null validator,
+         * otherwise they won't be restored.
+         *
+         * @hide
+         */
+        public static final Map<String, Validator> VALIDATORS = new ArrayMap<>();
+        static {
+            VALIDATORS.put(BUGREPORT_IN_POWER_MENU, BUGREPORT_IN_POWER_MENU_VALIDATOR);
+            VALIDATORS.put(ALLOW_MOCK_LOCATION, ALLOW_MOCK_LOCATION_VALIDATOR);
+            VALIDATORS.put(USB_MASS_STORAGE_ENABLED, USB_MASS_STORAGE_ENABLED_VALIDATOR);
+            VALIDATORS.put(ACCESSIBILITY_DISPLAY_INVERSION_ENABLED,
+                    ACCESSIBILITY_DISPLAY_INVERSION_ENABLED_VALIDATOR);
+            VALIDATORS.put(ACCESSIBILITY_DISPLAY_DALTONIZER,
+                    ACCESSIBILITY_DISPLAY_DALTONIZER_VALIDATOR);
+            VALIDATORS.put(ACCESSIBILITY_DISPLAY_DALTONIZER_ENABLED,
+                    ACCESSIBILITY_DISPLAY_DALTONIZER_ENABLED_VALIDATOR);
+            VALIDATORS.put(ACCESSIBILITY_DISPLAY_MAGNIFICATION_ENABLED,
+                    ACCESSIBILITY_DISPLAY_MAGNIFICATION_ENABLED_VALIDATOR);
+            VALIDATORS.put(ACCESSIBILITY_DISPLAY_MAGNIFICATION_NAVBAR_ENABLED,
+                    ACCESSIBILITY_DISPLAY_MAGNIFICATION_NAVBAR_ENABLED_VALIDATOR);
+            VALIDATORS.put(AUTOFILL_SERVICE, AUTOFILL_SERVICE_VALIDATOR);
+            VALIDATORS.put(ACCESSIBILITY_DISPLAY_MAGNIFICATION_SCALE,
+                    ACCESSIBILITY_DISPLAY_MAGNIFICATION_SCALE_VALIDATOR);
+            VALIDATORS.put(ENABLED_ACCESSIBILITY_SERVICES,
+                    ENABLED_ACCESSIBILITY_SERVICES_VALIDATOR);
+            VALIDATORS.put(ENABLED_VR_LISTENERS, ENABLED_VR_LISTENERS_VALIDATOR);
+            VALIDATORS.put(ENABLED_INPUT_METHODS, ENABLED_INPUT_METHODS_VALIDATOR);
+            VALIDATORS.put(TOUCH_EXPLORATION_GRANTED_ACCESSIBILITY_SERVICES,
+                    TOUCH_EXPLORATION_GRANTED_ACCESSIBILITY_SERVICES_VALIDATOR);
+            VALIDATORS.put(TOUCH_EXPLORATION_ENABLED, TOUCH_EXPLORATION_ENABLED_VALIDATOR);
+            VALIDATORS.put(ACCESSIBILITY_ENABLED, ACCESSIBILITY_ENABLED_VALIDATOR);
+            VALIDATORS.put(ACCESSIBILITY_SHORTCUT_TARGET_SERVICE,
+                    ACCESSIBILITY_SHORTCUT_TARGET_SERVICE_VALIDATOR);
+            VALIDATORS.put(ACCESSIBILITY_BUTTON_TARGET_COMPONENT,
+                    ACCESSIBILITY_BUTTON_TARGET_COMPONENT_VALIDATOR);
+            VALIDATORS.put(ACCESSIBILITY_SHORTCUT_DIALOG_SHOWN,
+                    ACCESSIBILITY_SHORTCUT_DIALOG_SHOWN_VALIDATOR);
+            VALIDATORS.put(ACCESSIBILITY_SHORTCUT_ENABLED,
+                    ACCESSIBILITY_SHORTCUT_ENABLED_VALIDATOR);
+            VALIDATORS.put(ACCESSIBILITY_SHORTCUT_ON_LOCK_SCREEN,
+                    ACCESSIBILITY_SHORTCUT_ON_LOCK_SCREEN_VALIDATOR);
+            VALIDATORS.put(ACCESSIBILITY_SPEAK_PASSWORD, ACCESSIBILITY_SPEAK_PASSWORD_VALIDATOR);
+            VALIDATORS.put(ACCESSIBILITY_HIGH_TEXT_CONTRAST_ENABLED,
+                    ACCESSIBILITY_HIGH_TEXT_CONTRAST_ENABLED_VALIDATOR);
+            VALIDATORS.put(ACCESSIBILITY_CAPTIONING_PRESET,
+                    ACCESSIBILITY_CAPTIONING_PRESET_VALIDATOR);
+            VALIDATORS.put(ACCESSIBILITY_CAPTIONING_ENABLED,
+                    ACCESSIBILITY_CAPTIONING_ENABLED_VALIDATOR);
+            VALIDATORS.put(ACCESSIBILITY_CAPTIONING_LOCALE,
+                    ACCESSIBILITY_CAPTIONING_LOCALE_VALIDATOR);
+            VALIDATORS.put(ACCESSIBILITY_CAPTIONING_BACKGROUND_COLOR,
+                    ACCESSIBILITY_CAPTIONING_BACKGROUND_COLOR_VALIDATOR);
+            VALIDATORS.put(ACCESSIBILITY_CAPTIONING_FOREGROUND_COLOR,
+                    ACCESSIBILITY_CAPTIONING_FOREGROUND_COLOR_VALIDATOR);
+            VALIDATORS.put(ACCESSIBILITY_CAPTIONING_EDGE_TYPE,
+                    ACCESSIBILITY_CAPTIONING_EDGE_TYPE_VALIDATOR);
+            VALIDATORS.put(ACCESSIBILITY_CAPTIONING_EDGE_COLOR,
+                    ACCESSIBILITY_CAPTIONING_EDGE_COLOR_VALIDATOR);
+            VALIDATORS.put(ACCESSIBILITY_CAPTIONING_TYPEFACE,
+                    ACCESSIBILITY_CAPTIONING_TYPEFACE_VALIDATOR);
+            VALIDATORS.put(ACCESSIBILITY_CAPTIONING_FONT_SCALE,
+                    ACCESSIBILITY_CAPTIONING_FONT_SCALE_VALIDATOR);
+            VALIDATORS.put(ACCESSIBILITY_CAPTIONING_WINDOW_COLOR,
+                    ACCESSIBILITY_CAPTIONING_WINDOW_COLOR_VALIDATOR);
+            VALIDATORS.put(TTS_DEFAULT_RATE, TTS_DEFAULT_RATE_VALIDATOR);
+            VALIDATORS.put(TTS_DEFAULT_PITCH, TTS_DEFAULT_PITCH_VALIDATOR);
+            VALIDATORS.put(TTS_DEFAULT_SYNTH, TTS_DEFAULT_SYNTH_VALIDATOR);
+            VALIDATORS.put(TTS_ENABLED_PLUGINS, TTS_ENABLED_PLUGINS_VALIDATOR);
+            VALIDATORS.put(TTS_DEFAULT_LOCALE, TTS_DEFAULT_LOCALE_VALIDATOR);
+            VALIDATORS.put(SHOW_IME_WITH_HARD_KEYBOARD, SHOW_IME_WITH_HARD_KEYBOARD_VALIDATOR);
+            VALIDATORS.put(WIFI_NETWORKS_AVAILABLE_NOTIFICATION_ON,
+                    WIFI_NETWORKS_AVAILABLE_NOTIFICATION_ON_VALIDATOR);
+            VALIDATORS.put(WIFI_NETWORKS_AVAILABLE_REPEAT_DELAY,
+                    WIFI_NETWORKS_AVAILABLE_REPEAT_DELAY_VALIDATOR);
+            VALIDATORS.put(WIFI_NUM_OPEN_NETWORKS_KEPT, WIFI_NUM_OPEN_NETWORKS_KEPT_VALIDATOR);
+            VALIDATORS.put(SELECTED_SPELL_CHECKER, SELECTED_SPELL_CHECKER_VALIDATOR);
+            VALIDATORS.put(SELECTED_SPELL_CHECKER_SUBTYPE,
+                    SELECTED_SPELL_CHECKER_SUBTYPE_VALIDATOR);
+            VALIDATORS.put(SPELL_CHECKER_ENABLED, SPELL_CHECKER_ENABLED_VALIDATOR);
+            VALIDATORS.put(MOUNT_PLAY_NOTIFICATION_SND, MOUNT_PLAY_NOTIFICATION_SND_VALIDATOR);
+            VALIDATORS.put(MOUNT_UMS_AUTOSTART, MOUNT_UMS_AUTOSTART_VALIDATOR);
+            VALIDATORS.put(MOUNT_UMS_PROMPT, MOUNT_UMS_PROMPT_VALIDATOR);
+            VALIDATORS.put(MOUNT_UMS_NOTIFY_ENABLED, MOUNT_UMS_NOTIFY_ENABLED_VALIDATOR);
+            VALIDATORS.put(SLEEP_TIMEOUT, SLEEP_TIMEOUT_VALIDATOR);
+            VALIDATORS.put(DOUBLE_TAP_TO_WAKE, DOUBLE_TAP_TO_WAKE_VALIDATOR);
+            VALIDATORS.put(WAKE_GESTURE_ENABLED, WAKE_GESTURE_ENABLED_VALIDATOR);
+            VALIDATORS.put(LONG_PRESS_TIMEOUT, LONG_PRESS_TIMEOUT_VALIDATOR);
+            VALIDATORS.put(CAMERA_GESTURE_DISABLED, CAMERA_GESTURE_DISABLED_VALIDATOR);
+            VALIDATORS.put(ACCESSIBILITY_AUTOCLICK_ENABLED,
+                    ACCESSIBILITY_AUTOCLICK_ENABLED_VALIDATOR);
+            VALIDATORS.put(ACCESSIBILITY_AUTOCLICK_DELAY, ACCESSIBILITY_AUTOCLICK_DELAY_VALIDATOR);
+            VALIDATORS.put(ACCESSIBILITY_LARGE_POINTER_ICON,
+                    ACCESSIBILITY_LARGE_POINTER_ICON_VALIDATOR);
+            VALIDATORS.put(PREFERRED_TTY_MODE, PREFERRED_TTY_MODE_VALIDATOR);
+            VALIDATORS.put(ENHANCED_VOICE_PRIVACY_ENABLED,
+                    ENHANCED_VOICE_PRIVACY_ENABLED_VALIDATOR);
+            VALIDATORS.put(TTY_MODE_ENABLED, TTY_MODE_ENABLED_VALIDATOR);
+            VALIDATORS.put(INCALL_POWER_BUTTON_BEHAVIOR, INCALL_POWER_BUTTON_BEHAVIOR_VALIDATOR);
+            VALIDATORS.put(NIGHT_DISPLAY_CUSTOM_START_TIME,
+                    NIGHT_DISPLAY_CUSTOM_START_TIME_VALIDATOR);
+            VALIDATORS.put(NIGHT_DISPLAY_CUSTOM_END_TIME, NIGHT_DISPLAY_CUSTOM_END_TIME_VALIDATOR);
+            VALIDATORS.put(NIGHT_DISPLAY_COLOR_TEMPERATURE,
+                    NIGHT_DISPLAY_COLOR_TEMPERATURE_VALIDATOR);
+            VALIDATORS.put(NIGHT_DISPLAY_AUTO_MODE, NIGHT_DISPLAY_AUTO_MODE_VALIDATOR);
+            VALIDATORS.put(SYNC_PARENT_SOUNDS, SYNC_PARENT_SOUNDS_VALIDATOR);
+            VALIDATORS.put(CAMERA_DOUBLE_TWIST_TO_FLIP_ENABLED,
+                    CAMERA_DOUBLE_TWIST_TO_FLIP_ENABLED_VALIDATOR);
+            VALIDATORS.put(CAMERA_DOUBLE_TAP_POWER_GESTURE_DISABLED,
+                    CAMERA_DOUBLE_TAP_POWER_GESTURE_DISABLED_VALIDATOR);
+            VALIDATORS.put(SYSTEM_NAVIGATION_KEYS_ENABLED,
+                    SYSTEM_NAVIGATION_KEYS_ENABLED_VALIDATOR);
+            VALIDATORS.put(QS_TILES, QS_TILES_VALIDATOR);
+            VALIDATORS.put(DOZE_ENABLED, DOZE_ENABLED_VALIDATOR);
+            VALIDATORS.put(DOZE_PULSE_ON_PICK_UP, DOZE_PULSE_ON_PICK_UP_VALIDATOR);
+            VALIDATORS.put(DOZE_PULSE_ON_DOUBLE_TAP, DOZE_PULSE_ON_DOUBLE_TAP_VALIDATOR);
+            VALIDATORS.put(NFC_PAYMENT_DEFAULT_COMPONENT, NFC_PAYMENT_DEFAULT_COMPONENT_VALIDATOR);
+            VALIDATORS.put(AUTOMATIC_STORAGE_MANAGER_DAYS_TO_RETAIN,
+                    AUTOMATIC_STORAGE_MANAGER_DAYS_TO_RETAIN_VALIDATOR);
+            VALIDATORS.put(ASSIST_GESTURE_ENABLED, ASSIST_GESTURE_ENABLED_VALIDATOR);
+            VALIDATORS.put(ASSIST_GESTURE_SENSITIVITY, ASSIST_GESTURE_SENSITIVITY_VALIDATOR);
+            VALIDATORS.put(ASSIST_GESTURE_SETUP_COMPLETE, ASSIST_GESTURE_SETUP_COMPLETE_VALIDATOR);
+            VALIDATORS.put(ASSIST_GESTURE_SILENCE_ALERTS_ENABLED,
+                    ASSIST_GESTURE_SILENCE_ALERTS_ENABLED_VALIDATOR);
+            VALIDATORS.put(ASSIST_GESTURE_WAKE_ENABLED, ASSIST_GESTURE_WAKE_ENABLED_VALIDATOR);
+            VALIDATORS.put(VR_DISPLAY_MODE, VR_DISPLAY_MODE_VALIDATOR);
+            VALIDATORS.put(NOTIFICATION_BADGING, NOTIFICATION_BADGING_VALIDATOR);
+            VALIDATORS.put(QS_AUTO_ADDED_TILES, QS_AUTO_ADDED_TILES_VALIDATOR);
+            VALIDATORS.put(SCREENSAVER_ENABLED, SCREENSAVER_ENABLED_VALIDATOR);
+            VALIDATORS.put(SCREENSAVER_COMPONENTS, SCREENSAVER_COMPONENTS_VALIDATOR);
+            VALIDATORS.put(SCREENSAVER_ACTIVATE_ON_DOCK, SCREENSAVER_ACTIVATE_ON_DOCK_VALIDATOR);
+            VALIDATORS.put(SCREENSAVER_ACTIVATE_ON_SLEEP, SCREENSAVER_ACTIVATE_ON_SLEEP_VALIDATOR);
+            VALIDATORS.put(LOCKDOWN_IN_POWER_MENU, LOCKDOWN_IN_POWER_MENU_VALIDATOR);
+            VALIDATORS.put(SHOW_FIRST_CRASH_DIALOG_DEV_OPTION,
+                    SHOW_FIRST_CRASH_DIALOG_DEV_OPTION_VALIDATOR);
+            VALIDATORS.put(ENABLED_NOTIFICATION_LISTENERS,
+                    ENABLED_NOTIFICATION_LISTENERS_VALIDATOR); //legacy restore setting
+            VALIDATORS.put(ENABLED_NOTIFICATION_ASSISTANT,
+                    ENABLED_NOTIFICATION_ASSISTANT_VALIDATOR); //legacy restore setting
+            VALIDATORS.put(ENABLED_NOTIFICATION_POLICY_ACCESS_PACKAGES,
+                    ENABLED_NOTIFICATION_POLICY_ACCESS_PACKAGES_VALIDATOR); //legacy restore setting
+        }
+
+        /**
+         * Keys we no longer back up under the current schema, but want to continue to
+         * process when restoring historical backup datasets.
+         *
+         * All settings in {@link LEGACY_RESTORE_SETTINGS} array *must* have a non-null validator,
+         * otherwise they won't be restored.
+         *
+         * @hide
+         */
         public static final String[] LEGACY_RESTORE_SETTINGS = {
                 ENABLED_NOTIFICATION_LISTENERS,
                 ENABLED_NOTIFICATION_ASSISTANT,
@@ -8706,6 +9179,9 @@
        public static final String WIFI_NETWORKS_AVAILABLE_REPEAT_DELAY =
                "wifi_networks_available_repeat_delay";
 
+       private static final Validator WIFI_NETWORKS_AVAILABLE_REPEAT_DELAY_VALIDATOR =
+               NON_NEGATIVE_INTEGER_VALIDATOR;
+
        /**
         * 802.11 country code in ISO 3166 format
         * @hide
@@ -8735,6 +9211,9 @@
         */
        public static final String WIFI_NUM_OPEN_NETWORKS_KEPT = "wifi_num_open_networks_kept";
 
+       private static final Validator WIFI_NUM_OPEN_NETWORKS_KEPT_VALIDATOR =
+               NON_NEGATIVE_INTEGER_VALIDATOR;
+
        /**
         * Whether the Wi-Fi should be on.  Only the Wi-Fi service should touch this.
         */
@@ -8945,6 +9424,14 @@
        public static final String WIFI_VERBOSE_LOGGING_ENABLED =
                "wifi_verbose_logging_enabled";
 
+        /**
+         * Setting to enable connected MAC randomization in Wi-Fi; disabled by default, and
+         * setting to 1 will enable it. In the future, additional values may be supported.
+         * @hide
+         */
+        public static final String WIFI_CONNECTED_MAC_RANDOMIZATION_ENABLED =
+                "wifi_connected_mac_randomization_enabled";
+
        /**
         * The maximum number of times we will retry a connection to an access
         * point for which we have failed in acquiring an IP address from DHCP.
@@ -9649,7 +10136,8 @@
          * This is encoded as a key=value list, separated by commas. Ex:
          *
          * "battery_tip_enabled=true,summary_enabled=true,high_usage_enabled=true,"
-         * "high_usage_app_count=3,reduced_battery_enabled=false,reduced_battery_percent=50"
+         * "high_usage_app_count=3,reduced_battery_enabled=false,reduced_battery_percent=50,"
+         * "high_usage_battery_draining=25,high_usage_period_ms=3000"
          *
          * The following keys are supported:
          *
@@ -9659,6 +10147,8 @@
          * battery_saver_tip_enabled        (boolean)
          * high_usage_enabled               (boolean)
          * high_usage_app_count             (int)
+         * high_usage_period_ms             (long)
+         * high_usage_battery_draining      (int)
          * app_restriction_enabled          (boolean)
          * reduced_battery_enabled          (boolean)
          * reduced_battery_percent          (int)
@@ -9700,6 +10190,14 @@
         public static final String SYS_VDSO = "sys_vdso";
 
         /**
+         * An integer to reduce the FPS by this factor. Only for experiments. Need to reboot the
+         * device for this setting to take full effect.
+         *
+         * @hide
+         */
+        public static final String FPS_DEVISOR = "fps_divisor";
+
+        /**
          * App standby (app idle) specific settings.
          * This is encoded as a key=value list, separated by commas. Ex:
          * <p>
@@ -9861,6 +10359,8 @@
          * The following keys are supported:
          * <pre>
          * track_cpu_times_by_proc_state (boolean)
+         * track_cpu_active_cluster_time (boolean)
+         * read_binary_cpu_time          (boolean)
          * </pre>
          *
          * <p>
@@ -9888,6 +10388,15 @@
         public static final String FORCED_APP_STANDBY_ENABLED = "forced_app_standby_enabled";
 
         /**
+         * Whether or not to enable Forced App Standby on small battery devices.
+         * Type: int (0 for false, 1 for true)
+         * Default: 0
+         * @hide
+         */
+        public static final String FORCED_APP_STANDBY_FOR_SMALL_BATTERY_ENABLED
+                = "forced_app_standby_for_small_battery_enabled";
+
+        /**
          * Whether or not Network Watchlist feature is enabled.
          * Type: int (0 for false, 1 for true)
          * Default: 0
@@ -10772,7 +11281,15 @@
                 LOCATION_GLOBAL_KILL_SWITCH,
         };
 
-        /** @hide */
+        /**
+         * Keys we no longer back up under the current schema, but want to continue to
+         * process when restoring historical backup datasets.
+         *
+         * All settings in {@link LEGACY_RESTORE_SETTINGS} array *must* have a non-null validator,
+         * otherwise they won't be restored.
+         *
+         * @hide
+         */
         public static final String[] LEGACY_RESTORE_SETTINGS = {
         };
 
diff --git a/core/java/android/provider/SettingsValidators.java b/core/java/android/provider/SettingsValidators.java
index 22ef3b6..84c9e88 100644
--- a/core/java/android/provider/SettingsValidators.java
+++ b/core/java/android/provider/SettingsValidators.java
@@ -21,6 +21,8 @@
 
 import com.android.internal.util.ArrayUtils;
 
+import java.util.Locale;
+
 /**
  * This class provides both interface for validation and common validators
  * used to ensure Settings have meaningful values.
@@ -50,6 +52,18 @@
         }
     };
 
+    public static final Validator ANY_INTEGER_VALIDATOR = new Validator() {
+        @Override
+        public boolean validate(String value) {
+            try {
+                Integer.parseInt(value);
+                return true;
+            } catch (NumberFormatException e) {
+                return false;
+            }
+        }
+    };
+
     public static final Validator URI_VALIDATOR = new Validator() {
         @Override
         public boolean validate(String value) {
@@ -80,7 +94,10 @@
             // and underscores ('_'). However, individual package name parts may only
             // start with letters.
             // (https://developer.android.com/guide/topics/manifest/manifest-element.html#package)
-            String[] subparts = value.split(".");
+            if (value == null) {
+                return false;
+            }
+            String[] subparts = value.split("\\.");
             boolean isValidPackageName = true;
             for (String subpart : subparts) {
                 isValidPackageName |= isSubpartValidForPackageName(subpart);
@@ -106,10 +123,29 @@
 
         @Override
         public boolean validate(String value) {
+            if (value == null) {
+                return false;
+            }
             return value.length() <= MAX_IPV6_LENGTH;
         }
     };
 
+    public static final Validator LOCALE_VALIDATOR = new Validator() {
+        @Override
+        public boolean validate(String value) {
+            if (value == null) {
+                return false;
+            }
+            Locale[] validLocales = Locale.getAvailableLocales();
+            for (Locale locale : validLocales) {
+                if (value.equals(locale.toString())) {
+                    return true;
+                }
+            }
+            return false;
+        }
+    };
+
     public interface Validator {
         boolean validate(String value);
     }
@@ -166,4 +202,48 @@
             }
         }
     }
+
+    public static final class ComponentNameListValidator implements Validator {
+        private final String mSeparator;
+
+        public ComponentNameListValidator(String separator) {
+            mSeparator = separator;
+        }
+
+        @Override
+        public boolean validate(String value) {
+            if (value == null) {
+                return false;
+            }
+            String[] elements = value.split(mSeparator);
+            for (String element : elements) {
+                if (!COMPONENT_NAME_VALIDATOR.validate(element)) {
+                    return false;
+                }
+            }
+            return true;
+        }
+    }
+
+    public static final class PackageNameListValidator implements Validator {
+        private final String mSeparator;
+
+        public PackageNameListValidator(String separator) {
+            mSeparator = separator;
+        }
+
+        @Override
+        public boolean validate(String value) {
+            if (value == null) {
+                return false;
+            }
+            String[] elements = value.split(mSeparator);
+            for (String element : elements) {
+                if (!PACKAGE_NAME_VALIDATOR.validate(element)) {
+                    return false;
+                }
+            }
+            return true;
+        }
+    }
 }
diff --git a/core/java/android/security/keymaster/KeymasterDefs.java b/core/java/android/security/keymaster/KeymasterDefs.java
index f409e5b..72afbb8 100644
--- a/core/java/android/security/keymaster/KeymasterDefs.java
+++ b/core/java/android/security/keymaster/KeymasterDefs.java
@@ -73,6 +73,7 @@
     public static final int KM_TAG_USER_AUTH_TYPE = KM_ENUM | 504;
     public static final int KM_TAG_AUTH_TIMEOUT = KM_UINT | 505;
     public static final int KM_TAG_ALLOW_WHILE_ON_BODY = KM_BOOL | 506;
+    public static final int KM_TAG_TRUSTED_USER_PRESENCE_REQUIRED = KM_BOOL | 507;
 
     public static final int KM_TAG_ALL_APPLICATIONS = KM_BOOL | 600;
     public static final int KM_TAG_APPLICATION_ID = KM_BYTES | 601;
diff --git a/core/java/android/service/autofill/AutofillFieldClassificationService.java b/core/java/android/service/autofill/AutofillFieldClassificationService.java
index 18f6dab..df63a91 100644
--- a/core/java/android/service/autofill/AutofillFieldClassificationService.java
+++ b/core/java/android/service/autofill/AutofillFieldClassificationService.java
@@ -15,9 +15,6 @@
  */
 package android.service.autofill;
 
-import static android.view.autofill.AutofillManager.EXTRA_AVAILABLE_ALGORITHMS;
-import static android.view.autofill.AutofillManager.EXTRA_DEFAULT_ALGORITHM;
-
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.SystemApi;
@@ -58,9 +55,7 @@
 
     private static final String TAG = "AutofillFieldClassificationService";
 
-    private static final int MSG_GET_AVAILABLE_ALGORITHMS = 1;
-    private static final int MSG_GET_DEFAULT_ALGORITHM = 2;
-    private static final int MSG_GET_SCORES = 3;
+    private static final int MSG_GET_SCORES = 1;
 
     /**
      * The {@link Intent} action that must be declared as handled by a service
@@ -69,6 +64,20 @@
     public static final String SERVICE_INTERFACE =
             "android.service.autofill.AutofillFieldClassificationService";
 
+    /**
+     * Manifest metadata key for the resource string containing the name of the default field
+     * classification algorithm.
+     */
+    public static final String SERVICE_META_DATA_KEY_DEFAULT_ALGORITHM =
+            "android.autofill.field_classification.default_algorithm";
+    /**
+     * Manifest metadata key for the resource string array containing the names of all field
+     * classification algorithms provided by the service.
+     */
+    public static final String SERVICE_META_DATA_KEY_AVAILABLE_ALGORITHMS =
+            "android.autofill.field_classification.available_algorithms";
+
+
     /** {@hide} **/
     public static final String EXTRA_SCORES = "scores";
 
@@ -79,21 +88,6 @@
         final Bundle data = new Bundle();
         final RemoteCallback callback;
         switch (action) {
-            case MSG_GET_AVAILABLE_ALGORITHMS:
-                callback = (RemoteCallback) msg.obj;
-                final List<String> availableAlgorithms = onGetAvailableAlgorithms();
-                String[] asArray = null;
-                if (availableAlgorithms != null) {
-                    asArray = new String[availableAlgorithms.size()];
-                    availableAlgorithms.toArray(asArray);
-                }
-                data.putStringArray(EXTRA_AVAILABLE_ALGORITHMS, asArray);
-                break;
-            case MSG_GET_DEFAULT_ALGORITHM:
-                callback = (RemoteCallback) msg.obj;
-                final String defaultAlgorithm = onGetDefaultAlgorithm();
-                data.putString(EXTRA_DEFAULT_ALGORITHM, defaultAlgorithm);
-                break;
             case MSG_GET_SCORES:
                 final SomeArgs args = (SomeArgs) msg.obj;
                 callback = (RemoteCallback) args.arg1;
@@ -103,9 +97,11 @@
                 final List<AutofillValue> actualValues = ((List<AutofillValue>) args.arg4);
                 @SuppressWarnings("unchecked")
                 final String[] userDataValues = (String[]) args.arg5;
-                final Scores scores = onGetScores(algorithmName, algorithmArgs, actualValues,
+                final float[][] scores = onGetScores(algorithmName, algorithmArgs, actualValues,
                         Arrays.asList(userDataValues));
-                data.putParcelable(EXTRA_SCORES, scores);
+                if (scores != null) {
+                    data.putParcelable(EXTRA_SCORES, new Scores(scores));
+                }
                 break;
             default:
                 Log.w(TAG, "Handling unknown message: " + action);
@@ -134,27 +130,6 @@
     }
 
     /**
-     * Gets the name of all available algorithms.
-     *
-     * @throws UnsupportedOperationException if not implemented by service.
-     */
-    // TODO(b/70939974): rename to onGetAvailableAlgorithms if not removed
-    @NonNull
-    public List<String> onGetAvailableAlgorithms() {
-        throw new UnsupportedOperationException("Must be implemented by external service");
-    }
-
-    /**
-     * Gets the default algorithm that's used when an algorithm is not specified or is invalid.
-     *
-     * @throws UnsupportedOperationException if not implemented by service.
-     */
-    @NonNull
-    public String onGetDefaultAlgorithm() {
-        throw new UnsupportedOperationException("Must be implemented by external service");
-    }
-
-    /**
      * Calculates field classification scores in a batch.
      *
      * <p>See {@link AutofillFieldClassificationService} for more info about field classification
@@ -165,31 +140,22 @@
      * @param args optional arguments to be passed to the algorithm.
      * @param actualValues values entered by the user.
      * @param userDataValues values predicted from the user data.
-     * @return the calculated scores and the algorithm used.
+     * @return the calculated scores, with the first dimension representing actual values and the
+     * second dimension values from {@link UserData}.
      *
      * {@hide}
      */
     @Nullable
     @SystemApi
-    public Scores onGetScores(@Nullable String algorithm,
+    public float[][] onGetScores(@Nullable String algorithm,
             @Nullable Bundle args, @NonNull List<AutofillValue> actualValues,
             @NonNull List<String> userDataValues) {
-        throw new UnsupportedOperationException("Must be implemented by external service");
+        Log.e(TAG, "service implementation (" + getClass() + " does not implement onGetScore()");
+        return null;
     }
 
     private final class AutofillFieldClassificationServiceWrapper
             extends IAutofillFieldClassificationService.Stub {
-
-        @Override
-        public void getAvailableAlgorithms(RemoteCallback callback) throws RemoteException {
-            mHandlerCaller.obtainMessageO(MSG_GET_AVAILABLE_ALGORITHMS, callback).sendToTarget();
-        }
-
-        @Override
-        public void getDefaultAlgorithm(RemoteCallback callback) throws RemoteException {
-            mHandlerCaller.obtainMessageO(MSG_GET_DEFAULT_ALGORITHM, callback).sendToTarget();
-        }
-
         @Override
         public void getScores(RemoteCallback callback, String algorithmName, Bundle algorithmArgs,
                 List<AutofillValue> actualValues, String[] userDataValues)
@@ -200,52 +166,27 @@
         }
     }
 
-
-    // TODO(b/70939974): it might be simpler to remove this class and return the float[][] directly,
-    // ignoring the request if the algorithm name is invalid.
     /**
-     * Represents field classification scores used in a batch calculation.
+     * Helper class used to encapsulate a float[][] in a Parcelable.
      *
      * {@hide}
      */
-    @SystemApi
     public static final class Scores implements Parcelable {
-        private final String mAlgorithmName;
-        private final float[][] mScores;
+        public final float[][] scores;
 
-        /* @hide */
-        public Scores(String algorithmName, int size1, int size2) {
-            mAlgorithmName = algorithmName;
-            mScores = new float[size1][size2];
-        }
-
-        public Scores(Parcel parcel) {
-            mAlgorithmName = parcel.readString();
+        private Scores(Parcel parcel) {
             final int size1 = parcel.readInt();
             final int size2 = parcel.readInt();
-            mScores = new float[size1][size2];
+            scores = new float[size1][size2];
             for (int i = 0; i < size1; i++) {
                 for (int j = 0; j < size2; j++) {
-                    mScores[i][j] = parcel.readFloat();
+                    scores[i][j] = parcel.readFloat();
                 }
             }
         }
 
-        /**
-         * Gets the name of algorithm used to calculate the score.
-         */
-        @NonNull
-        public String getAlgorithm() {
-            return mAlgorithmName;
-        }
-
-        /**
-         * Gets the resulting scores, with the 1st dimension representing actual values and the 2nd
-         * dimension values from {@link UserData}.
-         */
-        @NonNull
-        public float[][] getScores() {
-            return mScores;
+        private Scores(float[][] scores) {
+            this.scores = scores;
         }
 
         @Override
@@ -255,20 +196,18 @@
 
         @Override
         public void writeToParcel(Parcel parcel, int flags) {
-            parcel.writeString(mAlgorithmName);
-            int size1 = mScores.length;
-            int size2 = mScores[0].length;
+            int size1 = scores.length;
+            int size2 = scores[0].length;
             parcel.writeInt(size1);
             parcel.writeInt(size2);
             for (int i = 0; i < size1; i++) {
                 for (int j = 0; j < size2; j++) {
-                    parcel.writeFloat(mScores[i][j]);
+                    parcel.writeFloat(scores[i][j]);
                 }
             }
         }
 
         public static final Creator<Scores> CREATOR = new Creator<Scores>() {
-
             @Override
             public Scores createFromParcel(Parcel parcel) {
                 return new Scores(parcel);
@@ -278,7 +217,6 @@
             public Scores[] newArray(int size) {
                 return new Scores[size];
             }
-
         };
     }
 }
diff --git a/core/java/android/service/autofill/FieldClassification.java b/core/java/android/service/autofill/FieldClassification.java
index 5361803..cd1efd6 100644
--- a/core/java/android/service/autofill/FieldClassification.java
+++ b/core/java/android/service/autofill/FieldClassification.java
@@ -105,21 +105,16 @@
 
     /**
      * Represents the score of a {@link UserData} entry for the field.
-     *
-     * <p>The score is calculated by the given {@link #getAlgorithm() algorithm} and
-     * the entry is identified by {@link #getRemoteId()}.
      */
     public static final class Match {
 
         private final String mRemoteId;
         private final float mScore;
-        private final String mAlgorithm;
 
         /** @hide */
-        public Match(String remoteId, float score, String algorithm) {
+        public Match(String remoteId, float score) {
             mRemoteId = Preconditions.checkNotNull(remoteId);
             mScore = score;
-            mAlgorithm = algorithm;
         }
 
         /**
@@ -150,38 +145,22 @@
             return mScore;
         }
 
-        /**
-         * Gets the algorithm used to calculate this score.
-         *
-         * <p>Typically, this is either the algorithm set by
-         * {@link UserData.Builder#setFieldClassificationAlgorithm(String, android.os.Bundle)},
-         * or the
-         * {@link android.view.autofill.AutofillManager#getDefaultFieldClassificationAlgorithm()}.
-         */
-        @NonNull
-        public String getAlgorithm() {
-            return mAlgorithm;
-        }
-
         @Override
         public String toString() {
             if (!sDebug) return super.toString();
 
             final StringBuilder string = new StringBuilder("Match: remoteId=");
             Helper.appendRedacted(string, mRemoteId);
-            return string.append(", score=").append(mScore)
-                    .append(", algorithm=").append(mAlgorithm)
-                    .toString();
+            return string.append(", score=").append(mScore).toString();
         }
 
         private void writeToParcel(@NonNull Parcel parcel) {
             parcel.writeString(mRemoteId);
             parcel.writeFloat(mScore);
-            parcel.writeString(mAlgorithm);
         }
 
         private static Match readFromParcel(@NonNull Parcel parcel) {
-            return new Match(parcel.readString(), parcel.readFloat(), parcel.readString());
+            return new Match(parcel.readString(), parcel.readFloat());
         }
     }
 }
diff --git a/core/java/android/service/autofill/IAutofillFieldClassificationService.aidl b/core/java/android/service/autofill/IAutofillFieldClassificationService.aidl
index d8e829d..398557d 100644
--- a/core/java/android/service/autofill/IAutofillFieldClassificationService.aidl
+++ b/core/java/android/service/autofill/IAutofillFieldClassificationService.aidl
@@ -27,8 +27,6 @@
  * @hide
  */
 oneway interface IAutofillFieldClassificationService {
-    void getAvailableAlgorithms(in RemoteCallback callback);
-    void getDefaultAlgorithm(in RemoteCallback callback);
     void getScores(in RemoteCallback callback, String algorithmName, in Bundle algorithmArgs,
-                  in List<AutofillValue> actualValues, in String[] userDataValues);
+                   in List<AutofillValue> actualValues, in String[] userDataValues);
 }
diff --git a/core/java/android/service/autofill/UserData.java b/core/java/android/service/autofill/UserData.java
index 2f9225a..9017848 100644
--- a/core/java/android/service/autofill/UserData.java
+++ b/core/java/android/service/autofill/UserData.java
@@ -155,12 +155,9 @@
          * <p>The currently available algorithms can be retrieve through
          * {@link AutofillManager#getAvailableFieldClassificationAlgorithms()}.
          *
-         * <p><b>Note: </b>The available algorithms in the Android System can change dinamically,
-         * so it's not guaranteed that the algorithm set here is the one that will be effectually
-         * used. If the algorithm set here is not available at runtime, the
-         * {@link AutofillManager#getDefaultFieldClassificationAlgorithm()} is used instead.
-         * You can verify which algorithm was used by calling
-         * {@link FieldClassification.Match#getAlgorithm()}.
+         * <p>If not set, the
+         * {@link AutofillManager#getDefaultFieldClassificationAlgorithm() default algorithm} is
+         * used instead.
          *
          * @param name name of the algorithm or {@code null} to used default.
          * @param args optional arguments to the algorithm.
diff --git a/core/java/android/service/notification/NotifyingApp.aidl b/core/java/android/service/notification/NotifyingApp.aidl
new file mode 100644
index 0000000..5358c2f
--- /dev/null
+++ b/core/java/android/service/notification/NotifyingApp.aidl
@@ -0,0 +1,19 @@
+/**
+ * Copyright (c) 2018, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.service.notification;
+
+parcelable NotifyingApp;
\ No newline at end of file
diff --git a/core/java/android/service/notification/NotifyingApp.java b/core/java/android/service/notification/NotifyingApp.java
new file mode 100644
index 0000000..38f18c6
--- /dev/null
+++ b/core/java/android/service/notification/NotifyingApp.java
@@ -0,0 +1,139 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.service.notification;
+
+import android.annotation.NonNull;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.util.Objects;
+
+/**
+ * @hide
+ */
+public final class NotifyingApp implements Parcelable, Comparable<NotifyingApp> {
+
+    private int mUid;
+    private String mPkg;
+    private long mLastNotified;
+
+    public NotifyingApp() {}
+
+    protected NotifyingApp(Parcel in) {
+        mUid = in.readInt();
+        mPkg = in.readString();
+        mLastNotified = in.readLong();
+    }
+
+    public int getUid() {
+        return mUid;
+    }
+
+    /**
+     * Sets the uid of the package that sent the notification. Returns self.
+     */
+    public NotifyingApp setUid(int mUid) {
+        this.mUid = mUid;
+        return this;
+    }
+
+    public String getPackage() {
+        return mPkg;
+    }
+
+    /**
+     * Sets the package that sent the notification. Returns self.
+     */
+    public NotifyingApp setPackage(@NonNull String mPkg) {
+        this.mPkg = mPkg;
+        return this;
+    }
+
+    public long getLastNotified() {
+        return mLastNotified;
+    }
+
+    /**
+     * Sets the time the notification was originally sent. Returns self.
+     */
+    public NotifyingApp setLastNotified(long mLastNotified) {
+        this.mLastNotified = mLastNotified;
+        return this;
+    }
+
+    public static final Creator<NotifyingApp> CREATOR = new Creator<NotifyingApp>() {
+        @Override
+        public NotifyingApp createFromParcel(Parcel in) {
+            return new NotifyingApp(in);
+        }
+
+        @Override
+        public NotifyingApp[] newArray(int size) {
+            return new NotifyingApp[size];
+        }
+    };
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeInt(mUid);
+        dest.writeString(mPkg);
+        dest.writeLong(mLastNotified);
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+        NotifyingApp that = (NotifyingApp) o;
+        return getUid() == that.getUid()
+                && getLastNotified() == that.getLastNotified()
+                && Objects.equals(mPkg, that.mPkg);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(getUid(), mPkg, getLastNotified());
+    }
+
+    /**
+     * Sorts notifying apps from newest last notified date to oldest.
+     */
+    @Override
+    public int compareTo(NotifyingApp o) {
+        if (getLastNotified() == o.getLastNotified()) {
+            if (getUid() == o.getUid()) {
+                return getPackage().compareTo(o.getPackage());
+            }
+            return Integer.compare(getUid(), o.getUid());
+        }
+
+        return -Long.compare(getLastNotified(), o.getLastNotified());
+    }
+
+    @Override
+    public String toString() {
+        return "NotifyingApp{"
+                + "mUid=" + mUid
+                + ", mPkg='" + mPkg + '\''
+                + ", mLastNotified=" + mLastNotified
+                + '}';
+    }
+}
diff --git a/core/java/android/text/style/AbsoluteSizeSpan.java b/core/java/android/text/style/AbsoluteSizeSpan.java
index 908ef55..3b4eea7 100644
--- a/core/java/android/text/style/AbsoluteSizeSpan.java
+++ b/core/java/android/text/style/AbsoluteSizeSpan.java
@@ -16,71 +16,105 @@
 
 package android.text.style;
 
+import android.annotation.NonNull;
 import android.os.Parcel;
 import android.text.ParcelableSpan;
 import android.text.TextPaint;
 import android.text.TextUtils;
 
+/**
+ * A span that changes the size of the text it's attached to.
+ * <p>
+ * For example, the size of the text can be changed to 55dp like this:
+ * <pre>{@code
+ * SpannableString string = new SpannableString("Text with absolute size span");
+ *string.setSpan(new AbsoluteSizeSpan(55, true), 10, 23, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);}</pre>
+ * <img src="{@docRoot}reference/android/images/text/style/absolutesizespan.png" />
+ * <figcaption>Text with text size updated.</figcaption>
+ */
 public class AbsoluteSizeSpan extends MetricAffectingSpan implements ParcelableSpan {
 
     private final int mSize;
-    private boolean mDip;
+    private final boolean mDip;
 
     /**
      * Set the text size to <code>size</code> physical pixels.
      */
     public AbsoluteSizeSpan(int size) {
-        mSize = size;
+        this(size, false);
     }
 
     /**
-     * Set the text size to <code>size</code> physical pixels,
-     * or to <code>size</code> device-independent pixels if
-     * <code>dip</code> is true.
+     * Set the text size to <code>size</code> physical pixels, or to <code>size</code>
+     * device-independent pixels if <code>dip</code> is true.
      */
     public AbsoluteSizeSpan(int size, boolean dip) {
         mSize = size;
         mDip = dip;
     }
 
-    public AbsoluteSizeSpan(Parcel src) {
+    /**
+     * Creates an {@link AbsoluteSizeSpan} from a parcel.
+     */
+    public AbsoluteSizeSpan(@NonNull Parcel src) {
         mSize = src.readInt();
         mDip = src.readInt() != 0;
     }
-    
+
+    @Override
     public int getSpanTypeId() {
         return getSpanTypeIdInternal();
     }
 
     /** @hide */
+    @Override
     public int getSpanTypeIdInternal() {
         return TextUtils.ABSOLUTE_SIZE_SPAN;
     }
-    
+
+    @Override
     public int describeContents() {
         return 0;
     }
 
-    public void writeToParcel(Parcel dest, int flags) {
+    @Override
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
         writeToParcelInternal(dest, flags);
     }
 
     /** @hide */
-    public void writeToParcelInternal(Parcel dest, int flags) {
+    @Override
+    public void writeToParcelInternal(@NonNull Parcel dest, int flags) {
         dest.writeInt(mSize);
         dest.writeInt(mDip ? 1 : 0);
     }
 
+    /**
+     * Get the text size. This is in physical pixels if {@link #getDip()} returns false or in
+     * device-independent pixels if {@link #getDip()} returns true.
+     *
+     * @return the text size, either in physical pixels or device-independent pixels.
+     * @see AbsoluteSizeSpan#AbsoluteSizeSpan(int, boolean)
+     */
     public int getSize() {
         return mSize;
     }
 
+    /**
+     * Returns whether the size is in device-independent pixels or not, depending on the
+     * <code>dip</code> flag passed in {@link #AbsoluteSizeSpan(int, boolean)}
+     *
+     * @return <code>true</code> if the size is in device-independent pixels, <code>false</code>
+     * otherwise
+     *
+     * @see #AbsoluteSizeSpan(int, boolean)
+     */
     public boolean getDip() {
         return mDip;
     }
 
     @Override
-    public void updateDrawState(TextPaint ds) {
+    public void updateDrawState(@NonNull TextPaint ds) {
         if (mDip) {
             ds.setTextSize(mSize * ds.density);
         } else {
@@ -89,7 +123,7 @@
     }
 
     @Override
-    public void updateMeasureState(TextPaint ds) {
+    public void updateMeasureState(@NonNull TextPaint ds) {
         if (mDip) {
             ds.setTextSize(mSize * ds.density);
         } else {
diff --git a/core/java/android/text/style/BackgroundColorSpan.java b/core/java/android/text/style/BackgroundColorSpan.java
index de05f50..44e35615 100644
--- a/core/java/android/text/style/BackgroundColorSpan.java
+++ b/core/java/android/text/style/BackgroundColorSpan.java
@@ -16,52 +16,88 @@
 
 package android.text.style;
 
+import android.annotation.ColorInt;
+import android.annotation.NonNull;
 import android.os.Parcel;
 import android.text.ParcelableSpan;
 import android.text.TextPaint;
 import android.text.TextUtils;
 
+/**
+ * Changes the background color of the text to which the span is attached.
+ * <p>
+ * For example, to set a green background color for a text you would create a {@link
+ * android.text.SpannableString} based on the text and set the span.
+ * <pre>{@code
+ * SpannableString string = new SpannableString("Text with a background color span");
+ *string.setSpan(new BackgroundColorSpan(color), 12, 28, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);}</pre>
+ * <img src="{@docRoot}reference/android/images/text/style/backgroundcolorspan.png" />
+ * <figcaption>Set a background color for the text.</figcaption>
+ */
 public class BackgroundColorSpan extends CharacterStyle
         implements UpdateAppearance, ParcelableSpan {
 
     private final int mColor;
 
-    public BackgroundColorSpan(int color) {
+    /**
+     * Creates a {@link BackgroundColorSpan} from a color integer.
+     * <p>
+     *
+     * @param color color integer that defines the background color
+     * @see android.content.res.Resources#getColor(int, Resources.Theme)
+     */
+    public BackgroundColorSpan(@ColorInt int color) {
         mColor = color;
     }
 
-    public BackgroundColorSpan(Parcel src) {
+    /**
+     * Creates a {@link BackgroundColorSpan} from a parcel.
+     */
+    public BackgroundColorSpan(@NonNull Parcel src) {
         mColor = src.readInt();
     }
-    
+
+    @Override
     public int getSpanTypeId() {
         return getSpanTypeIdInternal();
     }
 
     /** @hide */
+    @Override
     public int getSpanTypeIdInternal() {
         return TextUtils.BACKGROUND_COLOR_SPAN;
     }
-    
+
+    @Override
     public int describeContents() {
         return 0;
     }
 
-    public void writeToParcel(Parcel dest, int flags) {
+    @Override
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
         writeToParcelInternal(dest, flags);
     }
 
     /** @hide */
-    public void writeToParcelInternal(Parcel dest, int flags) {
+    @Override
+    public void writeToParcelInternal(@NonNull Parcel dest, int flags) {
         dest.writeInt(mColor);
     }
 
+    /**
+     * @return the background color of this span.
+     * @see BackgroundColorSpan#BackgroundColorSpan(int)
+     */
+    @ColorInt
     public int getBackgroundColor() {
         return mColor;
     }
 
+    /**
+     * Updates the background color of the TextPaint.
+     */
     @Override
-    public void updateDrawState(TextPaint ds) {
-        ds.bgColor = mColor;
+    public void updateDrawState(@NonNull TextPaint textPaint) {
+        textPaint.bgColor = mColor;
     }
 }
diff --git a/core/java/android/text/style/BulletSpan.java b/core/java/android/text/style/BulletSpan.java
index 43dd0ff..70175c8 100644
--- a/core/java/android/text/style/BulletSpan.java
+++ b/core/java/android/text/style/BulletSpan.java
@@ -16,6 +16,11 @@
 
 package android.text.style;
 
+import android.annotation.ColorInt;
+import android.annotation.IntRange;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.Px;
 import android.graphics.Canvas;
 import android.graphics.Paint;
 import android.graphics.Path;
@@ -26,38 +31,108 @@
 import android.text.Spanned;
 import android.text.TextUtils;
 
+/**
+ * A span which styles paragraphs as bullet points (respecting layout direction).
+ * <p>
+ * BulletSpans must be attached from the first character to the last character of a single
+ * paragraph, otherwise the bullet point will not be displayed but the first paragraph encountered
+ * will have a leading margin.
+ * <p>
+ * BulletSpans allow configuring the following elements:
+ * <ul>
+ * <li><b>gap width</b> - the distance, in pixels, between the bullet point and the paragraph.
+ * Default value is 2px.</li>
+ * <li><b>color</b> - the bullet point color. By default, the bullet point color is 0 - no color,
+ * so it uses the TextView's text color.</li>
+ * <li><b>bullet radius</b> - the radius, in pixels, of the bullet point. Default value is
+ * 4px.</li>
+ * </ul>
+ * For example, a BulletSpan using the default values can be constructed like this:
+ * <pre>{@code
+ *  SpannableString string = new SpannableString("Text with\nBullet point");
+ *string.setSpan(new BulletSpan(), 10, 22, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);}</pre>
+ * <img src="{@docRoot}reference/android/images/text/style/defaultbulletspan.png" />
+ * <figcaption>BulletSpan constructed with default values.</figcaption>
+ * <p>
+ * <p>
+ * To construct a BulletSpan with a gap width of 40px, green bullet point and bullet radius of
+ * 20px:
+ * <pre>{@code
+ *  SpannableString string = new SpannableString("Text with\nBullet point");
+ *string.setSpan(new BulletSpan(40, color, 20), 10, 22, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);}</pre>
+ * <img src="{@docRoot}reference/android/images/text/style/custombulletspan.png" />
+ * <figcaption>Customized BulletSpan.</figcaption>
+ */
 public class BulletSpan implements LeadingMarginSpan, ParcelableSpan {
-    private final int mGapWidth;
-    private final boolean mWantColor;
-    private final int mColor;
-
     // Bullet is slightly bigger to avoid aliasing artifacts on mdpi devices.
-    private static final float BULLET_RADIUS = 3 * 1.2f;
-    private static Path sBulletPath = null;
+    private static final int STANDARD_BULLET_RADIUS = 4;
     public static final int STANDARD_GAP_WIDTH = 2;
+    private static final int STANDARD_COLOR = 0;
 
+    @Px
+    private final int mGapWidth;
+    @Px
+    private final int mBulletRadius;
+    private Path mBulletPath = null;
+    @ColorInt
+    private final int mColor;
+    private final boolean mWantColor;
+
+    /**
+     * Creates a {@link BulletSpan} with the default values.
+     */
     public BulletSpan() {
-        mGapWidth = STANDARD_GAP_WIDTH;
-        mWantColor = false;
-        mColor = 0;
+        this(STANDARD_GAP_WIDTH, STANDARD_COLOR, false, STANDARD_BULLET_RADIUS);
     }
 
+    /**
+     * Creates a {@link BulletSpan} based on a gap width
+     *
+     * @param gapWidth the distance, in pixels, between the bullet point and the paragraph.
+     */
     public BulletSpan(int gapWidth) {
-        mGapWidth = gapWidth;
-        mWantColor = false;
-        mColor = 0;
+        this(gapWidth, STANDARD_COLOR, false, STANDARD_BULLET_RADIUS);
     }
 
-    public BulletSpan(int gapWidth, int color) {
+    /**
+     * Creates a {@link BulletSpan} based on a gap width and a color integer.
+     *
+     * @param gapWidth the distance, in pixels, between the bullet point and the paragraph.
+     * @param color    the bullet point color, as a color integer
+     * @see android.content.res.Resources#getColor(int, Resources.Theme)
+     */
+    public BulletSpan(int gapWidth, @ColorInt int color) {
+        this(gapWidth, color, true, STANDARD_BULLET_RADIUS);
+    }
+
+    /**
+     * Creates a {@link BulletSpan} based on a gap width and a color integer.
+     *
+     * @param gapWidth     the distance, in pixels, between the bullet point and the paragraph.
+     * @param color        the bullet point color, as a color integer.
+     * @param bulletRadius the radius of the bullet point, in pixels.
+     * @see android.content.res.Resources#getColor(int, Resources.Theme)
+     */
+    public BulletSpan(int gapWidth, @ColorInt int color, @IntRange(from = 0) int bulletRadius) {
+        this(gapWidth, color, true, bulletRadius);
+    }
+
+    private BulletSpan(int gapWidth, @ColorInt int color, boolean wantColor,
+            @IntRange(from = 0) int bulletRadius) {
         mGapWidth = gapWidth;
-        mWantColor = true;
+        mBulletRadius = bulletRadius;
         mColor = color;
+        mWantColor = wantColor;
     }
 
-    public BulletSpan(Parcel src) {
+    /**
+     * Creates a {@link BulletSpan} from a parcel.
+     */
+    public BulletSpan(@NonNull Parcel src) {
         mGapWidth = src.readInt();
         mWantColor = src.readInt() != 0;
         mColor = src.readInt();
+        mBulletRadius = src.readInt();
     }
 
     @Override
@@ -77,68 +152,97 @@
     }
 
     @Override
-    public void writeToParcel(Parcel dest, int flags) {
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
         writeToParcelInternal(dest, flags);
     }
 
     /** @hide */
     @Override
-    public void writeToParcelInternal(Parcel dest, int flags) {
+    public void writeToParcelInternal(@NonNull Parcel dest, int flags) {
         dest.writeInt(mGapWidth);
         dest.writeInt(mWantColor ? 1 : 0);
         dest.writeInt(mColor);
+        dest.writeInt(mBulletRadius);
     }
 
     @Override
     public int getLeadingMargin(boolean first) {
-        return (int) (2 * BULLET_RADIUS + mGapWidth);
+        return 2 * mBulletRadius + mGapWidth;
+    }
+
+    /**
+     * Get the distance, in pixels, between the bullet point and the paragraph.
+     *
+     * @return the distance, in pixels, between the bullet point and the paragraph.
+     */
+    public int getGapWidth() {
+        return mGapWidth;
+    }
+
+    /**
+     * Get the radius, in pixels, of the bullet point.
+     *
+     * @return the radius, in pixels, of the bullet point.
+     */
+    public int getBulletRadius() {
+        return mBulletRadius;
+    }
+
+    /**
+     * Get the bullet point color.
+     *
+     * @return the bullet point color
+     */
+    public int getColor() {
+        return mColor;
     }
 
     @Override
-    public void drawLeadingMargin(Canvas c, Paint p, int x, int dir,
-                                  int top, int baseline, int bottom,
-                                  CharSequence text, int start, int end,
-                                  boolean first, Layout l) {
+    public void drawLeadingMargin(@NonNull Canvas canvas, @NonNull Paint paint, int x, int dir,
+            int top, int baseline, int bottom,
+            @NonNull CharSequence text, int start, int end,
+            boolean first, @Nullable Layout layout) {
         if (((Spanned) text).getSpanStart(this) == start) {
-            Paint.Style style = p.getStyle();
+            Paint.Style style = paint.getStyle();
             int oldcolor = 0;
 
             if (mWantColor) {
-                oldcolor = p.getColor();
-                p.setColor(mColor);
+                oldcolor = paint.getColor();
+                paint.setColor(mColor);
             }
 
-            p.setStyle(Paint.Style.FILL);
+            paint.setStyle(Paint.Style.FILL);
 
-            if (l != null) {
+            if (layout != null) {
                 // "bottom" position might include extra space as a result of line spacing
                 // configuration. Subtract extra space in order to show bullet in the vertical
                 // center of characters.
-                final int line = l.getLineForOffset(start);
-                bottom = bottom - l.getLineExtra(line);
+                final int line = layout.getLineForOffset(start);
+                bottom = bottom - layout.getLineExtra(line);
             }
 
-            final float y = (top + bottom) / 2f;
+            final float yPosition = (top + bottom) / 2f;
+            final float xPosition = x + dir * mBulletRadius;
 
-            if (c.isHardwareAccelerated()) {
-                if (sBulletPath == null) {
-                    sBulletPath = new Path();
-                    sBulletPath.addCircle(0.0f, 0.0f, BULLET_RADIUS, Direction.CW);
+            if (canvas.isHardwareAccelerated()) {
+                if (mBulletPath == null) {
+                    mBulletPath = new Path();
+                    mBulletPath.addCircle(0.0f, 0.0f, mBulletRadius, Direction.CW);
                 }
 
-                c.save();
-                c.translate(x + dir * BULLET_RADIUS, y);
-                c.drawPath(sBulletPath, p);
-                c.restore();
+                canvas.save();
+                canvas.translate(xPosition, yPosition);
+                canvas.drawPath(mBulletPath, paint);
+                canvas.restore();
             } else {
-                c.drawCircle(x + dir * BULLET_RADIUS, y, BULLET_RADIUS, p);
+                canvas.drawCircle(xPosition, yPosition, mBulletRadius, paint);
             }
 
             if (mWantColor) {
-                p.setColor(oldcolor);
+                paint.setColor(oldcolor);
             }
 
-            p.setStyle(style);
+            paint.setStyle(style);
         }
     }
 }
diff --git a/core/java/android/text/style/ForegroundColorSpan.java b/core/java/android/text/style/ForegroundColorSpan.java
index 2bc6d54..f7706745 100644
--- a/core/java/android/text/style/ForegroundColorSpan.java
+++ b/core/java/android/text/style/ForegroundColorSpan.java
@@ -17,53 +17,88 @@
 package android.text.style;
 
 import android.annotation.ColorInt;
+import android.annotation.NonNull;
 import android.os.Parcel;
 import android.text.ParcelableSpan;
 import android.text.TextPaint;
 import android.text.TextUtils;
 
+/**
+ * Changes the color of the text to which the span is attached.
+ * <p>
+ * For example, to set a green text color you would create a {@link
+ * android.text.SpannableString} based on the text and set the span.
+ * <pre>{@code
+ * SpannableString string = new SpannableString("Text with a foreground color span");
+ *string.setSpan(new ForegroundColorSpan(color), 12, 28, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);}</pre>
+ * <img src="{@docRoot}reference/android/images/text/style/foregroundcolorspan.png" />
+ * <figcaption>Set a text color.</figcaption>
+ */
 public class ForegroundColorSpan extends CharacterStyle
         implements UpdateAppearance, ParcelableSpan {
 
     private final int mColor;
 
+    /**
+     * Creates a {@link ForegroundColorSpan} from a color integer.
+     * <p>
+     * To get the color integer associated with a particular color resource ID, use
+     * {@link android.content.res.Resources#getColor(int, Resources.Theme)}
+     *
+     * @param color color integer that defines the text color
+     */
     public ForegroundColorSpan(@ColorInt int color) {
         mColor = color;
     }
 
-    public ForegroundColorSpan(Parcel src) {
+    /**
+     * Creates a {@link ForegroundColorSpan} from a parcel.
+     */
+    public ForegroundColorSpan(@NonNull Parcel src) {
         mColor = src.readInt();
     }
-    
+
+    @Override
     public int getSpanTypeId() {
         return getSpanTypeIdInternal();
     }
 
     /** @hide */
+    @Override
     public int getSpanTypeIdInternal() {
         return TextUtils.FOREGROUND_COLOR_SPAN;
     }
 
+    @Override
     public int describeContents() {
         return 0;
     }
 
-    public void writeToParcel(Parcel dest, int flags) {
+    @Override
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
         writeToParcelInternal(dest, flags);
     }
 
     /** @hide */
-    public void writeToParcelInternal(Parcel dest, int flags) {
+    @Override
+    public void writeToParcelInternal(@NonNull Parcel dest, int flags) {
         dest.writeInt(mColor);
     }
 
+    /**
+     * @return the foreground color of this span.
+     * @see ForegroundColorSpan#ForegroundColorSpan(int)
+     */
     @ColorInt
     public int getForegroundColor() {
         return mColor;
     }
 
+    /**
+     * Updates the color of the TextPaint to the foreground color.
+     */
     @Override
-    public void updateDrawState(TextPaint ds) {
-        ds.setColor(mColor);
+    public void updateDrawState(@NonNull TextPaint textPaint) {
+        textPaint.setColor(mColor);
     }
 }
diff --git a/core/java/android/text/style/RelativeSizeSpan.java b/core/java/android/text/style/RelativeSizeSpan.java
index 95f048a..3094f27 100644
--- a/core/java/android/text/style/RelativeSizeSpan.java
+++ b/core/java/android/text/style/RelativeSizeSpan.java
@@ -16,56 +16,85 @@
 
 package android.text.style;
 
+import android.annotation.FloatRange;
+import android.annotation.NonNull;
 import android.os.Parcel;
 import android.text.ParcelableSpan;
 import android.text.TextPaint;
 import android.text.TextUtils;
 
+/**
+ * Uniformly scales the size of the text to which it's attached by a certain proportion.
+ * <p>
+ * For example, a <code>RelativeSizeSpan</code> that increases the text size by 50% can be
+ * constructed like this:
+ * <pre>{@code
+ *  SpannableString string = new SpannableString("Text with relative size span");
+ *string.setSpan(new RelativeSizeSpan(1.5f), 10, 24, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);}</pre>
+ * <img src="{@docRoot}reference/android/images/text/style/relativesizespan.png" />
+ * <figcaption>Text increased by 50% with <code>RelativeSizeSpan</code>.</figcaption>
+ */
 public class RelativeSizeSpan extends MetricAffectingSpan implements ParcelableSpan {
 
     private final float mProportion;
 
-    public RelativeSizeSpan(float proportion) {
+    /**
+     * Creates a {@link RelativeSizeSpan} based on a proportion.
+     *
+     * @param proportion the proportion with which the text is scaled.
+     */
+    public RelativeSizeSpan(@FloatRange(from = 0) float proportion) {
         mProportion = proportion;
     }
 
-    public RelativeSizeSpan(Parcel src) {
+    /**
+     * Creates a {@link RelativeSizeSpan} from a parcel.
+     */
+    public RelativeSizeSpan(@NonNull Parcel src) {
         mProportion = src.readFloat();
     }
-    
+
+    @Override
     public int getSpanTypeId() {
         return getSpanTypeIdInternal();
     }
 
     /** @hide */
+    @Override
     public int getSpanTypeIdInternal() {
         return TextUtils.RELATIVE_SIZE_SPAN;
     }
-    
+
+    @Override
     public int describeContents() {
         return 0;
     }
 
-    public void writeToParcel(Parcel dest, int flags) {
+    @Override
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
         writeToParcelInternal(dest, flags);
     }
 
     /** @hide */
-    public void writeToParcelInternal(Parcel dest, int flags) {
+    @Override
+    public void writeToParcelInternal(@NonNull Parcel dest, int flags) {
         dest.writeFloat(mProportion);
     }
 
+    /**
+     * @return the proportion with which the text size is changed.
+     */
     public float getSizeChange() {
         return mProportion;
     }
 
     @Override
-    public void updateDrawState(TextPaint ds) {
+    public void updateDrawState(@NonNull TextPaint ds) {
         ds.setTextSize(ds.getTextSize() * mProportion);
     }
 
     @Override
-    public void updateMeasureState(TextPaint ds) {
+    public void updateMeasureState(@NonNull TextPaint ds) {
         ds.setTextSize(ds.getTextSize() * mProportion);
     }
 }
diff --git a/core/java/android/text/style/ScaleXSpan.java b/core/java/android/text/style/ScaleXSpan.java
index d0850185..6ef4cec 100644
--- a/core/java/android/text/style/ScaleXSpan.java
+++ b/core/java/android/text/style/ScaleXSpan.java
@@ -16,45 +16,79 @@
 
 package android.text.style;
 
+import android.annotation.FloatRange;
+import android.annotation.NonNull;
 import android.os.Parcel;
 import android.text.ParcelableSpan;
 import android.text.TextPaint;
 import android.text.TextUtils;
 
+/**
+ * Scales horizontally the size of the text to which it's attached by a certain factor.
+ * <p>
+ * Values > 1.0 will stretch the text wider. Values < 1.0 will stretch the text narrower.
+ * <p>
+ * For example, a <code>ScaleXSpan</code> that stretches the text size by 100% can be
+ * constructed like this:
+ * <pre>{@code
+ * SpannableString string = new SpannableString("Text with ScaleX span");
+ *string.setSpan(new ScaleXSpan(2f), 10, 16, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);}</pre>
+ * <img src="{@docRoot}reference/android/images/text/style/scalexspan.png" />
+ * <figcaption>Text scaled by 100% with <code>ScaleXSpan</code>.</figcaption>
+ */
 public class ScaleXSpan extends MetricAffectingSpan implements ParcelableSpan {
 
     private final float mProportion;
 
-    public ScaleXSpan(float proportion) {
+    /**
+     * Creates a {@link ScaleXSpan} based on a proportion. Values > 1.0 will stretch the text wider.
+     * Values < 1.0 will stretch the text narrower.
+     *
+     * @param proportion the horizontal scale factor.
+     */
+    public ScaleXSpan(@FloatRange(from = 0) float proportion) {
         mProportion = proportion;
     }
 
-    public ScaleXSpan(Parcel src) {
+    /**
+     * Creates a {@link ScaleXSpan} from a parcel.
+     */
+    public ScaleXSpan(@NonNull Parcel src) {
         mProportion = src.readFloat();
     }
-    
+
+    @Override
     public int getSpanTypeId() {
         return getSpanTypeIdInternal();
     }
 
     /** @hide */
+    @Override
     public int getSpanTypeIdInternal() {
         return TextUtils.SCALE_X_SPAN;
     }
-    
+
+    @Override
     public int describeContents() {
         return 0;
     }
 
-    public void writeToParcel(Parcel dest, int flags) {
+    @Override
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
         writeToParcelInternal(dest, flags);
     }
 
     /** @hide */
-    public void writeToParcelInternal(Parcel dest, int flags) {
+    @Override
+    public void writeToParcelInternal(@NonNull Parcel dest, int flags) {
         dest.writeFloat(mProportion);
     }
 
+    /**
+     * Get the horizontal scale factor for the text.
+     *
+     * @return the horizontal scale factor.
+     */
     public float getScaleX() {
         return mProportion;
     }
diff --git a/core/java/android/text/style/StrikethroughSpan.java b/core/java/android/text/style/StrikethroughSpan.java
index 1389704..a630505 100644
--- a/core/java/android/text/style/StrikethroughSpan.java
+++ b/core/java/android/text/style/StrikethroughSpan.java
@@ -16,42 +16,65 @@
 
 package android.text.style;
 
+import android.annotation.NonNull;
 import android.os.Parcel;
 import android.text.ParcelableSpan;
 import android.text.TextPaint;
 import android.text.TextUtils;
 
+/**
+ * A span that strikes through the text it's attached to.
+ * <p>
+ * The span can be used like this:
+ * <pre>{@code
+ * SpannableString string = new SpannableString("Text with strikethrough span");
+ *string.setSpan(new StrikethroughSpan(), 10, 23, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);}</pre>
+ * <img src="{@docRoot}reference/android/images/text/style/strikethroughspan.png" />
+ * <figcaption>Strikethrough text.</figcaption>
+ */
 public class StrikethroughSpan extends CharacterStyle
         implements UpdateAppearance, ParcelableSpan {
+
+    /**
+     * Creates a {@link StrikethroughSpan}.
+     */
     public StrikethroughSpan() {
     }
-    
-    public StrikethroughSpan(Parcel src) {
+
+    /**
+     * Creates a {@link StrikethroughSpan} from a parcel.
+     */
+    public StrikethroughSpan(@NonNull Parcel src) {
     }
-    
+
+    @Override
     public int getSpanTypeId() {
         return getSpanTypeIdInternal();
     }
 
     /** @hide */
+    @Override
     public int getSpanTypeIdInternal() {
         return TextUtils.STRIKETHROUGH_SPAN;
     }
-    
+
+    @Override
     public int describeContents() {
         return 0;
     }
 
-    public void writeToParcel(Parcel dest, int flags) {
+    @Override
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
         writeToParcelInternal(dest, flags);
     }
 
     /** @hide */
-    public void writeToParcelInternal(Parcel dest, int flags) {
+    @Override
+    public void writeToParcelInternal(@NonNull Parcel dest, int flags) {
     }
 
     @Override
-    public void updateDrawState(TextPaint ds) {
+    public void updateDrawState(@NonNull TextPaint ds) {
         ds.setStrikeThruText(true);
     }
 }
diff --git a/core/java/android/text/style/SubscriptSpan.java b/core/java/android/text/style/SubscriptSpan.java
index f1b0d38..3d15aad 100644
--- a/core/java/android/text/style/SubscriptSpan.java
+++ b/core/java/android/text/style/SubscriptSpan.java
@@ -16,46 +16,74 @@
 
 package android.text.style;
 
+import android.annotation.NonNull;
 import android.os.Parcel;
 import android.text.ParcelableSpan;
 import android.text.TextPaint;
 import android.text.TextUtils;
 
+/**
+ * The span that moves the position of the text baseline lower.
+ * <p>
+ * The span can be used like this:
+ * <pre>{@code
+ *  SpannableString string = new SpannableString("☕- C8H10N4O2\n");
+ *string.setSpan(new SubscriptSpan(), 4, 5, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
+ *string.setSpan(new SubscriptSpan(), 6, 8, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
+ *string.setSpan(new SubscriptSpan(), 9, 10, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
+ *string.setSpan(new SubscriptSpan(), 11, 12, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);}</pre>
+ * <img src="{@docRoot}reference/android/images/text/style/subscriptspan.png" />
+ * <figcaption>Text with <code>SubscriptSpan</code>.</figcaption>
+ * Note: Since the span affects the position of the text, if the text is on the last line of a
+ * TextView, it may appear cut.
+ */
 public class SubscriptSpan extends MetricAffectingSpan implements ParcelableSpan {
+
+    /**
+     * Creates a {@link SubscriptSpan}.
+     */
     public SubscriptSpan() {
     }
-    
-    public SubscriptSpan(Parcel src) {
+
+    /**
+     * Creates a {@link SubscriptSpan} from a parcel.
+     */
+    public SubscriptSpan(@NonNull Parcel src) {
     }
-    
+
+    @Override
     public int getSpanTypeId() {
         return getSpanTypeIdInternal();
     }
 
     /** @hide */
+    @Override
     public int getSpanTypeIdInternal() {
         return TextUtils.SUBSCRIPT_SPAN;
     }
-    
+
+    @Override
     public int describeContents() {
         return 0;
     }
 
+    @Override
     public void writeToParcel(Parcel dest, int flags) {
         writeToParcelInternal(dest, flags);
     }
 
     /** @hide */
+    @Override
     public void writeToParcelInternal(Parcel dest, int flags) {
     }
 
     @Override
-    public void updateDrawState(TextPaint tp) {
-        tp.baselineShift -= (int) (tp.ascent() / 2);
+    public void updateDrawState(@NonNull TextPaint textPaint) {
+        textPaint.baselineShift -= (int) (textPaint.ascent() / 2);
     }
 
     @Override
-    public void updateMeasureState(TextPaint tp) {
-        tp.baselineShift -= (int) (tp.ascent() / 2);
+    public void updateMeasureState(@NonNull TextPaint textPaint) {
+        textPaint.baselineShift -= (int) (textPaint.ascent() / 2);
     }
 }
diff --git a/core/java/android/text/style/SuperscriptSpan.java b/core/java/android/text/style/SuperscriptSpan.java
index abcf688..3dc9d3f 100644
--- a/core/java/android/text/style/SuperscriptSpan.java
+++ b/core/java/android/text/style/SuperscriptSpan.java
@@ -16,46 +16,71 @@
 
 package android.text.style;
 
+import android.annotation.NonNull;
 import android.os.Parcel;
 import android.text.ParcelableSpan;
 import android.text.TextPaint;
 import android.text.TextUtils;
 
+/**
+ * The span that moves the position of the text baseline higher.
+ * <p>
+ * The span can be used like this:
+ * <pre>{@code
+ *  SpannableString string = new SpannableString("1st example");
+ *string.setSpan(new SuperscriptSpan(), 1, 3, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);}</pre>
+ * <img src="{@docRoot}reference/android/images/text/style/superscriptspan.png" />
+ * <figcaption>Text with <code>SuperscriptSpan</code>.</figcaption>
+ * Note: Since the span affects the position of the text, if the text is on the first line of a
+ * TextView, it may appear cut. This can be avoided by decreasing the text size with an {@link
+ * AbsoluteSizeSpan}
+ */
 public class SuperscriptSpan extends MetricAffectingSpan implements ParcelableSpan {
+    /**
+     * Creates a {@link SuperscriptSpan}.
+     */
     public SuperscriptSpan() {
     }
-    
-    public SuperscriptSpan(Parcel src) {
+
+    /**
+     * Creates a {@link SuperscriptSpan} from a parcel.
+     */
+    public SuperscriptSpan(@NonNull Parcel src) {
     }
-    
+
+    @Override
     public int getSpanTypeId() {
         return getSpanTypeIdInternal();
     }
 
     /** @hide */
+    @Override
     public int getSpanTypeIdInternal() {
         return TextUtils.SUPERSCRIPT_SPAN;
     }
-    
+
+    @Override
     public int describeContents() {
         return 0;
     }
 
-    public void writeToParcel(Parcel dest, int flags) {
+    @Override
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
         writeToParcelInternal(dest, flags);
     }
 
     /** @hide */
-    public void writeToParcelInternal(Parcel dest, int flags) {
+    @Override
+    public void writeToParcelInternal(@NonNull Parcel dest, int flags) {
     }
 
     @Override
-    public void updateDrawState(TextPaint tp) {
-        tp.baselineShift += (int) (tp.ascent() / 2);
+    public void updateDrawState(@NonNull TextPaint textPaint) {
+        textPaint.baselineShift += (int) (textPaint.ascent() / 2);
     }
 
     @Override
-    public void updateMeasureState(TextPaint tp) {
-        tp.baselineShift += (int) (tp.ascent() / 2);
+    public void updateMeasureState(@NonNull TextPaint textPaint) {
+        textPaint.baselineShift += (int) (textPaint.ascent() / 2);
     }
 }
diff --git a/core/java/android/text/style/UnderlineSpan.java b/core/java/android/text/style/UnderlineSpan.java
index 9024dcd..800838e 100644
--- a/core/java/android/text/style/UnderlineSpan.java
+++ b/core/java/android/text/style/UnderlineSpan.java
@@ -16,42 +16,65 @@
 
 package android.text.style;
 
+import android.annotation.NonNull;
 import android.os.Parcel;
 import android.text.ParcelableSpan;
 import android.text.TextPaint;
 import android.text.TextUtils;
 
+/**
+ * A span that underlines the text it's attached to.
+ * <p>
+ * The span can be used like this:
+ * <pre>{@code
+ * SpannableString string = new SpannableString("Text with underline span");
+ *string.setSpan(new UnderlineSpan(), 10, 19, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);}</pre>
+ * <img src="{@docRoot}reference/android/images/text/style/underlinespan.png" />
+ * <figcaption>Underlined text.</figcaption>
+ */
 public class UnderlineSpan extends CharacterStyle
         implements UpdateAppearance, ParcelableSpan {
+
+    /**
+     * Creates an {@link UnderlineSpan}.
+     */
     public UnderlineSpan() {
     }
-    
-    public UnderlineSpan(Parcel src) {
+
+    /**
+     * Creates an {@link UnderlineSpan} from a parcel.
+     */
+    public UnderlineSpan(@NonNull Parcel src) {
     }
-    
+
+    @Override
     public int getSpanTypeId() {
         return getSpanTypeIdInternal();
     }
 
     /** @hide */
+    @Override
     public int getSpanTypeIdInternal() {
         return TextUtils.UNDERLINE_SPAN;
     }
-    
+
+    @Override
     public int describeContents() {
         return 0;
     }
 
-    public void writeToParcel(Parcel dest, int flags) {
+    @Override
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
         writeToParcelInternal(dest, flags);
     }
 
     /** @hide */
-    public void writeToParcelInternal(Parcel dest, int flags) {
+    @Override
+    public void writeToParcelInternal(@NonNull Parcel dest, int flags) {
     }
 
     @Override
-    public void updateDrawState(TextPaint ds) {
+    public void updateDrawState(@NonNull TextPaint ds) {
         ds.setUnderlineText(true);
     }
 }
diff --git a/core/java/android/util/FeatureFlagUtils.java b/core/java/android/util/FeatureFlagUtils.java
index e94f91a..9f9033c 100644
--- a/core/java/android/util/FeatureFlagUtils.java
+++ b/core/java/android/util/FeatureFlagUtils.java
@@ -43,8 +43,9 @@
         DEFAULT_FLAGS.put("settings_battery_v2", "false");
         DEFAULT_FLAGS.put("settings_battery_display_app_list", "false");
         DEFAULT_FLAGS.put("settings_security_settings_v2", "true");
-        DEFAULT_FLAGS.put("settings_zone_picker_v2", "false");
+        DEFAULT_FLAGS.put("settings_zone_picker_v2", "true");
         DEFAULT_FLAGS.put("settings_suggestion_ui_v2", "false");
+        DEFAULT_FLAGS.put("settings_about_phone_v2", "false");
     }
 
     /**
diff --git a/core/java/android/util/apk/ApkSignatureSchemeV2Verifier.java b/core/java/android/util/apk/ApkSignatureSchemeV2Verifier.java
index ce8998f..5a09dab 100644
--- a/core/java/android/util/apk/ApkSignatureSchemeV2Verifier.java
+++ b/core/java/android/util/apk/ApkSignatureSchemeV2Verifier.java
@@ -16,6 +16,7 @@
 
 package android.util.apk;
 
+import static android.util.apk.ApkSigningBlockUtils.CONTENT_DIGEST_VERITY_CHUNKED_SHA256;
 import static android.util.apk.ApkSigningBlockUtils.SIGNATURE_DSA_WITH_SHA256;
 import static android.util.apk.ApkSigningBlockUtils.SIGNATURE_ECDSA_WITH_SHA256;
 import static android.util.apk.ApkSigningBlockUtils.SIGNATURE_ECDSA_WITH_SHA512;
@@ -42,6 +43,7 @@
 import java.io.RandomAccessFile;
 import java.nio.BufferUnderflowException;
 import java.nio.ByteBuffer;
+import java.security.DigestException;
 import java.security.InvalidAlgorithmParameterException;
 import java.security.InvalidKeyException;
 import java.security.KeyFactory;
@@ -105,7 +107,8 @@
      */
     public static X509Certificate[][] verify(String apkFile)
             throws SignatureNotFoundException, SecurityException, IOException {
-        return verify(apkFile, true);
+        VerifiedSigner vSigner = verify(apkFile, true);
+        return vSigner.certs;
     }
 
     /**
@@ -119,10 +122,11 @@
      */
     public static X509Certificate[][] plsCertsNoVerifyOnlyCerts(String apkFile)
             throws SignatureNotFoundException, SecurityException, IOException {
-        return verify(apkFile, false);
+        VerifiedSigner vSigner = verify(apkFile, false);
+        return vSigner.certs;
     }
 
-    private static X509Certificate[][] verify(String apkFile, boolean verifyIntegrity)
+    private static VerifiedSigner verify(String apkFile, boolean verifyIntegrity)
             throws SignatureNotFoundException, SecurityException, IOException {
         try (RandomAccessFile apk = new RandomAccessFile(apkFile, "r")) {
             return verify(apk, verifyIntegrity);
@@ -138,7 +142,7 @@
      *         verify.
      * @throws IOException if an I/O error occurs while reading the APK file.
      */
-    private static X509Certificate[][] verify(RandomAccessFile apk, boolean verifyIntegrity)
+    private static VerifiedSigner verify(RandomAccessFile apk, boolean verifyIntegrity)
             throws SignatureNotFoundException, SecurityException, IOException {
         SignatureInfo signatureInfo = findSignature(apk);
         return verify(apk, signatureInfo, verifyIntegrity);
@@ -163,7 +167,7 @@
      * @param signatureInfo APK Signature Scheme v2 Block and information relevant for verifying it
      *        against the APK file.
      */
-    private static X509Certificate[][] verify(
+    private static VerifiedSigner verify(
             RandomAccessFile apk,
             SignatureInfo signatureInfo,
             boolean doVerifyIntegrity) throws SecurityException, IOException {
@@ -207,7 +211,14 @@
             ApkSigningBlockUtils.verifyIntegrity(contentDigests, apk, signatureInfo);
         }
 
-        return signerCerts.toArray(new X509Certificate[signerCerts.size()][]);
+        byte[] verityRootHash = null;
+        if (contentDigests.containsKey(CONTENT_DIGEST_VERITY_CHUNKED_SHA256)) {
+            verityRootHash = contentDigests.get(CONTENT_DIGEST_VERITY_CHUNKED_SHA256);
+        }
+
+        return new VerifiedSigner(
+                signerCerts.toArray(new X509Certificate[signerCerts.size()][]),
+                verityRootHash);
     }
 
     private static X509Certificate[] verifySigner(
@@ -383,6 +394,24 @@
         return;
     }
 
+    static byte[] getVerityRootHash(String apkPath)
+            throws IOException, SignatureNotFoundException, SecurityException {
+        try (RandomAccessFile apk = new RandomAccessFile(apkPath, "r")) {
+            SignatureInfo signatureInfo = findSignature(apk);
+            VerifiedSigner vSigner = verify(apk, false);
+            return vSigner.verityRootHash;
+        }
+    }
+
+    static byte[] generateApkVerity(String apkPath, ByteBufferFactory bufferFactory)
+            throws IOException, SignatureNotFoundException, SecurityException, DigestException,
+                   NoSuchAlgorithmException {
+        try (RandomAccessFile apk = new RandomAccessFile(apkPath, "r")) {
+            SignatureInfo signatureInfo = findSignature(apk);
+            return ApkSigningBlockUtils.generateApkVerity(apkPath, bufferFactory, signatureInfo);
+        }
+    }
+
     private static boolean isSupportedSignatureAlgorithm(int sigAlgorithm) {
         switch (sigAlgorithm) {
             case SIGNATURE_RSA_PSS_WITH_SHA256:
@@ -400,4 +429,20 @@
                 return false;
         }
     }
+
+    /**
+     * Verified APK Signature Scheme v2 signer.
+     *
+     * @hide for internal use only.
+     */
+    public static class VerifiedSigner {
+        public final X509Certificate[][] certs;
+        public final byte[] verityRootHash;
+
+        public VerifiedSigner(X509Certificate[][] certs, byte[] verityRootHash) {
+            this.certs = certs;
+            this.verityRootHash = verityRootHash;
+        }
+
+    }
 }
diff --git a/core/java/android/util/apk/ApkSignatureSchemeV3Verifier.java b/core/java/android/util/apk/ApkSignatureSchemeV3Verifier.java
index c9e67fe1..1b04eb2 100644
--- a/core/java/android/util/apk/ApkSignatureSchemeV3Verifier.java
+++ b/core/java/android/util/apk/ApkSignatureSchemeV3Verifier.java
@@ -16,6 +16,7 @@
 
 package android.util.apk;
 
+import static android.util.apk.ApkSigningBlockUtils.CONTENT_DIGEST_VERITY_CHUNKED_SHA256;
 import static android.util.apk.ApkSigningBlockUtils.SIGNATURE_DSA_WITH_SHA256;
 import static android.util.apk.ApkSigningBlockUtils.SIGNATURE_ECDSA_WITH_SHA256;
 import static android.util.apk.ApkSigningBlockUtils.SIGNATURE_ECDSA_WITH_SHA512;
@@ -43,6 +44,7 @@
 import java.io.RandomAccessFile;
 import java.nio.BufferUnderflowException;
 import java.nio.ByteBuffer;
+import java.security.DigestException;
 import java.security.InvalidAlgorithmParameterException;
 import java.security.InvalidKeyException;
 import java.security.KeyFactory;
@@ -211,6 +213,10 @@
             ApkSigningBlockUtils.verifyIntegrity(contentDigests, apk, signatureInfo);
         }
 
+        if (contentDigests.containsKey(CONTENT_DIGEST_VERITY_CHUNKED_SHA256)) {
+            result.verityRootHash = contentDigests.get(CONTENT_DIGEST_VERITY_CHUNKED_SHA256);
+        }
+
         return result;
     }
 
@@ -499,6 +505,24 @@
         return new VerifiedProofOfRotation(certs, flagsList);
     }
 
+    static byte[] getVerityRootHash(String apkPath)
+            throws IOException, SignatureNotFoundException, SecurityException {
+        try (RandomAccessFile apk = new RandomAccessFile(apkPath, "r")) {
+            SignatureInfo signatureInfo = findSignature(apk);
+            VerifiedSigner vSigner = verify(apk, false);
+            return vSigner.verityRootHash;
+        }
+    }
+
+    static byte[] generateApkVerity(String apkPath, ByteBufferFactory bufferFactory)
+            throws IOException, SignatureNotFoundException, SecurityException, DigestException,
+                   NoSuchAlgorithmException {
+        try (RandomAccessFile apk = new RandomAccessFile(apkPath, "r")) {
+            SignatureInfo signatureInfo = findSignature(apk);
+            return ApkSigningBlockUtils.generateApkVerity(apkPath, bufferFactory, signatureInfo);
+        }
+    }
+
     private static boolean isSupportedSignatureAlgorithm(int sigAlgorithm) {
         switch (sigAlgorithm) {
             case SIGNATURE_RSA_PSS_WITH_SHA256:
@@ -541,6 +565,8 @@
         public final X509Certificate[] certs;
         public final VerifiedProofOfRotation por;
 
+        public byte[] verityRootHash;
+
         public VerifiedSigner(X509Certificate[] certs, VerifiedProofOfRotation por) {
             this.certs = certs;
             this.por = por;
diff --git a/core/java/android/util/apk/ApkSignatureVerifier.java b/core/java/android/util/apk/ApkSignatureVerifier.java
index 555c474..a2a7616 100644
--- a/core/java/android/util/apk/ApkSignatureVerifier.java
+++ b/core/java/android/util/apk/ApkSignatureVerifier.java
@@ -36,7 +36,9 @@
 
 import java.io.IOException;
 import java.io.InputStream;
+import java.security.DigestException;
 import java.security.GeneralSecurityException;
+import java.security.NoSuchAlgorithmException;
 import java.security.cert.Certificate;
 import java.security.cert.CertificateEncodingException;
 import java.util.ArrayList;
@@ -367,4 +369,51 @@
         // v2 didn't work, try jarsigner
         return verifyV1Signature(apkPath, false);
     }
+
+    /**
+     * @return the verity root hash in the Signing Block.
+     */
+    public static byte[] getVerityRootHash(String apkPath)
+            throws IOException, SignatureNotFoundException, SecurityException {
+        // first try v3
+        try {
+            return ApkSignatureSchemeV3Verifier.getVerityRootHash(apkPath);
+        } catch (SignatureNotFoundException e) {
+            // try older version
+        }
+        return ApkSignatureSchemeV2Verifier.getVerityRootHash(apkPath);
+    }
+
+    /**
+     * Generates the Merkle tree and verity metadata to the buffer allocated by the {@code
+     * ByteBufferFactory}.
+     *
+     * @return the verity root hash of the generated Merkle tree.
+     */
+    public static byte[] generateApkVerity(String apkPath, ByteBufferFactory bufferFactory)
+            throws IOException, SignatureNotFoundException, SecurityException, DigestException,
+                   NoSuchAlgorithmException {
+        // first try v3
+        try {
+            return ApkSignatureSchemeV3Verifier.generateApkVerity(apkPath, bufferFactory);
+        } catch (SignatureNotFoundException e) {
+            // try older version
+        }
+        return ApkSignatureSchemeV2Verifier.generateApkVerity(apkPath, bufferFactory);
+    }
+
+    /**
+     * Result of a successful APK verification operation.
+     */
+    public static class Result {
+        public final Certificate[][] certs;
+        public final Signature[] sigs;
+        public final int signatureSchemeVersion;
+
+        public Result(Certificate[][] certs, Signature[] sigs, int signingVersion) {
+            this.certs = certs;
+            this.sigs = sigs;
+            this.signatureSchemeVersion = signingVersion;
+        }
+    }
 }
diff --git a/core/java/android/util/apk/ApkSigningBlockUtils.java b/core/java/android/util/apk/ApkSigningBlockUtils.java
index 9d53847..4146f6f 100644
--- a/core/java/android/util/apk/ApkSigningBlockUtils.java
+++ b/core/java/android/util/apk/ApkSigningBlockUtils.java
@@ -306,6 +306,26 @@
     }
 
     /**
+     * Generates the fsverity header and hash tree to be used by kernel for the given apk. This
+     * method does not check whether the root hash exists in the Signing Block or not.
+     *
+     * <p>The output is stored in the {@link ByteBuffer} created by the given {@link
+     * ByteBufferFactory}.
+     *
+     * @return the root hash of the generated hash tree.
+     */
+    public static byte[] generateApkVerity(String apkPath, ByteBufferFactory bufferFactory,
+            SignatureInfo signatureInfo)
+            throws IOException, SignatureNotFoundException, SecurityException, DigestException,
+                   NoSuchAlgorithmException {
+        try (RandomAccessFile apk = new RandomAccessFile(apkPath, "r")) {
+            ApkVerityBuilder.ApkVerityResult result = ApkVerityBuilder.generateApkVerity(apk,
+                    signatureInfo, bufferFactory);
+            return result.rootHash;
+        }
+    }
+
+    /**
      * Returns the ZIP End of Central Directory (EoCD) and its offset in the file.
      *
      * @throws IOException if an I/O error occurs while reading the file.
diff --git a/core/java/android/util/apk/ApkVerityBuilder.java b/core/java/android/util/apk/ApkVerityBuilder.java
index 7412ef4..a0d5e4c 100644
--- a/core/java/android/util/apk/ApkVerityBuilder.java
+++ b/core/java/android/util/apk/ApkVerityBuilder.java
@@ -164,11 +164,11 @@
         }
 
         private void fillUpLastOutputChunk() {
-            int extra = (int) (BUFFER_SIZE - mOutput.position() % BUFFER_SIZE);
-            if (extra == 0) {
+            int lastBlockSize = (int) (mOutput.position() % BUFFER_SIZE);
+            if (lastBlockSize == 0) {
                 return;
             }
-            mOutput.put(ByteBuffer.allocate(extra));
+            mOutput.put(ByteBuffer.allocate(BUFFER_SIZE - lastBlockSize));
         }
     }
 
diff --git a/core/java/android/view/Surface.java b/core/java/android/view/Surface.java
index 1f7f8b9a..8830c90 100644
--- a/core/java/android/view/Surface.java
+++ b/core/java/android/view/Surface.java
@@ -283,6 +283,7 @@
      */
     public long getNextFrameNumber() {
         synchronized (mLock) {
+            checkNotReleasedLocked();
             return nativeGetNextFrameNumber(mNativeObject);
         }
     }
diff --git a/core/java/android/view/autofill/AutofillManager.java b/core/java/android/view/autofill/AutofillManager.java
index f54561b..4b24a71 100644
--- a/core/java/android/view/autofill/AutofillManager.java
+++ b/core/java/android/view/autofill/AutofillManager.java
@@ -33,7 +33,6 @@
 import android.os.Bundle;
 import android.os.IBinder;
 import android.os.Parcelable;
-import android.os.RemoteCallback;
 import android.os.RemoteException;
 import android.service.autofill.AutofillService;
 import android.service.autofill.FillEventHistory;
@@ -58,8 +57,6 @@
 import java.util.Collections;
 import java.util.List;
 import java.util.Objects;
-import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.TimeUnit;
 
 // TODO: use java.lang.ref.Cleaner once Android supports Java 9
 import sun.misc.Cleaner;
@@ -177,11 +174,6 @@
     public static final String EXTRA_RESTORE_SESSION_TOKEN =
             "android.view.autofill.extra.RESTORE_SESSION_TOKEN";
 
-    /** @hide */
-    public static final String EXTRA_AVAILABLE_ALGORITHMS = "available_algorithms";
-    /** @hide */
-    public static final String EXTRA_DEFAULT_ALGORITHM = "default_algorithm";
-
     private static final String SESSION_ID_TAG = "android:sessionId";
     private static final String STATE_TAG = "android:state";
     private static final String LAST_AUTOFILLED_DATA_TAG = "android:lastAutoFilledData";
@@ -1174,22 +1166,10 @@
      * and it's ignored if the caller currently doesn't have an enabled autofill service for
      * the user.
      */
-    // TODO(b/70939974): refactor this method to be "purely" sync by getting the info from the
-    // the ExtService manifest (instead of calling the service)
     @Nullable
     public String getDefaultFieldClassificationAlgorithm() {
-        final SyncRemoteCallbackListener<String> listener =
-                new SyncRemoteCallbackListener<String>() {
-
-            @Override
-            String getResult(Bundle result) {
-                return result == null ? null : result.getString(EXTRA_DEFAULT_ALGORITHM);
-            }
-        };
-
         try {
-            mService.getDefaultFieldClassificationAlgorithm(new RemoteCallback(listener));
-            return listener.getResult(FC_SERVICE_TIMEOUT);
+            return mService.getDefaultFieldClassificationAlgorithm();
         } catch (RemoteException e) {
             e.rethrowFromSystemServer();
             return null;
@@ -1204,29 +1184,12 @@
      * and it returns an empty list if the caller currently doesn't have an enabled autofill service
      * for the user.
      */
-    // TODO(b/70939974): refactor this method to be "purely" sync by getting the info from the
-    // the ExtService manifest (instead of calling the service)
     @NonNull
     public List<String> getAvailableFieldClassificationAlgorithms() {
-        final SyncRemoteCallbackListener<List<String>> listener =
-                new SyncRemoteCallbackListener<List<String>>() {
-
-            @Override
-            List<String> getResult(Bundle result) {
-                List<String> algorithms = null;
-                if (result != null) {
-                    final String[] asArray = result.getStringArray(EXTRA_AVAILABLE_ALGORITHMS);
-                    if (asArray != null) {
-                        algorithms = Arrays.asList(asArray);
-                    }
-                }
-                return algorithms != null ? algorithms : Collections.emptyList();
-            }
-        };
-
+        final String[] algorithms;
         try {
-            mService.getAvailableFieldClassificationAlgorithms(new RemoteCallback(listener));
-            return listener.getResult(FC_SERVICE_TIMEOUT);
+            algorithms = mService.getAvailableFieldClassificationAlgorithms();
+            return algorithms != null ? Arrays.asList(algorithms) : Collections.emptyList();
         } catch (RemoteException e) {
             e.rethrowFromSystemServer();
             return null;
@@ -2322,36 +2285,4 @@
             }
         }
     }
-
-    private abstract static class SyncRemoteCallbackListener<T>
-            implements RemoteCallback.OnResultListener {
-
-        private final CountDownLatch mLatch = new CountDownLatch(1);
-        private T mResult;
-
-        @Override
-        public void onResult(Bundle result) {
-            if (sVerbose) Log.w(TAG, "SyncRemoteCallbackListener.onResult(): " + result);
-            mResult = getResult(result);
-            mLatch.countDown();
-        }
-
-        T getResult(int timeoutMs) {
-            T result = null;
-            try {
-                if (mLatch.await(timeoutMs, TimeUnit.MILLISECONDS)) {
-                    result = mResult;
-                } else {
-                    Log.w(TAG, "SyncRemoteCallbackListener not called in " + timeoutMs + "ms");
-                }
-            } catch (InterruptedException e) {
-                Log.w(TAG, "SyncRemoteCallbackListener interrupted: " + e);
-                Thread.currentThread().interrupt();
-            }
-            if (sVerbose) Log.w(TAG, "SyncRemoteCallbackListener: returning " + result);
-            return result;
-        }
-
-        abstract T getResult(Bundle result);
-    }
 }
diff --git a/core/java/android/view/autofill/IAutoFillManager.aidl b/core/java/android/view/autofill/IAutoFillManager.aidl
index 41672e7..1a11fbb 100644
--- a/core/java/android/view/autofill/IAutoFillManager.aidl
+++ b/core/java/android/view/autofill/IAutoFillManager.aidl
@@ -59,6 +59,6 @@
     void setUserData(in UserData userData);
     boolean isFieldClassificationEnabled();
     ComponentName getAutofillServiceComponentName();
-    void getAvailableFieldClassificationAlgorithms(in RemoteCallback callback);
-    void getDefaultFieldClassificationAlgorithm(in RemoteCallback callback);
+    String[] getAvailableFieldClassificationAlgorithms();
+    String getDefaultFieldClassificationAlgorithm();
 }
diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java
index deb94e81..00860a4 100644
--- a/core/java/android/webkit/WebView.java
+++ b/core/java/android/webkit/WebView.java
@@ -2412,6 +2412,16 @@
         return mProvider.getTextClassifier();
     }
 
+    /**
+     * Returns the {@link ClassLoader} used to load internal WebView classes.
+     * This method is meant for use by the WebView Support Library, there is no reason to use this
+     * method otherwise.
+     */
+    @NonNull
+    public static ClassLoader getWebViewClassLoader() {
+        return getFactory().getWebViewClassLoader();
+    }
+
     //-------------------------------------------------------------------------
     // Interface for WebView providers
     //-------------------------------------------------------------------------
diff --git a/core/java/android/webkit/WebViewFactoryProvider.java b/core/java/android/webkit/WebViewFactoryProvider.java
index 3ced6a5..4f7cdab 100644
--- a/core/java/android/webkit/WebViewFactoryProvider.java
+++ b/core/java/android/webkit/WebViewFactoryProvider.java
@@ -172,4 +172,10 @@
      * @return the singleton WebViewDatabase instance
      */
     WebViewDatabase getWebViewDatabase(Context context);
+
+    /**
+     * Gets the classloader used to load internal WebView implementation classes. This interface
+     * should only be used by the WebView Support Library.
+     */
+    ClassLoader getWebViewClassLoader();
 }
diff --git a/core/java/android/widget/MediaControlView2.java b/core/java/android/widget/MediaControlView2.java
index 6e85ece..0aa2b64 100644
--- a/core/java/android/widget/MediaControlView2.java
+++ b/core/java/android/widget/MediaControlView2.java
@@ -158,6 +158,15 @@
     }
 
     @Override
+    protected void onAttachedToWindow() {
+        mProvider.onAttachedToWindow_impl();
+    }
+    @Override
+    protected void onDetachedFromWindow() {
+        mProvider.onDetachedFromWindow_impl();
+    }
+
+    @Override
     public CharSequence getAccessibilityClassName() {
         return mProvider.getAccessibilityClassName_impl();
     }
@@ -194,6 +203,16 @@
 
     private class SuperProvider implements ViewProvider {
         @Override
+        public void onAttachedToWindow_impl() {
+            MediaControlView2.super.onAttachedToWindow();
+        }
+
+        @Override
+        public void onDetachedFromWindow_impl() {
+            MediaControlView2.super.onDetachedFromWindow();
+        }
+
+        @Override
         public CharSequence getAccessibilityClassName_impl() {
             return MediaControlView2.super.getAccessibilityClassName();
         }
diff --git a/core/java/android/widget/VideoView2.java b/core/java/android/widget/VideoView2.java
index 955f053..56f3dbd 100644
--- a/core/java/android/widget/VideoView2.java
+++ b/core/java/android/widget/VideoView2.java
@@ -34,46 +34,95 @@
 import java.lang.annotation.RetentionPolicy;
 import java.util.Map;
 
+// TODO: Use @link tag to refer MediaPlayer2 in docs once MediaPlayer2.java is submitted. Same to
+// MediaSession2.
+// TODO: change the reference from MediaPlayer to MediaPlayer2.
 /**
- * TODO PUBLIC API
+ * Displays a video file.  VideoView2 class is a View class which is wrapping MediaPlayer2 so that
+ * developers can easily implement a video rendering application.
+ *
+ * <p>
+ * <em> Data sources that VideoView2 supports : </em>
+ * VideoView2 can play video files and audio-only fiels as
+ * well. It can load from various sources such as resources or content providers. The supported
+ * media file formats are the same as MediaPlayer2.
+ *
+ * <p>
+ * <em> View type can be selected : </em>
+ * VideoView2 can render videos on top of TextureView as well as
+ * SurfaceView selectively. The default is SurfaceView and it can be changed using
+ * {@link #setViewType(int)} method. Using SurfaceView is recommended in most cases for saving
+ * battery. TextureView might be preferred for supporting various UIs such as animation and
+ * translucency.
+ *
+ * <p>
+ * <em> Differences between {@link VideoView} class : </em>
+ * VideoView2 covers and inherits the most of
+ * VideoView's functionalities. The main differences are
+ * <ul>
+ * <li> VideoView2 inherits FrameLayout and renders videos using SurfaceView and TextureView
+ * selectively while VideoView inherits SurfaceView class.
+ * <li> VideoView2 is integrated with MediaControlView2 and a default MediaControlView2 instance is
+ * attached to VideoView2 by default. If a developer does not want to use the default
+ * MediaControlView2, needs to set enableControlView attribute to false. For instance,
+ * <pre>
+ * &lt;VideoView2
+ *     android:id="@+id/video_view"
+ *     xmlns:widget="http://schemas.android.com/apk/com.android.media.update"
+ *     widget:enableControlView="false" /&gt;
+ * </pre>
+ * If a developer wants to attach a customed MediaControlView2, then set enableControlView attribute
+ * to false and assign the customed media control widget using {@link #setMediaControlView2}.
+ * <li> VideoView2 is integrated with MediaPlayer2 while VideoView is integrated with MediaPlayer.
+ * <li> VideoView2 is integrated with MediaSession2 and so it responses with media key events.
+ * A VideoView2 keeps a MediaSession2 instance internally and connects it to a corresponding
+ * MediaControlView2 instance.
+ * </p>
+ * </ul>
+ *
+ * <p>
+ * <em> Audio focus and audio attributes : </em>
+ * By default, VideoView2 requests audio focus with
+ * {@link AudioManager#AUDIOFOCUS_GAIN}. Use {@link #setAudioFocusRequest(int)} to change this
+ * behavior. The default {@link AudioAttributes} used during playback have a usage of
+ * {@link AudioAttributes#USAGE_MEDIA} and a content type of
+ * {@link AudioAttributes#CONTENT_TYPE_MOVIE}, use {@link #setAudioAttributes(AudioAttributes)} to
+ * modify them.
+ *
+ * <p>
+ * Note: VideoView2 does not retain its full state when going into the background. In particular, it
+ * does not restore the current play state, play position, selected tracks. Applications should save
+ * and restore these on their own in {@link android.app.Activity#onSaveInstanceState} and
+ * {@link android.app.Activity#onRestoreInstanceState}.
+ *
  * @hide
  */
 public class VideoView2 extends FrameLayout {
+    /** @hide */
     @IntDef({
             VIEW_TYPE_TEXTUREVIEW,
             VIEW_TYPE_SURFACEVIEW
     })
     @Retention(RetentionPolicy.SOURCE)
     public @interface ViewType {}
+
     public static final int VIEW_TYPE_SURFACEVIEW = 1;
     public static final int VIEW_TYPE_TEXTUREVIEW = 2;
 
     private final VideoView2Provider mProvider;
 
-    /**
-     * @hide
-     */
     public VideoView2(@NonNull Context context) {
         this(context, null);
     }
 
-    /**
-     * @hide
-     */
     public VideoView2(@NonNull Context context, @Nullable AttributeSet attrs) {
         this(context, attrs, 0);
     }
 
-    /**
-     * @hide
-     */
     public VideoView2(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
         this(context, attrs, defStyleAttr, 0);
     }
 
-    /**
-     * @hide
-     */
     public VideoView2(
             @NonNull Context context, @Nullable AttributeSet attrs,
             int defStyleAttr, int defStyleRes) {
@@ -91,89 +140,102 @@
     }
 
     /**
-     * @hide
+     * Sets MediaControlView2 instance. It will replace the previously assigned MediaControlView2
+     * instance if any.
+     *
+     * @param mediaControlView a media control view2 instance.
      */
     public void setMediaControlView2(MediaControlView2 mediaControlView) {
         mProvider.setMediaControlView2_impl(mediaControlView);
     }
 
     /**
-     * @hide
+     * Returns MediaControlView2 instance which is currently attached to VideoView2 by default or by
+     * {@link #setMediaControlView2} method.
      */
     public MediaControlView2 getMediaControlView2() {
         return mProvider.getMediaControlView2_impl();
     }
 
     /**
-     * @hide
+     * Starts playback with the media contents specified by {@link #setVideoURI} and
+     * {@link #setVideoPath}.
+     * If it has been paused, this method will resume playback from the current position.
      */
     public void start() {
         mProvider.start_impl();
     }
 
     /**
-     * @hide
+     * Pauses playback.
      */
     public void pause() {
         mProvider.pause_impl();
     }
 
     /**
-     * @hide
+     * Gets the duration of the media content specified by #setVideoURI and #setVideoPath
+     * in milliseconds.
      */
     public int getDuration() {
         return mProvider.getDuration_impl();
     }
 
     /**
-     * @hide
+     * Gets current playback position in milliseconds.
      */
     public int getCurrentPosition() {
         return mProvider.getCurrentPosition_impl();
     }
 
+    // TODO: mention about key-frame related behavior.
     /**
-     * @hide
+     * Moves the media by specified time position.
+     * @param msec the offset in milliseconds from the start to seek to.
      */
     public void seekTo(int msec) {
         mProvider.seekTo_impl(msec);
     }
 
     /**
-     * @hide
+     * Says if the media is currently playing.
+     * @return true if the media is playing, false if it is not (eg. paused or stopped).
      */
     public boolean isPlaying() {
         return mProvider.isPlaying_impl();
     }
 
+    // TODO: check what will return if it is a local media.
     /**
-     * @hide
+     * Gets the percentage (0-100) of the content that has been buffered or played so far.
      */
     public int getBufferPercentage() {
         return mProvider.getBufferPercentage_impl();
     }
 
     /**
-     * @hide
+     * Returns the audio session ID.
      */
     public int getAudioSessionId() {
         return mProvider.getAudioSessionId_impl();
     }
 
     /**
-     * @hide
+     * Starts rendering closed caption or subtitles if there is any. The first subtitle track will
+     * be chosen by default if there multiple subtitle tracks exist.
      */
     public void showSubtitle() {
         mProvider.showSubtitle_impl();
     }
 
     /**
-     * @hide
+     * Stops showing closed captions or subtitles.
      */
     public void hideSubtitle() {
         mProvider.hideSubtitle_impl();
     }
 
+    // TODO: This should be revised after integration with MediaPlayer2.
     /**
      * Sets playback speed.
      *
@@ -181,9 +243,7 @@
      * or equal to zero, it will be just ignored and nothing will be changed. If it exceeds the
      * maximum speed that internal engine supports, system will determine best handling or it will
      * be reset to the normal speed 1.0f.
-     * TODO: This should be revised after integration with MediaPlayer2.
      * @param speed the playback speed. It should be positive.
-     * @hide
      */
     public void setSpeed(float speed) {
         mProvider.setSpeed_impl(speed);
@@ -194,7 +254,6 @@
      *
      * If setSpeed() has never been called, returns the default value 1.0f.
      * @return current speed setting
-     * @hide
      */
     public float getSpeed() {
         return mProvider.getSpeed_impl();
@@ -213,8 +272,6 @@
      *
      * @param focusGain the type of audio focus gain that will be requested, or
      *     {@link AudioManager#AUDIOFOCUS_NONE} to disable the use audio focus during playback.
-     *
-     * @hide
      */
     public void setAudioFocusRequest(int focusGain) {
         mProvider.setAudioFocusRequest_impl(focusGain);
@@ -224,8 +281,6 @@
      * Sets the {@link AudioAttributes} to be used during the playback of the video.
      *
      * @param attributes non-null <code>AudioAttributes</code>.
-     *
-     * @hide
      */
     public void setAudioAttributes(@NonNull AudioAttributes attributes) {
         mProvider.setAudioAttributes_impl(attributes);
@@ -235,35 +290,51 @@
      * Sets video path.
      *
      * @param path the path of the video.
-     * @hide
      */
     public void setVideoPath(String path) {
         mProvider.setVideoPath_impl(path);
     }
 
     /**
-     * @hide
+     * Sets video URI.
+     *
+     * @param uri the URI of the video.
      */
     public void setVideoURI(Uri uri) {
         mProvider.setVideoURI_impl(uri);
     }
 
     /**
-     * @hide
+     * Sets video URI using specific headers.
+     *
+     * @param uri     the URI of the video.
+     * @param headers the headers for the URI request.
+     *                Note that the cross domain redirection is allowed by default, but that can be
+     *                changed with key/value pairs through the headers parameter with
+     *                "android-allow-cross-domain-redirect" as the key and "0" or "1" as the value
+     *                to disallow or allow cross domain redirection.
      */
     public void setVideoURI(Uri uri, Map<String, String> headers) {
         mProvider.setVideoURI_impl(uri, headers);
     }
 
     /**
-     * @hide
+     * Selects which view will be used to render video between SurfacView and TextureView.
+     *
+     * @param viewType the view type to render video
+     * <ul>
+     * <li>{@link #VIEW_TYPE_SURFACEVIEW}
+     * <li>{@link #VIEW_TYPE_TEXTUREVIEW}
+     * </ul>
      */
     public void setViewType(@ViewType int viewType) {
         mProvider.setViewType_impl(viewType);
     }
 
     /**
-     * @hide
+     * Returns view type.
+     *
+     * @return view type. See {@see setViewType}.
      */
     @ViewType
     public int getViewType() {
@@ -271,42 +342,57 @@
     }
 
     /**
-     * @hide
+     * Stops playback and release all the resources. This should be called whenever a VideoView2
+     * instance is no longer to be used.
      */
     public void stopPlayback() {
         mProvider.stopPlayback_impl();
     }
 
     /**
-     * @hide
+     * Registers a callback to be invoked when the media file is loaded and ready to go.
+     *
+     * @param l the callback that will be run.
      */
     public void setOnPreparedListener(OnPreparedListener l) {
         mProvider.setOnPreparedListener_impl(l);
     }
 
     /**
-     * @hide
+     * Registers a callback to be invoked when the end of a media file has been reached during
+     * playback.
+     *
+     * @param l the callback that will be run.
      */
     public void setOnCompletionListener(OnCompletionListener l) {
         mProvider.setOnCompletionListener_impl(l);
     }
 
     /**
-     * @hide
+     * Registers a callback to be invoked when an error occurs during playback or setup.  If no
+     * listener is specified, or if the listener returned false, VideoView2 will inform the user of
+     * any errors.
+     *
+     * @param l The callback that will be run
      */
     public void setOnErrorListener(OnErrorListener l) {
         mProvider.setOnErrorListener_impl(l);
     }
 
     /**
-     * @hide
+     * Registers a callback to be invoked when an informational event occurs during playback or
+     * setup.
+     *
+     * @param l The callback that will be run
      */
     public void setOnInfoListener(OnInfoListener l) {
         mProvider.setOnInfoListener_impl(l);
     }
 
     /**
-     * @hide
+     * Registers a callback to be invoked when a view type change is done.
+     * {@see #setViewType(int)}
+     * @param l The callback that will be run
      */
     public void setOnViewTypeChangedListener(OnViewTypeChangedListener l) {
         mProvider.setOnViewTypeChangedListener_impl(l);
@@ -314,18 +400,22 @@
 
     /**
      * Interface definition of a callback to be invoked when the viw type has been changed.
-     * @hide
      */
     public interface OnViewTypeChangedListener {
         /**
          * Called when the view type has been changed.
-         * @see VideoView2#setViewType(int)
+         * @see #setViewType(int)
+         * @param viewType
+         * <ul>
+         * <li>{@link #VIEW_TYPE_SURFACEVIEW}
+         * <li>{@link #VIEW_TYPE_TEXTUREVIEW}
+         * </ul>
          */
         void onViewTypeChanged(@ViewType int viewType);
     }
 
     /**
-     * @hide
+     * Interface definition of a callback to be invoked when the media source is ready for playback.
      */
     public interface OnPreparedListener {
         /**
@@ -335,7 +425,8 @@
     }
 
     /**
-     * @hide
+     * Interface definition for a callback to be invoked when playback of a media source has
+     * completed.
      */
     public interface OnCompletionListener {
         /**
@@ -345,30 +436,47 @@
     }
 
     /**
-     * @hide
+     * Interface definition of a callback to be invoked when there has been an error during an
+     * asynchronous operation.
      */
     public interface OnErrorListener {
+        // TODO: Redefine error codes.
         /**
          * Called to indicate an error.
+         * @param what the type of error that has occurred
+         * @param extra an extra code, specific to the error.
+         * @return true if the method handled the error, false if it didn't.
+         * @see MediaPlayer#OnErrorListener
          */
         boolean onError(int what, int extra);
     }
 
     /**
-     * @hide
+     * Interface definition of a callback to be invoked to communicate some info and/or warning
+     * about the media or its playback.
      */
     public interface OnInfoListener {
         /**
          * Called to indicate an info or a warning.
-         * @see MediaPlayer#OnInfoListener
-         *
          * @param what the type of info or warning.
          * @param extra an extra code, specific to the info.
+         *
+         * @see MediaPlayer#OnInfoListener
          */
         void onInfo(int what, int extra);
     }
 
     @Override
+    protected void onAttachedToWindow() {
+        mProvider.onAttachedToWindow_impl();
+    }
+
+    @Override
+    protected void onDetachedFromWindow() {
+        mProvider.onDetachedFromWindow_impl();
+    }
+
+    @Override
     public CharSequence getAccessibilityClassName() {
         return mProvider.getAccessibilityClassName_impl();
     }
@@ -405,6 +513,16 @@
 
     private class SuperProvider implements ViewProvider {
         @Override
+        public void onAttachedToWindow_impl() {
+            VideoView2.super.onAttachedToWindow();
+        }
+
+        @Override
+        public void onDetachedFromWindow_impl() {
+            VideoView2.super.onDetachedFromWindow();
+        }
+
+        @Override
         public CharSequence getAccessibilityClassName_impl() {
             return VideoView2.super.getAccessibilityClassName();
         }
diff --git a/core/java/com/android/internal/app/IBatteryStats.aidl b/core/java/com/android/internal/app/IBatteryStats.aidl
index 388180d..d3e807d 100644
--- a/core/java/com/android/internal/app/IBatteryStats.aidl
+++ b/core/java/com/android/internal/app/IBatteryStats.aidl
@@ -23,6 +23,8 @@
 import android.os.ParcelFileDescriptor;
 import android.os.WorkSource;
 import android.os.connectivity.CellularBatteryStats;
+import android.os.connectivity.WifiBatteryStats;
+import android.os.connectivity.GpsBatteryStats;
 import android.os.health.HealthStatsParceler;
 import android.telephony.DataConnectionRealTimeInfo;
 import android.telephony.ModemActivityInfo;
@@ -91,6 +93,7 @@
     void noteVibratorOff(int uid);
     void noteStartGps(int uid);
     void noteStopGps(int uid);
+    void noteGpsSignalQuality(int signalLevel);
     void noteScreenState(int state);
     void noteScreenBrightness(int brightness);
     void noteUserActivity(int uid, int event);
@@ -140,6 +143,12 @@
     /** {@hide} */
     CellularBatteryStats getCellularBatteryStats();
 
+    /** {@hide} */
+    WifiBatteryStats getWifiBatteryStats();
+
+    /** {@hide} */
+    GpsBatteryStats getGpsBatteryStats();
+
     HealthStatsParceler takeUidSnapshot(int uid);
     HealthStatsParceler[] takeUidSnapshots(in int[] uid);
 
diff --git a/core/java/com/android/internal/net/INetworkWatchlistManager.aidl b/core/java/com/android/internal/net/INetworkWatchlistManager.aidl
index ee01a23..d69c7de 100644
--- a/core/java/com/android/internal/net/INetworkWatchlistManager.aidl
+++ b/core/java/com/android/internal/net/INetworkWatchlistManager.aidl
@@ -24,4 +24,5 @@
     boolean stopWatchlistLogging();
     void reloadWatchlist();
     void reportWatchlistIfNecessary();
+    byte[] getWatchlistConfigHash();
 }
diff --git a/core/java/com/android/internal/os/BatteryStatsHelper.java b/core/java/com/android/internal/os/BatteryStatsHelper.java
index 15dc6f5..5a59e70 100644
--- a/core/java/com/android/internal/os/BatteryStatsHelper.java
+++ b/core/java/com/android/internal/os/BatteryStatsHelper.java
@@ -665,14 +665,14 @@
 
     /**
      * Calculate the baseline power usage for the device when it is in suspend and idle.
-     * The device is drawing POWER_CPU_IDLE power at its lowest power state.
-     * The device is drawing POWER_CPU_IDLE + POWER_CPU_AWAKE power when a wakelock is held.
+     * The device is drawing POWER_CPU_SUSPEND power at its lowest power state.
+     * The device is drawing POWER_CPU_SUSPEND + POWER_CPU_IDLE power when a wakelock is held.
      */
     private void addIdleUsage() {
         final double suspendPowerMaMs = (mTypeBatteryRealtimeUs / 1000) *
-                mPowerProfile.getAveragePower(PowerProfile.POWER_CPU_IDLE);
+                mPowerProfile.getAveragePower(PowerProfile.POWER_CPU_SUSPEND);
         final double idlePowerMaMs = (mTypeBatteryUptimeUs / 1000) *
-                mPowerProfile.getAveragePower(PowerProfile.POWER_CPU_AWAKE);
+                mPowerProfile.getAveragePower(PowerProfile.POWER_CPU_IDLE);
         final double totalPowerMah = (suspendPowerMaMs + idlePowerMaMs) / (60 * 60 * 1000);
         if (DEBUG && totalPowerMah != 0) {
             Log.d(TAG, "Suspend: time=" + (mTypeBatteryRealtimeUs / 1000)
diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java
index ac5dbc4..b1c45f7 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -34,6 +34,8 @@
 import android.os.BatteryStats;
 import android.os.Build;
 import android.os.connectivity.CellularBatteryStats;
+import android.os.connectivity.WifiBatteryStats;
+import android.os.connectivity.GpsBatteryStats;
 import android.os.FileUtils;
 import android.os.Handler;
 import android.os.IBatteryPropertiesRegistrar;
@@ -78,6 +80,7 @@
 
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.location.gnssmetrics.GnssMetrics;
 import com.android.internal.net.NetworkStatsFactory;
 import com.android.internal.util.ArrayUtils;
 import com.android.internal.util.FastPrintWriter;
@@ -129,7 +132,7 @@
     private static final int MAGIC = 0xBA757475; // 'BATSTATS'
 
     // Current on-disk Parcel version
-    private static final int VERSION = 173 + (USE_OLD_HISTORY ? 1000 : 0);
+    private static final int VERSION = 174 + (USE_OLD_HISTORY ? 1000 : 0);
 
     // Maximum number of items we will record in the history.
     private static final int MAX_HISTORY_ITEMS;
@@ -153,11 +156,11 @@
             MAX_HISTORY_BUFFER = 96*1024;  // 96KB
             MAX_MAX_HISTORY_BUFFER = 128*1024; // 128KB
         } else {
-            MAX_HISTORY_ITEMS = 2000;
-            MAX_MAX_HISTORY_ITEMS = 3000;
-            MAX_WAKELOCKS_PER_UID = 100;
-            MAX_HISTORY_BUFFER = 256*1024;  // 256KB
-            MAX_MAX_HISTORY_BUFFER = 320*1024;  // 256KB
+            MAX_HISTORY_ITEMS = 4000;
+            MAX_MAX_HISTORY_ITEMS = 6000;
+            MAX_WAKELOCKS_PER_UID = 200;
+            MAX_HISTORY_BUFFER = 512*1024;  // 512KB
+            MAX_MAX_HISTORY_BUFFER = 640*1024;  // 640KB
         }
     }
 
@@ -198,6 +201,12 @@
     protected KernelUidCpuFreqTimeReader mKernelUidCpuFreqTimeReader =
             new KernelUidCpuFreqTimeReader();
     @VisibleForTesting
+    protected KernelUidCpuActiveTimeReader mKernelUidCpuActiveTimeReader =
+            new KernelUidCpuActiveTimeReader();
+    @VisibleForTesting
+    protected KernelUidCpuClusterTimeReader mKernelUidCpuClusterTimeReader =
+            new KernelUidCpuClusterTimeReader();
+    @VisibleForTesting
     protected KernelSingleUidTimeReader mKernelSingleUidTimeReader;
 
     private final KernelMemoryBandwidthStats mKernelMemoryBandwidthStats
@@ -666,6 +675,10 @@
     int mCameraOnNesting;
     StopwatchTimer mCameraOnTimer;
 
+    int mGpsSignalQualityBin = -1;
+    final StopwatchTimer[] mGpsSignalQualityTimer =
+        new StopwatchTimer[GnssMetrics.NUM_GPS_SIGNAL_QUALITY_LEVELS];
+
     int mPhoneSignalStrengthBin = -1;
     int mPhoneSignalStrengthBinRaw = -1;
     final StopwatchTimer[] mPhoneSignalStrengthsTimer =
@@ -739,6 +752,8 @@
     final StopwatchTimer[] mWifiSignalStrengthsTimer =
             new StopwatchTimer[NUM_WIFI_SIGNAL_STRENGTH_BINS];
 
+    StopwatchTimer mWifiActiveTimer;
+
     int mBluetoothScanNesting;
     @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
     protected StopwatchTimer mBluetoothScanTimer;
@@ -2773,12 +2788,14 @@
     public static class ControllerActivityCounterImpl extends ControllerActivityCounter
             implements Parcelable {
         private final LongSamplingCounter mIdleTimeMillis;
+        private final LongSamplingCounter mScanTimeMillis;
         private final LongSamplingCounter mRxTimeMillis;
         private final LongSamplingCounter[] mTxTimeMillis;
         private final LongSamplingCounter mPowerDrainMaMs;
 
         public ControllerActivityCounterImpl(TimeBase timeBase, int numTxStates) {
             mIdleTimeMillis = new LongSamplingCounter(timeBase);
+            mScanTimeMillis = new LongSamplingCounter(timeBase);
             mRxTimeMillis = new LongSamplingCounter(timeBase);
             mTxTimeMillis = new LongSamplingCounter[numTxStates];
             for (int i = 0; i < numTxStates; i++) {
@@ -2789,6 +2806,7 @@
 
         public ControllerActivityCounterImpl(TimeBase timeBase, int numTxStates, Parcel in) {
             mIdleTimeMillis = new LongSamplingCounter(timeBase, in);
+            mScanTimeMillis = new LongSamplingCounter(timeBase, in);
             mRxTimeMillis = new LongSamplingCounter(timeBase, in);
             final int recordedTxStates = in.readInt();
             if (recordedTxStates != numTxStates) {
@@ -2804,6 +2822,7 @@
 
         public void readSummaryFromParcel(Parcel in) {
             mIdleTimeMillis.readSummaryFromParcelLocked(in);
+            mScanTimeMillis.readSummaryFromParcelLocked(in);
             mRxTimeMillis.readSummaryFromParcelLocked(in);
             final int recordedTxStates = in.readInt();
             if (recordedTxStates != mTxTimeMillis.length) {
@@ -2822,6 +2841,7 @@
 
         public void writeSummaryToParcel(Parcel dest) {
             mIdleTimeMillis.writeSummaryFromParcelLocked(dest);
+            mScanTimeMillis.writeSummaryFromParcelLocked(dest);
             mRxTimeMillis.writeSummaryFromParcelLocked(dest);
             dest.writeInt(mTxTimeMillis.length);
             for (LongSamplingCounter counter : mTxTimeMillis) {
@@ -2833,6 +2853,7 @@
         @Override
         public void writeToParcel(Parcel dest, int flags) {
             mIdleTimeMillis.writeToParcel(dest);
+            mScanTimeMillis.writeToParcel(dest);
             mRxTimeMillis.writeToParcel(dest);
             dest.writeInt(mTxTimeMillis.length);
             for (LongSamplingCounter counter : mTxTimeMillis) {
@@ -2843,6 +2864,7 @@
 
         public void reset(boolean detachIfReset) {
             mIdleTimeMillis.reset(detachIfReset);
+            mScanTimeMillis.reset(detachIfReset);
             mRxTimeMillis.reset(detachIfReset);
             for (LongSamplingCounter counter : mTxTimeMillis) {
                 counter.reset(detachIfReset);
@@ -2852,6 +2874,7 @@
 
         public void detach() {
             mIdleTimeMillis.detach();
+            mScanTimeMillis.detach();
             mRxTimeMillis.detach();
             for (LongSamplingCounter counter : mTxTimeMillis) {
                 counter.detach();
@@ -2869,6 +2892,15 @@
         }
 
         /**
+         * @return a LongSamplingCounter, measuring time spent in the scan state in
+         * milliseconds.
+         */
+        @Override
+        public LongSamplingCounter getScanTimeCounter() {
+            return mScanTimeMillis;
+        }
+
+        /**
          * @return a LongSamplingCounter, measuring time spent in the receive state in
          * milliseconds.
          */
@@ -3880,6 +3912,10 @@
         }
         mKernelUidCpuTimeReader.removeUid(isolatedUid);
         mKernelUidCpuFreqTimeReader.removeUid(isolatedUid);
+        if (mConstants.TRACK_CPU_ACTIVE_CLUSTER_TIME) {
+            mKernelUidCpuActiveTimeReader.removeUid(isolatedUid);
+            mKernelUidCpuClusterTimeReader.removeUid(isolatedUid);
+        }
     }
 
     public int mapUid(int uid) {
@@ -4575,10 +4611,37 @@
             if (DEBUG_HISTORY) Slog.v(TAG, "Stop GPS to: "
                     + Integer.toHexString(mHistoryCur.states));
             addHistoryRecordLocked(elapsedRealtime, uptime);
+            stopAllGpsSignalQualityTimersLocked(-1);
+            mGpsSignalQualityBin = -1;
         }
         getUidStatsLocked(uid).noteStopGps(elapsedRealtime);
     }
 
+    public void noteGpsSignalQualityLocked(int signalLevel) {
+        if (mGpsNesting == 0) {
+            return;
+        }
+        if (signalLevel < 0 || signalLevel >= GnssMetrics.NUM_GPS_SIGNAL_QUALITY_LEVELS) {
+            stopAllGpsSignalQualityTimersLocked(-1);
+            return;
+        }
+        final long elapsedRealtime = mClocks.elapsedRealtime();
+        final long uptime = mClocks.uptimeMillis();
+        if (mGpsSignalQualityBin != signalLevel) {
+            if (mGpsSignalQualityBin >= 0) {
+                mGpsSignalQualityTimer[mGpsSignalQualityBin].stopRunningLocked(elapsedRealtime);
+            }
+            if(!mGpsSignalQualityTimer[signalLevel].isRunningLocked()) {
+                mGpsSignalQualityTimer[signalLevel].startRunningLocked(elapsedRealtime);
+            }
+            mHistoryCur.states2 = (mHistoryCur.states2&~HistoryItem.STATE2_GPS_SIGNAL_QUALITY_MASK)
+                    | (signalLevel << HistoryItem.STATE2_GPS_SIGNAL_QUALITY_SHIFT);
+            addHistoryRecordLocked(elapsedRealtime, uptime);
+            mGpsSignalQualityBin = signalLevel;
+        }
+        return;
+    }
+
     public void noteScreenStateLocked(int state) {
         state = mPretendScreenOff ? Display.STATE_OFF : state;
 
@@ -4912,6 +4975,18 @@
         mDailyPackageChanges.add(pc);
     }
 
+    void stopAllGpsSignalQualityTimersLocked(int except) {
+        final long elapsedRealtime = mClocks.elapsedRealtime();
+        for (int i = 0; i < GnssMetrics.NUM_GPS_SIGNAL_QUALITY_LEVELS; i++) {
+            if (i == except) {
+                continue;
+            }
+            while (mGpsSignalQualityTimer[i].isRunningLocked()) {
+                mGpsSignalQualityTimer[i].stopRunningLocked(elapsedRealtime);
+            }
+        }
+    }
+
     public void notePhoneOnLocked() {
         if (!mPhoneOn) {
             final long elapsedRealtime = mClocks.elapsedRealtime();
@@ -5565,8 +5640,11 @@
                     noteWifiRadioApWakeupLocked(elapsedRealtime, uptime, uid);
                 }
                 mHistoryCur.states |= HistoryItem.STATE_WIFI_RADIO_ACTIVE_FLAG;
+                mWifiActiveTimer.startRunningLocked(elapsedRealtime);
             } else {
                 mHistoryCur.states &= ~HistoryItem.STATE_WIFI_RADIO_ACTIVE_FLAG;
+                mWifiActiveTimer.stopRunningLocked(
+                    timestampNs / (1000 * 1000));
             }
             if (DEBUG_HISTORY) Slog.v(TAG, "Wifi network active " + active + " to: "
                     + Integer.toHexString(mHistoryCur.states));
@@ -6123,6 +6201,20 @@
         return val;
     }
 
+    @Override public long getGpsSignalQualityTime(int strengthBin,
+        long elapsedRealtimeUs, int which) {
+        if (strengthBin < 0 || strengthBin >= GnssMetrics.NUM_GPS_SIGNAL_QUALITY_LEVELS) {
+            return 0;
+        }
+        return mGpsSignalQualityTimer[strengthBin].getTotalTimeLocked(
+            elapsedRealtimeUs, which);
+    }
+
+    @Override public long getGpsBatteryDrainMaMs() {
+        //TODO: Add GPS power computation (b/67213967)
+        return 0;
+    }
+
     @Override public long getPhoneOnTime(long elapsedRealtimeUs, int which) {
         return mPhoneOnTimer.getTotalTimeLocked(elapsedRealtimeUs, which);
     }
@@ -6203,6 +6295,10 @@
         return mWifiOnTimer.getTotalTimeLocked(elapsedRealtimeUs, which);
     }
 
+    @Override public long getWifiActiveTime(long elapsedRealtimeUs, int which) {
+        return mWifiActiveTimer.getTotalTimeLocked(elapsedRealtimeUs, which);
+    }
+
     @Override public long getGlobalWifiRunningTime(long elapsedRealtimeUs, int which) {
         return mGlobalWifiRunningTimer.getTotalTimeLocked(elapsedRealtimeUs, which);
     }
@@ -6479,9 +6575,11 @@
         LongSamplingCounter mUserCpuTime;
         LongSamplingCounter mSystemCpuTime;
         LongSamplingCounter[][] mCpuClusterSpeedTimesUs;
+        LongSamplingCounter mCpuActiveTimeMs;
 
         LongSamplingCounterArray mCpuFreqTimeMs;
         LongSamplingCounterArray mScreenOffCpuFreqTimeMs;
+        LongSamplingCounterArray mCpuClusterTimesMs;
 
         LongSamplingCounterArray[] mProcStateTimeMs;
         LongSamplingCounterArray[] mProcStateScreenOffTimeMs;
@@ -6551,6 +6649,8 @@
 
             mUserCpuTime = new LongSamplingCounter(mBsi.mOnBatteryTimeBase);
             mSystemCpuTime = new LongSamplingCounter(mBsi.mOnBatteryTimeBase);
+            mCpuActiveTimeMs = new LongSamplingCounter(mBsi.mOnBatteryTimeBase);
+            mCpuClusterTimesMs = new LongSamplingCounterArray(mBsi.mOnBatteryTimeBase);
 
             mWakelockStats = mBsi.new OverflowArrayMap<Wakelock>(uid) {
                 @Override public Wakelock instantiateObject() {
@@ -6598,6 +6698,17 @@
         }
 
         @Override
+        public long getCpuActiveTime() {
+            return mCpuActiveTimeMs.getCountLocked(STATS_SINCE_CHARGED);
+        }
+
+        @Override
+        public long[] getCpuClusterTimes() {
+            return nullIfAllZeros(mCpuClusterTimesMs, STATS_SINCE_CHARGED);
+        }
+
+
+        @Override
         public long[] getCpuFreqTimes(int which, int procState) {
             if (which < 0 || which >= NUM_PROCESS_STATE) {
                 return null;
@@ -7660,6 +7771,9 @@
                 mScreenOffCpuFreqTimeMs.reset(false);
             }
 
+            mCpuActiveTimeMs.reset(false);
+            mCpuClusterTimesMs.reset(false);
+
             if (mProcStateTimeMs != null) {
                 for (LongSamplingCounterArray counters : mProcStateTimeMs) {
                     if (counters != null) {
@@ -7864,6 +7978,8 @@
                 if (mScreenOffCpuFreqTimeMs != null) {
                     mScreenOffCpuFreqTimeMs.detach();
                 }
+                mCpuActiveTimeMs.detach();
+                mCpuClusterTimesMs.detach();
 
                 if (mProcStateTimeMs != null) {
                     for (LongSamplingCounterArray counters : mProcStateTimeMs) {
@@ -8139,6 +8255,10 @@
 
             LongSamplingCounterArray.writeToParcel(out, mCpuFreqTimeMs);
             LongSamplingCounterArray.writeToParcel(out, mScreenOffCpuFreqTimeMs);
+
+            mCpuActiveTimeMs.writeToParcel(out);
+            mCpuClusterTimesMs.writeToParcel(out);
+
             if (mProcStateTimeMs != null) {
                 out.writeInt(mProcStateTimeMs.length);
                 for (LongSamplingCounterArray counters : mProcStateTimeMs) {
@@ -8456,6 +8576,9 @@
             mScreenOffCpuFreqTimeMs = LongSamplingCounterArray.readFromParcel(
                     in, mBsi.mOnBatteryScreenOffTimeBase);
 
+            mCpuActiveTimeMs = new LongSamplingCounter(mBsi.mOnBatteryTimeBase, in);
+            mCpuClusterTimesMs = new LongSamplingCounterArray(mBsi.mOnBatteryTimeBase, in);
+
             int length = in.readInt();
             if (length == NUM_PROCESS_STATE) {
                 mProcStateTimeMs = new LongSamplingCounterArray[length];
@@ -9822,6 +9945,11 @@
             mWifiSignalStrengthsTimer[i] = new StopwatchTimer(mClocks, null, -800-i, null,
                     mOnBatteryTimeBase);
         }
+        mWifiActiveTimer = new StopwatchTimer(mClocks, null, -900, null, mOnBatteryTimeBase);
+        for (int i=0; i< GnssMetrics.NUM_GPS_SIGNAL_QUALITY_LEVELS; i++) {
+            mGpsSignalQualityTimer[i] = new StopwatchTimer(mClocks, null, -1000-i, null,
+                mOnBatteryTimeBase);
+        }
         mAudioOnTimer = new StopwatchTimer(mClocks, null, -7, null, mOnBatteryTimeBase);
         mVideoOnTimer = new StopwatchTimer(mClocks, null, -8, null, mOnBatteryTimeBase);
         mFlashlightOnTimer = new StopwatchTimer(mClocks, null, -9, null, mOnBatteryTimeBase);
@@ -10511,7 +10639,11 @@
             mWifiSignalStrengthsTimer[i].reset(false);
         }
         mWifiMulticastWakelockTimer.reset(false);
+        mWifiActiveTimer.reset(false);
         mWifiActivity.reset(false);
+        for (int i=0; i< GnssMetrics.NUM_GPS_SIGNAL_QUALITY_LEVELS; i++) {
+            mGpsSignalQualityTimer[i].reset(false);
+        }
         mBluetoothActivity.reset(false);
         mModemActivity.reset(false);
         mNumConnectivityChange = mLoadedNumConnectivityChange = mUnpluggedNumConnectivityChange = 0;
@@ -10774,6 +10906,7 @@
                 // Measured in mAms
                 final long txTimeMs = info.getControllerTxTimeMillis();
                 final long rxTimeMs = info.getControllerRxTimeMillis();
+                final long scanTimeMs = info.getControllerScanTimeMillis();
                 final long idleTimeMs = info.getControllerIdleTimeMillis();
                 final long totalTimeMs = txTimeMs + rxTimeMs + idleTimeMs;
 
@@ -10786,6 +10919,7 @@
                     Slog.d(TAG, "  Rx Time:    " + rxTimeMs + " ms");
                     Slog.d(TAG, "  Idle Time:  " + idleTimeMs + " ms");
                     Slog.d(TAG, "  Total Time: " + totalTimeMs + " ms");
+                    Slog.d(TAG, "  Scan Time:  " + scanTimeMs + " ms");
                 }
 
                 long totalWifiLockTimeMs = 0;
@@ -10919,6 +11053,8 @@
                 mWifiActivity.getRxTimeCounter().addCountLocked(info.getControllerRxTimeMillis());
                 mWifiActivity.getTxTimeCounters()[0].addCountLocked(
                         info.getControllerTxTimeMillis());
+                mWifiActivity.getScanTimeCounter().addCountLocked(
+                    info.getControllerScanTimeMillis());
                 mWifiActivity.getIdleTimeCounter().addCountLocked(
                         info.getControllerIdleTimeMillis());
 
@@ -10962,6 +11098,39 @@
                 return;
             }
 
+            if (activityInfo != null) {
+                mHasModemReporting = true;
+                mModemActivity.getIdleTimeCounter().addCountLocked(
+                    activityInfo.getIdleTimeMillis());
+                mModemActivity.getRxTimeCounter().addCountLocked(activityInfo.getRxTimeMillis());
+                for (int lvl = 0; lvl < ModemActivityInfo.TX_POWER_LEVELS; lvl++) {
+                    mModemActivity.getTxTimeCounters()[lvl]
+                        .addCountLocked(activityInfo.getTxTimeMillis()[lvl]);
+                }
+
+                // POWER_MODEM_CONTROLLER_OPERATING_VOLTAGE is measured in mV, so convert to V.
+                final double opVolt = mPowerProfile.getAveragePower(
+                    PowerProfile.POWER_MODEM_CONTROLLER_OPERATING_VOLTAGE) / 1000.0;
+                if (opVolt != 0) {
+                    double energyUsed =
+                        activityInfo.getSleepTimeMillis() *
+                            mPowerProfile.getAveragePower(PowerProfile.POWER_MODEM_CONTROLLER_SLEEP)
+                            + activityInfo.getIdleTimeMillis() *
+                            mPowerProfile.getAveragePower(PowerProfile.POWER_MODEM_CONTROLLER_IDLE)
+                            + activityInfo.getRxTimeMillis() *
+                            mPowerProfile.getAveragePower(PowerProfile.POWER_MODEM_CONTROLLER_RX);
+                    int[] txCurrentMa = activityInfo.getTxTimeMillis();
+                    for (int i = 0; i < Math.min(txCurrentMa.length,
+                        SignalStrength.NUM_SIGNAL_STRENGTH_BINS); i++) {
+                        energyUsed += txCurrentMa[i] * mPowerProfile.getAveragePower(
+                            PowerProfile.POWER_MODEM_CONTROLLER_TX, i);
+                    }
+
+                    // We store the power drain as mAms.
+                    mModemActivity.getPowerCounter().addCountLocked((long) energyUsed);
+                }
+            }
+
             final long elapsedRealtimeMs = mClocks.elapsedRealtime();
             long radioTime = mMobileRadioActivePerAppTimer.getTimeSinceMarkLocked(
                     elapsedRealtimeMs * 1000);
@@ -11060,26 +11229,6 @@
                 mNetworkStatsPool.release(delta);
                 delta = null;
             }
-
-            if (activityInfo != null) {
-                mHasModemReporting = true;
-                mModemActivity.getIdleTimeCounter().addCountLocked(
-                        activityInfo.getIdleTimeMillis());
-                mModemActivity.getRxTimeCounter().addCountLocked(activityInfo.getRxTimeMillis());
-                for (int lvl = 0; lvl < ModemActivityInfo.TX_POWER_LEVELS; lvl++) {
-                    mModemActivity.getTxTimeCounters()[lvl]
-                            .addCountLocked(activityInfo.getTxTimeMillis()[lvl]);
-                }
-
-                // POWER_MODEM_CONTROLLER_OPERATING_VOLTAGE is measured in mV, so convert to V.
-                final double opVolt = mPowerProfile.getAveragePower(
-                        PowerProfile.POWER_MODEM_CONTROLLER_OPERATING_VOLTAGE) / 1000.0;
-                if (opVolt != 0) {
-                    // We store the power drain as mAms.
-                    mModemActivity.getPowerCounter().addCountLocked(
-                            (long) (activityInfo.getEnergyUsed() / opVolt));
-                }
-            }
         }
     }
 
@@ -11437,6 +11586,10 @@
         if (!mOnBatteryInternal) {
             mKernelUidCpuTimeReader.readDelta(null);
             mKernelUidCpuFreqTimeReader.readDelta(null);
+            if (mConstants.TRACK_CPU_ACTIVE_CLUSTER_TIME) {
+                mKernelUidCpuActiveTimeReader.readDelta(null);
+                mKernelUidCpuClusterTimeReader.readDelta(null);
+            }
             for (int cluster = mKernelCpuSpeedReaders.length - 1; cluster >= 0; --cluster) {
                 mKernelCpuSpeedReaders[cluster].readDelta();
             }
@@ -11453,6 +11606,10 @@
             updateClusterSpeedTimes(updatedUids);
         }
         readKernelUidCpuFreqTimesLocked(partialTimersToConsider);
+        if (mConstants.TRACK_CPU_ACTIVE_CLUSTER_TIME) {
+            readKernelUidCpuActiveTimesLocked();
+            readKernelUidCpuClusterTimesLocked();
+        }
     }
 
     /**
@@ -11764,6 +11921,64 @@
         }
     }
 
+    /**
+     * Take a snapshot of the cpu active times spent by each uid and update the corresponding
+     * counters.
+     */
+    @VisibleForTesting
+    public void readKernelUidCpuActiveTimesLocked() {
+        final long startTimeMs = mClocks.uptimeMillis();
+        mKernelUidCpuActiveTimeReader.readDelta((uid, cpuActiveTimesUs) -> {
+            uid = mapUid(uid);
+            if (Process.isIsolated(uid)) {
+                mKernelUidCpuActiveTimeReader.removeUid(uid);
+                Slog.w(TAG, "Got active times for an isolated uid with no mapping: " + uid);
+                return;
+            }
+            if (!mUserInfoProvider.exists(UserHandle.getUserId(uid))) {
+                Slog.w(TAG, "Got active times for an invalid user's uid " + uid);
+                mKernelUidCpuActiveTimeReader.removeUid(uid);
+                return;
+            }
+            final Uid u = getUidStatsLocked(uid);
+            u.mCpuActiveTimeMs.addCountLocked(cpuActiveTimesUs);
+        });
+
+        final long elapsedTimeMs = mClocks.uptimeMillis() - startTimeMs;
+        if (DEBUG_ENERGY_CPU || elapsedTimeMs >= 100) {
+            Slog.d(TAG, "Reading cpu active times took " + elapsedTimeMs + "ms");
+        }
+    }
+
+    /**
+     * Take a snapshot of the cpu cluster times spent by each uid and update the corresponding
+     * counters.
+     */
+    @VisibleForTesting
+    public void readKernelUidCpuClusterTimesLocked() {
+        final long startTimeMs = mClocks.uptimeMillis();
+        mKernelUidCpuClusterTimeReader.readDelta((uid, cpuClusterTimesUs) -> {
+            uid = mapUid(uid);
+            if (Process.isIsolated(uid)) {
+                mKernelUidCpuClusterTimeReader.removeUid(uid);
+                Slog.w(TAG, "Got cluster times for an isolated uid with no mapping: " + uid);
+                return;
+            }
+            if (!mUserInfoProvider.exists(UserHandle.getUserId(uid))) {
+                Slog.w(TAG, "Got cluster times for an invalid user's uid " + uid);
+                mKernelUidCpuClusterTimeReader.removeUid(uid);
+                return;
+            }
+            final Uid u = getUidStatsLocked(uid);
+            u.mCpuClusterTimesMs.addCountLocked(cpuClusterTimesUs);
+        });
+
+        final long elapsedTimeMs = mClocks.uptimeMillis() - startTimeMs;
+        if (DEBUG_ENERGY_CPU || elapsedTimeMs >= 100) {
+            Slog.d(TAG, "Reading cpu cluster times took " + elapsedTimeMs + "ms");
+        }
+    }
+
     boolean setChargingLocked(boolean charging) {
         if (mCharging != charging) {
             mCharging = charging;
@@ -12367,6 +12582,71 @@
         return s;
     }
 
+     /*@hide */
+     public WifiBatteryStats getWifiBatteryStats() {
+         WifiBatteryStats s = new WifiBatteryStats();
+         final int which = STATS_SINCE_CHARGED;
+         final long rawRealTime = SystemClock.elapsedRealtime() * 1000;
+         final ControllerActivityCounter counter = getWifiControllerActivity();
+         final long idleTimeMs = counter.getIdleTimeCounter().getCountLocked(which);
+         final long scanTimeMs = counter.getScanTimeCounter().getCountLocked(which);
+         final long rxTimeMs = counter.getRxTimeCounter().getCountLocked(which);
+         final long txTimeMs = counter.getTxTimeCounters()[0].getCountLocked(which);
+         final long totalControllerActivityTimeMs
+             = computeBatteryRealtime(SystemClock.elapsedRealtime() * 1000, which) / 1000;
+         final long sleepTimeMs
+             = totalControllerActivityTimeMs - (idleTimeMs + rxTimeMs + txTimeMs);
+         final long energyConsumedMaMs = counter.getPowerCounter().getCountLocked(which);
+         long numAppScanRequest = 0;
+         for (int i = 0; i < mUidStats.size(); i++) {
+             numAppScanRequest += mUidStats.valueAt(i).mWifiScanTimer.getCountLocked(which);
+         }
+         long[] timeInStateMs = new long[NUM_WIFI_STATES];
+         for (int i=0; i<NUM_WIFI_STATES; i++) {
+            timeInStateMs[i] = getWifiStateTime(i, rawRealTime, which) / 1000;
+         }
+         long[] timeInSupplStateMs = new long[NUM_WIFI_SUPPL_STATES];
+         for (int i=0; i<NUM_WIFI_SUPPL_STATES; i++) {
+             timeInSupplStateMs[i] = getWifiSupplStateTime(i, rawRealTime, which) / 1000;
+         }
+         long[] timeSignalStrengthTimeMs = new long[NUM_WIFI_SIGNAL_STRENGTH_BINS];
+         for (int i=0; i<NUM_WIFI_SIGNAL_STRENGTH_BINS; i++) {
+             timeSignalStrengthTimeMs[i] = getWifiSignalStrengthTime(i, rawRealTime, which) / 1000;
+         }
+         s.setLoggingDurationMs(computeBatteryRealtime(rawRealTime, which) / 1000);
+         s.setKernelActiveTimeMs(getWifiActiveTime(rawRealTime, which) / 1000);
+         s.setNumPacketsTx(getNetworkActivityPackets(NETWORK_WIFI_TX_DATA, which));
+         s.setNumBytesTx(getNetworkActivityBytes(NETWORK_WIFI_TX_DATA, which));
+         s.setNumPacketsRx(getNetworkActivityPackets(NETWORK_WIFI_RX_DATA, which));
+         s.setNumBytesRx(getNetworkActivityBytes(NETWORK_WIFI_RX_DATA, which));
+         s.setSleepTimeMs(sleepTimeMs);
+         s.setIdleTimeMs(idleTimeMs);
+         s.setRxTimeMs(rxTimeMs);
+         s.setTxTimeMs(txTimeMs);
+         s.setScanTimeMs(scanTimeMs);
+         s.setEnergyConsumedMaMs(energyConsumedMaMs);
+         s.setNumAppScanRequest(numAppScanRequest);
+         s.setTimeInStateMs(timeInStateMs);
+         s.setTimeInSupplicantStateMs(timeInSupplStateMs);
+         s.setTimeInRxSignalStrengthLevelMs(timeSignalStrengthTimeMs);
+         return s;
+     }
+
+    /*@hide */
+    public GpsBatteryStats getGpsBatteryStats() {
+        GpsBatteryStats s = new GpsBatteryStats();
+        final int which = STATS_SINCE_CHARGED;
+        final long rawRealTime = SystemClock.elapsedRealtime() * 1000;
+        s.setLoggingDurationMs(computeBatteryRealtime(rawRealTime, which) / 1000);
+        s.setEnergyConsumedMaMs(getGpsBatteryDrainMaMs());
+        long[] time = new long[GnssMetrics.NUM_GPS_SIGNAL_QUALITY_LEVELS];
+        for (int i=0; i<time.length; i++) {
+            time[i] = getGpsSignalQualityTime(i, rawRealTime, which) / 1000;
+        }
+        s.setTimeInGpsSignalQualityLevel(time);
+        return s;
+    }
+
     @Override
     public LevelStepTracker getChargeLevelStepTracker() {
         return mChargeStepTracker;
@@ -12626,10 +12906,19 @@
     public final class Constants extends ContentObserver {
         public static final String KEY_TRACK_CPU_TIMES_BY_PROC_STATE
                 = "track_cpu_times_by_proc_state";
+        public static final String KEY_TRACK_CPU_ACTIVE_CLUSTER_TIME
+                = "track_cpu_active_cluster_time";
+        public static final String KEY_READ_BINARY_CPU_TIME
+                = "read_binary_cpu_time";
 
         private static final boolean DEFAULT_TRACK_CPU_TIMES_BY_PROC_STATE = true;
+        private static final boolean DEFAULT_TRACK_CPU_ACTIVE_CLUSTER_TIME = true;
+        private static final boolean DEFAULT_READ_BINARY_CPU_TIME = false;
 
         public boolean TRACK_CPU_TIMES_BY_PROC_STATE = DEFAULT_TRACK_CPU_TIMES_BY_PROC_STATE;
+        public boolean TRACK_CPU_ACTIVE_CLUSTER_TIME = DEFAULT_TRACK_CPU_ACTIVE_CLUSTER_TIME;
+        // Not used right now.
+        public boolean READ_BINARY_CPU_TIME = DEFAULT_READ_BINARY_CPU_TIME;
 
         private ContentResolver mResolver;
         private final KeyValueListParser mParser = new KeyValueListParser(',');
@@ -12665,6 +12954,11 @@
                 updateTrackCpuTimesByProcStateLocked(TRACK_CPU_TIMES_BY_PROC_STATE,
                         mParser.getBoolean(KEY_TRACK_CPU_TIMES_BY_PROC_STATE,
                                 DEFAULT_TRACK_CPU_TIMES_BY_PROC_STATE));
+                TRACK_CPU_ACTIVE_CLUSTER_TIME = mParser.getBoolean(
+                        KEY_TRACK_CPU_ACTIVE_CLUSTER_TIME, DEFAULT_TRACK_CPU_ACTIVE_CLUSTER_TIME);
+                READ_BINARY_CPU_TIME = mParser.getBoolean(
+                        KEY_READ_BINARY_CPU_TIME, DEFAULT_READ_BINARY_CPU_TIME);
+
             }
         }
 
@@ -12679,6 +12973,10 @@
         public void dumpLocked(PrintWriter pw) {
             pw.print(KEY_TRACK_CPU_TIMES_BY_PROC_STATE); pw.print("=");
             pw.println(TRACK_CPU_TIMES_BY_PROC_STATE);
+            pw.print(KEY_TRACK_CPU_ACTIVE_CLUSTER_TIME); pw.print("=");
+            pw.println(TRACK_CPU_ACTIVE_CLUSTER_TIME);
+            pw.print(KEY_READ_BINARY_CPU_TIME); pw.print("=");
+            pw.println(READ_BINARY_CPU_TIME);
         }
     }
 
@@ -13044,7 +13342,11 @@
         for (int i=0; i<NUM_WIFI_SIGNAL_STRENGTH_BINS; i++) {
             mWifiSignalStrengthsTimer[i].readSummaryFromParcelLocked(in);
         }
+        mWifiActiveTimer.readSummaryFromParcelLocked(in);
         mWifiActivity.readSummaryFromParcel(in);
+        for (int i=0; i<GnssMetrics.NUM_GPS_SIGNAL_QUALITY_LEVELS; i++) {
+            mGpsSignalQualityTimer[i].readSummaryFromParcelLocked(in);
+        }
         mBluetoothActivity.readSummaryFromParcel(in);
         mModemActivity.readSummaryFromParcel(in);
         mHasWifiReporting = in.readInt() != 0;
@@ -13249,6 +13551,10 @@
                     in, mOnBatteryTimeBase);
             u.mScreenOffCpuFreqTimeMs = LongSamplingCounterArray.readSummaryFromParcelLocked(
                     in, mOnBatteryScreenOffTimeBase);
+
+            u.mCpuActiveTimeMs.readSummaryFromParcelLocked(in);
+            u.mCpuClusterTimesMs.readSummaryFromParcelLocked(in);
+
             int length = in.readInt();
             if (length == Uid.NUM_PROCESS_STATE) {
                 u.mProcStateTimeMs = new LongSamplingCounterArray[length];
@@ -13482,7 +13788,11 @@
         for (int i=0; i<NUM_WIFI_SIGNAL_STRENGTH_BINS; i++) {
             mWifiSignalStrengthsTimer[i].writeSummaryFromParcelLocked(out, NOWREAL_SYS);
         }
+        mWifiActiveTimer.writeSummaryFromParcelLocked(out, NOWREAL_SYS);
         mWifiActivity.writeSummaryToParcel(out);
+        for (int i=0; i< GnssMetrics.NUM_GPS_SIGNAL_QUALITY_LEVELS; i++) {
+            mGpsSignalQualityTimer[i].writeSummaryFromParcelLocked(out, NOWREAL_SYS);
+        }
         mBluetoothActivity.writeSummaryToParcel(out);
         mModemActivity.writeSummaryToParcel(out);
         out.writeInt(mHasWifiReporting ? 1 : 0);
@@ -13725,6 +14035,9 @@
             LongSamplingCounterArray.writeSummaryToParcelLocked(out, u.mCpuFreqTimeMs);
             LongSamplingCounterArray.writeSummaryToParcelLocked(out, u.mScreenOffCpuFreqTimeMs);
 
+            u.mCpuActiveTimeMs.writeSummaryFromParcelLocked(out);
+            u.mCpuClusterTimesMs.writeSummaryToParcelLocked(out);
+
             if (u.mProcStateTimeMs != null) {
                 out.writeInt(u.mProcStateTimeMs.length);
                 for (LongSamplingCounterArray counters : u.mProcStateTimeMs) {
@@ -13954,9 +14267,14 @@
             mWifiSignalStrengthsTimer[i] = new StopwatchTimer(mClocks, null, -800-i,
                     null, mOnBatteryTimeBase, in);
         }
-
+        mWifiActiveTimer = new StopwatchTimer(mClocks, null, -900, null,
+            mOnBatteryTimeBase, in);
         mWifiActivity = new ControllerActivityCounterImpl(mOnBatteryTimeBase,
                 NUM_WIFI_TX_LEVELS, in);
+        for (int i=0; i<GnssMetrics.NUM_GPS_SIGNAL_QUALITY_LEVELS; i++) {
+            mGpsSignalQualityTimer[i] = new StopwatchTimer(mClocks, null, -1000-i,
+                null, mOnBatteryTimeBase, in);
+        }
         mBluetoothActivity = new ControllerActivityCounterImpl(mOnBatteryTimeBase,
                 NUM_BT_TX_LEVELS, in);
         mModemActivity = new ControllerActivityCounterImpl(mOnBatteryTimeBase,
@@ -14154,7 +14472,11 @@
         for (int i=0; i<NUM_WIFI_SIGNAL_STRENGTH_BINS; i++) {
             mWifiSignalStrengthsTimer[i].writeToParcel(out, uSecRealtime);
         }
+        mWifiActiveTimer.writeToParcel(out, uSecRealtime);
         mWifiActivity.writeToParcel(out, 0);
+        for (int i=0; i< GnssMetrics.NUM_GPS_SIGNAL_QUALITY_LEVELS; i++) {
+            mGpsSignalQualityTimer[i].writeToParcel(out, uSecRealtime);
+        }
         mBluetoothActivity.writeToParcel(out, 0);
         mModemActivity.writeToParcel(out, 0);
         out.writeInt(mHasWifiReporting ? 1 : 0);
@@ -14348,6 +14670,10 @@
                 pr.println("*** Wifi signal strength #" + i + ":");
                 mWifiSignalStrengthsTimer[i].logState(pr, "  ");
             }
+            for (int i=0; i<GnssMetrics.NUM_GPS_SIGNAL_QUALITY_LEVELS; i++) {
+                pr.println("*** GPS signal quality #" + i + ":");
+                mGpsSignalQualityTimer[i].logState(pr, "  ");
+            }
             pr.println("*** Flashlight timer:");
             mFlashlightOnTimer.logState(pr, "  ");
             pr.println("*** Camera timer:");
diff --git a/core/java/com/android/internal/os/CpuPowerCalculator.java b/core/java/com/android/internal/os/CpuPowerCalculator.java
index bb743c1..a34e7f5 100644
--- a/core/java/com/android/internal/os/CpuPowerCalculator.java
+++ b/core/java/com/android/internal/os/CpuPowerCalculator.java
@@ -31,8 +31,7 @@
 
     @Override
     public void calculateApp(BatterySipper app, BatteryStats.Uid u, long rawRealtimeUs,
-                             long rawUptimeUs, int statsType) {
-
+            long rawUptimeUs, int statsType) {
         app.cpuTimeMs = (u.getUserCpuTimeUs(statsType) + u.getSystemCpuTimeUs(statsType)) / 1000;
         final int numClusters = mProfile.getNumCpuClusters();
 
@@ -42,7 +41,7 @@
             for (int speed = 0; speed < speedsForCluster; speed++) {
                 final long timeUs = u.getTimeAtCpuSpeed(cluster, speed, statsType);
                 final double cpuSpeedStepPower = timeUs *
-                        mProfile.getAveragePowerForCpu(cluster, speed);
+                        mProfile.getAveragePowerForCpuCore(cluster, speed);
                 if (DEBUG) {
                     Log.d(TAG, "UID " + u.getUid() + ": CPU cluster #" + cluster + " step #"
                             + speed + " timeUs=" + timeUs + " power="
@@ -51,6 +50,25 @@
                 cpuPowerMaUs += cpuSpeedStepPower;
             }
         }
+        cpuPowerMaUs += u.getCpuActiveTime() * mProfile.getAveragePower(
+                PowerProfile.POWER_CPU_ACTIVE);
+        long[] cpuClusterTimes = u.getCpuClusterTimes();
+        if (cpuClusterTimes != null) {
+            if (cpuClusterTimes.length == numClusters) {
+                for (int i = 0; i < numClusters; i++) {
+                    double power = cpuClusterTimes[i] * mProfile.getAveragePowerForCpuCluster(i);
+                    cpuPowerMaUs += power;
+                    if (DEBUG) {
+                        Log.d(TAG, "UID " + u.getUid() + ": CPU cluster #" + i + " clusterTimeUs="
+                                + cpuClusterTimes[i] + " power="
+                                + BatteryStatsHelper.makemAh(power / MICROSEC_IN_HR));
+                    }
+                }
+            } else {
+                Log.w(TAG, "UID " + u.getUid() + " CPU cluster # mismatch: Power Profile # "
+                        + numClusters + " actual # " + cpuClusterTimes.length);
+            }
+        }
         app.cpuPowerMah = cpuPowerMaUs / MICROSEC_IN_HR;
 
         if (DEBUG && (app.cpuTimeMs != 0 || app.cpuPowerMah != 0)) {
diff --git a/core/java/com/android/internal/os/KernelUidCpuActiveTimeReader.java b/core/java/com/android/internal/os/KernelUidCpuActiveTimeReader.java
new file mode 100644
index 0000000..cb96c5c
--- /dev/null
+++ b/core/java/com/android/internal/os/KernelUidCpuActiveTimeReader.java
@@ -0,0 +1,146 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.os;
+
+import android.annotation.Nullable;
+import android.os.StrictMode;
+import android.os.SystemClock;
+import android.util.Slog;
+import android.util.SparseArray;
+import android.util.TimeUtils;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.io.BufferedReader;
+import java.io.FileReader;
+import java.io.IOException;
+
+/**
+ * Reads /proc/uid_concurrent_active_time which has the format:
+ * active: X (X is # cores)
+ * [uid0]: [time-0] [time-1] [time-2] ... (# entries = # cores)
+ * [uid1]: [time-0] [time-1] [time-2] ... ...
+ * ...
+ * Time-N means the CPU time a UID spent running concurrently with N other processes.
+ * The file contains a monotonically increasing count of time for a single boot. This class
+ * maintains the previous results of a call to {@link #readDelta} in order to provide a
+ * proper delta.
+ */
+public class KernelUidCpuActiveTimeReader {
+    private static final boolean DEBUG = false;
+    private static final String TAG = "KernelUidCpuActiveTimeReader";
+    private static final String UID_TIMES_PROC_FILE = "/proc/uid_concurrent_active_time";
+
+    private int mCoreCount;
+    private long mLastTimeReadMs;
+    private long mNowTimeMs;
+    private SparseArray<long[]> mLastUidCpuActiveTimeMs = new SparseArray<>();
+
+    public interface Callback {
+        void onUidCpuActiveTime(int uid, long cpuActiveTimeMs);
+    }
+
+    public void readDelta(@Nullable Callback cb) {
+        final int oldMask = StrictMode.allowThreadDiskReadsMask();
+        try (BufferedReader reader = new BufferedReader(new FileReader(UID_TIMES_PROC_FILE))) {
+            mNowTimeMs = SystemClock.elapsedRealtime();
+            readDeltaInternal(reader, cb);
+            mLastTimeReadMs = mNowTimeMs;
+        } catch (IOException e) {
+            Slog.e(TAG, "Failed to read " + UID_TIMES_PROC_FILE + ": " + e);
+        } finally {
+            StrictMode.setThreadPolicyMask(oldMask);
+        }
+    }
+
+    public void removeUid(int uid) {
+        mLastUidCpuActiveTimeMs.delete(uid);
+    }
+
+    public void removeUidsInRange(int startUid, int endUid) {
+        if (endUid < startUid) {
+            Slog.w(TAG, "End UID " + endUid + " is smaller than start UID " + startUid);
+            return;
+        }
+        mLastUidCpuActiveTimeMs.put(startUid, null);
+        mLastUidCpuActiveTimeMs.put(endUid, null);
+        final int firstIndex = mLastUidCpuActiveTimeMs.indexOfKey(startUid);
+        final int lastIndex = mLastUidCpuActiveTimeMs.indexOfKey(endUid);
+        mLastUidCpuActiveTimeMs.removeAtRange(firstIndex, lastIndex - firstIndex + 1);
+    }
+
+    @VisibleForTesting
+    public void readDeltaInternal(BufferedReader reader, @Nullable Callback cb) throws IOException {
+        String line = reader.readLine();
+        if (line == null || !line.startsWith("active:")) {
+            Slog.e(TAG, String.format("Malformed proc file: %s ", UID_TIMES_PROC_FILE));
+            return;
+        }
+        if (mCoreCount == 0) {
+            mCoreCount = Integer.parseInt(line.substring(line.indexOf(' ')+1));
+        }
+        while ((line = reader.readLine()) != null) {
+            final int index = line.indexOf(' ');
+            final int uid = Integer.parseInt(line.substring(0, index - 1), 10);
+            readTimesForUid(uid, line.substring(index + 1), cb);
+        }
+    }
+
+    private void readTimesForUid(int uid, String line, @Nullable Callback cb) {
+        long[] lastActiveTime = mLastUidCpuActiveTimeMs.get(uid);
+        if (lastActiveTime == null) {
+            lastActiveTime = new long[mCoreCount];
+            mLastUidCpuActiveTimeMs.put(uid, lastActiveTime);
+        }
+        final String[] timesStr = line.split(" ");
+        if (timesStr.length != mCoreCount) {
+            Slog.e(TAG, String.format("# readings don't match # cores, readings: %d, CPU cores: %d",
+                    timesStr.length, mCoreCount));
+            return;
+        }
+        long sumDeltas = 0;
+        final long[] curActiveTime = new long[mCoreCount];
+        boolean notify = false;
+        for (int i = 0; i < mCoreCount; i++) {
+            // Times read will be in units of 10ms
+            curActiveTime[i] = Long.parseLong(timesStr[i], 10) * 10;
+            long delta = curActiveTime[i] - lastActiveTime[i];
+            if (delta < 0 || curActiveTime[i] < 0) {
+                if (DEBUG) {
+                    final StringBuilder sb = new StringBuilder();
+                    sb.append(String.format("Malformed cpu active time for UID=%d\n", uid));
+                    sb.append(String.format("data=(%d,%d)\n", lastActiveTime[i], curActiveTime[i]));
+                    sb.append("times=(");
+                    TimeUtils.formatDuration(mLastTimeReadMs, sb);
+                    sb.append(",");
+                    TimeUtils.formatDuration(mNowTimeMs, sb);
+                    sb.append(")");
+                    Slog.e(TAG, sb.toString());
+                }
+                return;
+            }
+            notify |= delta > 0;
+            sumDeltas += delta / (i + 1);
+        }
+        if (notify) {
+            System.arraycopy(curActiveTime, 0, lastActiveTime, 0, mCoreCount);
+            if (cb != null) {
+                cb.onUidCpuActiveTime(uid, sumDeltas);
+            }
+        }
+    }
+}
diff --git a/core/java/com/android/internal/os/KernelUidCpuClusterTimeReader.java b/core/java/com/android/internal/os/KernelUidCpuClusterTimeReader.java
new file mode 100644
index 0000000..85153bc
--- /dev/null
+++ b/core/java/com/android/internal/os/KernelUidCpuClusterTimeReader.java
@@ -0,0 +1,178 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.os;
+
+import android.annotation.Nullable;
+import android.os.StrictMode;
+import android.os.SystemClock;
+import android.util.Slog;
+import android.util.SparseArray;
+import android.util.TimeUtils;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.io.BufferedReader;
+import java.io.FileReader;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Reads /proc/uid_concurrent_policy_time which has the format:
+ * policy0: X policy4: Y (there are X cores on policy0, Y cores on policy4)
+ * [uid0]: [time-0-0] [time-0-1] ... [time-1-0] [time-1-1] ...
+ * [uid1]: [time-0-0] [time-0-1] ... [time-1-0] [time-1-1] ...
+ * ...
+ * Time-X-Y means the time a UID spent on clusterX running concurrently with Y other processes.
+ * The file contains a monotonically increasing count of time for a single boot. This class
+ * maintains the previous results of a call to {@link #readDelta} in order to provide a proper
+ * delta.
+ */
+public class KernelUidCpuClusterTimeReader {
+
+    private static final boolean DEBUG = false;
+    private static final String TAG = "KernelUidCpuClusterTimeReader";
+    private static final String UID_TIMES_PROC_FILE = "/proc/uid_concurrent_policy_time";
+
+    // mCoreOnCluster[i] is the # of cores on cluster i
+    private int[] mCoreOnCluster;
+    private int mCores;
+    private long mLastTimeReadMs;
+    private long mNowTimeMs;
+    private SparseArray<long[]> mLastUidPolicyTimeMs = new SparseArray<>();
+
+    public interface Callback {
+        /**
+         * @param uid
+         * @param cpuActiveTimeMs the first dimension is cluster, the second dimension is the # of
+         *                        processes running concurrently with this uid.
+         */
+        void onUidCpuPolicyTime(int uid, long[] cpuActiveTimeMs);
+    }
+
+    public void readDelta(@Nullable Callback cb) {
+        final int oldMask = StrictMode.allowThreadDiskReadsMask();
+        try (BufferedReader reader = new BufferedReader(new FileReader(UID_TIMES_PROC_FILE))) {
+            mNowTimeMs = SystemClock.elapsedRealtime();
+            readDeltaInternal(reader, cb);
+            mLastTimeReadMs = mNowTimeMs;
+        } catch (IOException e) {
+            Slog.e(TAG, "Failed to read " + UID_TIMES_PROC_FILE + ": " + e);
+        } finally {
+            StrictMode.setThreadPolicyMask(oldMask);
+        }
+    }
+
+    public void removeUid(int uid) {
+        mLastUidPolicyTimeMs.delete(uid);
+    }
+
+    public void removeUidsInRange(int startUid, int endUid) {
+        if (endUid < startUid) {
+            Slog.w(TAG, "End UID " + endUid + " is smaller than start UID " + startUid);
+            return;
+        }
+        mLastUidPolicyTimeMs.put(startUid, null);
+        mLastUidPolicyTimeMs.put(endUid, null);
+        final int firstIndex = mLastUidPolicyTimeMs.indexOfKey(startUid);
+        final int lastIndex = mLastUidPolicyTimeMs.indexOfKey(endUid);
+        mLastUidPolicyTimeMs.removeAtRange(firstIndex, lastIndex - firstIndex + 1);
+    }
+
+    @VisibleForTesting
+    public void readDeltaInternal(BufferedReader reader, @Nullable Callback cb) throws IOException {
+        String line = reader.readLine();
+        if (line == null || !line.startsWith("policy")) {
+            Slog.e(TAG, String.format("Malformed proc file: %s ", UID_TIMES_PROC_FILE));
+            return;
+        }
+        if (mCoreOnCluster == null) {
+            List<Integer> list = new ArrayList<>();
+            String[] policies = line.split(" ");
+
+            if (policies.length == 0 || policies.length % 2 != 0) {
+                Slog.e(TAG, String.format("Malformed proc file: %s ", UID_TIMES_PROC_FILE));
+                return;
+            }
+
+            for (int i = 0; i < policies.length; i+=2) {
+                list.add(Integer.parseInt(policies[i+1]));
+            }
+
+            mCoreOnCluster = new int[list.size()];
+            for(int i=0;i<list.size();i++){
+                mCoreOnCluster[i] = list.get(i);
+                mCores += mCoreOnCluster[i];
+            }
+        }
+        while ((line = reader.readLine()) != null) {
+            final int index = line.indexOf(' ');
+            final int uid = Integer.parseInt(line.substring(0, index - 1), 10);
+            readTimesForUid(uid, line.substring(index + 1), cb);
+        }
+    }
+
+    private void readTimesForUid(int uid, String line, @Nullable Callback cb) {
+        long[] lastPolicyTime = mLastUidPolicyTimeMs.get(uid);
+        if (lastPolicyTime == null) {
+            lastPolicyTime = new long[mCores];
+            mLastUidPolicyTimeMs.put(uid, lastPolicyTime);
+        }
+        final String[] timeStr = line.split(" ");
+        if (timeStr.length != mCores) {
+            Slog.e(TAG, String.format("# readings don't match # cores, readings: %d, # CPU cores: %d",
+                    timeStr.length, mCores));
+            return;
+        }
+        final long[] deltaPolicyTime = new long[mCores];
+        final long[] currPolicyTime = new long[mCores];
+        boolean notify = false;
+        for (int i = 0; i < mCores; i++) {
+            // Times read will be in units of 10ms
+            currPolicyTime[i] = Long.parseLong(timeStr[i], 10) * 10;
+            deltaPolicyTime[i] = currPolicyTime[i] - lastPolicyTime[i];
+            if (deltaPolicyTime[i] < 0 || currPolicyTime[i] < 0) {
+                if (DEBUG) {
+                    final StringBuilder sb = new StringBuilder();
+                    sb.append(String.format("Malformed cpu policy time for UID=%d\n", uid));
+                    sb.append(String.format("data=(%d,%d)\n", lastPolicyTime[i], currPolicyTime[i]));
+                    sb.append("times=(");
+                    TimeUtils.formatDuration(mLastTimeReadMs, sb);
+                    sb.append(",");
+                    TimeUtils.formatDuration(mNowTimeMs, sb);
+                    sb.append(")");
+                    Slog.e(TAG, sb.toString());
+                }
+                return;
+            }
+            notify |= deltaPolicyTime[i] > 0;
+        }
+        if (notify) {
+            System.arraycopy(currPolicyTime, 0, lastPolicyTime, 0, mCores);
+            if (cb != null) {
+                final long[] times = new long[mCoreOnCluster.length];
+                int core = 0;
+                for (int i = 0; i < mCoreOnCluster.length; i++) {
+                    for (int j = 0; j < mCoreOnCluster[i]; j++) {
+                        times[i] += deltaPolicyTime[core++] / (j+1);
+                    }
+                }
+                cb.onUidCpuPolicyTime(uid, times);
+            }
+        }
+    }
+}
diff --git a/core/java/com/android/internal/os/PowerProfile.java b/core/java/com/android/internal/os/PowerProfile.java
index 872b465..240fc51 100644
--- a/core/java/com/android/internal/os/PowerProfile.java
+++ b/core/java/com/android/internal/os/PowerProfile.java
@@ -21,6 +21,7 @@
 import android.content.res.Resources;
 import android.content.res.XmlResourceParser;
 
+import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.XmlUtils;
 
 import org.xmlpull.v1.XmlPullParser;
@@ -43,23 +44,25 @@
     public static final String POWER_NONE = "none";
 
     /**
-     * Power consumption when CPU is in power collapse mode.
+     * POWER_CPU_SUSPEND: Power consumption when CPU is in power collapse mode.
+     * POWER_CPU_IDLE: Power consumption when CPU is awake (when a wake lock is held). This should
+     *                 be zero on devices that can go into full CPU power collapse even when a wake
+     *                 lock is held. Otherwise, this is the power consumption in addition to
+     *                 POWER_CPU_SUSPEND due to a wake lock being held but with no CPU activity.
+     * POWER_CPU_ACTIVE: Power consumption when CPU is running, excluding power consumed by clusters
+     *                   and cores.
+     *
+     * CPU Power Equation (assume two clusters):
+     * Total power = POWER_CPU_SUSPEND  (always added)
+     *               + POWER_CPU_IDLE   (skip this and below if in power collapse mode)
+     *               + POWER_CPU_ACTIVE (skip this and below if CPU is not running, but a wakelock
+     *                                   is held)
+     *               + cluster_power.cluster0 + cluster_power.cluster1 (skip cluster not running)
+     *               + core_power.cluster0 * num running cores in cluster 0
+     *               + core_power.cluster1 * num running cores in cluster 1
      */
+    public static final String POWER_CPU_SUSPEND = "cpu.suspend";
     public static final String POWER_CPU_IDLE = "cpu.idle";
-
-    /**
-     * Power consumption when CPU is awake (when a wake lock is held).  This
-     * should be 0 on devices that can go into full CPU power collapse even
-     * when a wake lock is held.  Otherwise, this is the power consumption in
-     * addition to POWER_CPU_IDLE due to a wake lock being held but with no
-     * CPU activity.
-     */
-    public static final String POWER_CPU_AWAKE = "cpu.awake";
-
-    /**
-     * Power consumption when CPU is in power collapse mode.
-     */
-    @Deprecated
     public static final String POWER_CPU_ACTIVE = "cpu.active";
 
     /**
@@ -94,6 +97,7 @@
     public static final String POWER_BLUETOOTH_CONTROLLER_OPERATING_VOLTAGE =
             "bluetooth.controller.voltage";
 
+    public static final String POWER_MODEM_CONTROLLER_SLEEP = "modem.controller.sleep";
     public static final String POWER_MODEM_CONTROLLER_IDLE = "modem.controller.idle";
     public static final String POWER_MODEM_CONTROLLER_RX = "modem.controller.rx";
     public static final String POWER_MODEM_CONTROLLER_TX = "modem.controller.tx";
@@ -182,9 +186,6 @@
      */
     public static final String POWER_CAMERA = "camera.avg";
 
-    @Deprecated
-    public static final String POWER_CPU_SPEEDS = "cpu.speeds";
-
     /**
      * Power consumed by wif batched scaning.  Broken down into bins by
      * Channels Scanned per Hour.  May do 1-720 scans per hour of 1-100 channels
@@ -197,7 +198,15 @@
      */
     public static final String POWER_BATTERY_CAPACITY = "battery.capacity";
 
-    static final HashMap<String, Object> sPowerMap = new HashMap<>();
+    /**
+     * A map from Power Use Item to its power consumption.
+     */
+    static final HashMap<String, Double> sPowerItemMap = new HashMap<>();
+    /**
+     * A map from Power Use Item to an array of its power consumption
+     * (for items with variable power e.g. CPU).
+     */
+    static final HashMap<String, Double[]> sPowerArrayMap = new HashMap<>();
 
     private static final String TAG_DEVICE = "device";
     private static final String TAG_ITEM = "item";
@@ -207,23 +216,32 @@
 
     private static final Object sLock = new Object();
 
+    @VisibleForTesting
     public PowerProfile(Context context) {
-        // Read the XML file for the given profile (normally only one per
-        // device)
+        this(context, false);
+    }
+
+    /**
+     * For PowerProfileTest
+     */
+    @VisibleForTesting
+    public PowerProfile(Context context, boolean forTest) {
+        // Read the XML file for the given profile (normally only one per device)
         synchronized (sLock) {
-            if (sPowerMap.size() == 0) {
-                readPowerValuesFromXml(context);
+            if (sPowerItemMap.size() == 0 && sPowerArrayMap.size() == 0) {
+                readPowerValuesFromXml(context, forTest);
             }
             initCpuClusters();
         }
     }
 
-    private void readPowerValuesFromXml(Context context) {
-        int id = com.android.internal.R.xml.power_profile;
+    private void readPowerValuesFromXml(Context context, boolean forTest) {
+        final int id = forTest ? com.android.internal.R.xml.power_profile_test :
+                com.android.internal.R.xml.power_profile;
         final Resources resources = context.getResources();
         XmlResourceParser parser = resources.getXml(id);
         boolean parsingArray = false;
-        ArrayList<Double> array = new ArrayList<Double>();
+        ArrayList<Double> array = new ArrayList<>();
         String arrayName = null;
 
         try {
@@ -237,7 +255,7 @@
 
                 if (parsingArray && !element.equals(TAG_ARRAYITEM)) {
                     // Finish array
-                    sPowerMap.put(arrayName, array.toArray(new Double[array.size()]));
+                    sPowerArrayMap.put(arrayName, array.toArray(new Double[array.size()]));
                     parsingArray = false;
                 }
                 if (element.equals(TAG_ARRAY)) {
@@ -255,7 +273,7 @@
                         } catch (NumberFormatException nfe) {
                         }
                         if (element.equals(TAG_ITEM)) {
-                            sPowerMap.put(name, value);
+                            sPowerItemMap.put(name, value);
                         } else if (parsingArray) {
                             array.add(value);
                         }
@@ -263,7 +281,7 @@
                 }
             }
             if (parsingArray) {
-                sPowerMap.put(arrayName, array.toArray(new Double[array.size()]));
+                sPowerArrayMap.put(arrayName, array.toArray(new Double[array.size()]));
             }
         } catch (XmlPullParserException e) {
             throw new RuntimeException(e);
@@ -279,10 +297,6 @@
                 com.android.internal.R.integer.config_bluetooth_rx_cur_ma,
                 com.android.internal.R.integer.config_bluetooth_tx_cur_ma,
                 com.android.internal.R.integer.config_bluetooth_operating_voltage_mv,
-                com.android.internal.R.integer.config_wifi_idle_receive_cur_ma,
-                com.android.internal.R.integer.config_wifi_active_rx_cur_ma,
-                com.android.internal.R.integer.config_wifi_tx_cur_ma,
-                com.android.internal.R.integer.config_wifi_operating_voltage_mv,
         };
 
         String[] configResIdKeys = new String[]{
@@ -290,62 +304,62 @@
                 POWER_BLUETOOTH_CONTROLLER_RX,
                 POWER_BLUETOOTH_CONTROLLER_TX,
                 POWER_BLUETOOTH_CONTROLLER_OPERATING_VOLTAGE,
-                POWER_WIFI_CONTROLLER_IDLE,
-                POWER_WIFI_CONTROLLER_RX,
-                POWER_WIFI_CONTROLLER_TX,
-                POWER_WIFI_CONTROLLER_OPERATING_VOLTAGE,
         };
 
         for (int i = 0; i < configResIds.length; i++) {
             String key = configResIdKeys[i];
             // if we already have some of these parameters in power_profile.xml, ignore the
             // value in config.xml
-            if ((sPowerMap.containsKey(key) && (Double) sPowerMap.get(key) > 0)) {
+            if ((sPowerItemMap.containsKey(key) && sPowerItemMap.get(key) > 0)) {
                 continue;
             }
             int value = resources.getInteger(configResIds[i]);
             if (value > 0) {
-                sPowerMap.put(key, (double) value);
+                sPowerItemMap.put(key, (double) value);
             }
         }
     }
 
     private CpuClusterKey[] mCpuClusters;
 
-    private static final String POWER_CPU_CLUSTER_CORE_COUNT = "cpu.clusters.cores";
-    private static final String POWER_CPU_CLUSTER_SPEED_PREFIX = "cpu.speeds.cluster";
-    private static final String POWER_CPU_CLUSTER_ACTIVE_PREFIX = "cpu.active.cluster";
+    private static final String CPU_PER_CLUSTER_CORE_COUNT = "cpu.clusters.cores";
+    private static final String CPU_CLUSTER_POWER_COUNT = "cpu.cluster_power.cluster";
+    private static final String CPU_CORE_SPEED_PREFIX = "cpu.core_speeds.cluster";
+    private static final String CPU_CORE_POWER_PREFIX = "cpu.core_power.cluster";
 
-    @SuppressWarnings("deprecation")
     private void initCpuClusters() {
-        // Figure out how many CPU clusters we're dealing with
-        final Object obj = sPowerMap.get(POWER_CPU_CLUSTER_CORE_COUNT);
-        if (obj == null || !(obj instanceof Double[])) {
+        if (sPowerArrayMap.containsKey(CPU_PER_CLUSTER_CORE_COUNT)) {
+            final Double[] data = sPowerArrayMap.get(CPU_PER_CLUSTER_CORE_COUNT);
+            mCpuClusters = new CpuClusterKey[data.length];
+            for (int cluster = 0; cluster < data.length; cluster++) {
+                int numCpusInCluster = (int) Math.round(data[cluster]);
+                mCpuClusters[cluster] = new CpuClusterKey(
+                        CPU_CORE_SPEED_PREFIX + cluster, CPU_CLUSTER_POWER_COUNT + cluster,
+                        CPU_CORE_POWER_PREFIX + cluster, numCpusInCluster);
+            }
+        } else {
             // Default to single.
             mCpuClusters = new CpuClusterKey[1];
-            mCpuClusters[0] = new CpuClusterKey(POWER_CPU_SPEEDS, POWER_CPU_ACTIVE, 1);
-
-        } else {
-            final Double[] array = (Double[]) obj;
-            mCpuClusters = new CpuClusterKey[array.length];
-            for (int cluster = 0; cluster < array.length; cluster++) {
-                int numCpusInCluster = (int) Math.round(array[cluster]);
-                mCpuClusters[cluster] = new CpuClusterKey(
-                        POWER_CPU_CLUSTER_SPEED_PREFIX + cluster,
-                        POWER_CPU_CLUSTER_ACTIVE_PREFIX + cluster,
-                        numCpusInCluster);
+            int numCpus = 1;
+            if (sPowerItemMap.containsKey(CPU_PER_CLUSTER_CORE_COUNT)) {
+                numCpus = (int) Math.round(sPowerItemMap.get(CPU_PER_CLUSTER_CORE_COUNT));
             }
+            mCpuClusters[0] = new CpuClusterKey(CPU_CORE_SPEED_PREFIX + 0,
+                    CPU_CLUSTER_POWER_COUNT + 0, CPU_CORE_POWER_PREFIX + 0, numCpus);
         }
     }
 
     public static class CpuClusterKey {
-        private final String timeKey;
-        private final String powerKey;
+        private final String freqKey;
+        private final String clusterPowerKey;
+        private final String corePowerKey;
         private final int numCpus;
 
-        private CpuClusterKey(String timeKey, String powerKey, int numCpus) {
-            this.timeKey = timeKey;
-            this.powerKey = powerKey;
+        private CpuClusterKey(String freqKey, String clusterPowerKey,
+                String corePowerKey, int numCpus) {
+            this.freqKey = freqKey;
+            this.clusterPowerKey = clusterPowerKey;
+            this.corePowerKey = corePowerKey;
             this.numCpus = numCpus;
         }
     }
@@ -354,21 +368,30 @@
         return mCpuClusters.length;
     }
 
-    public int getNumCoresInCpuCluster(int index) {
-        return mCpuClusters[index].numCpus;
+    public int getNumCoresInCpuCluster(int cluster) {
+        return mCpuClusters[cluster].numCpus;
     }
 
-    public int getNumSpeedStepsInCpuCluster(int index) {
-        Object value = sPowerMap.get(mCpuClusters[index].timeKey);
-        if (value != null && value instanceof Double[]) {
-            return ((Double[])value).length;
+    public int getNumSpeedStepsInCpuCluster(int cluster) {
+        if (cluster < 0 || cluster >= mCpuClusters.length) {
+            return 0; // index out of bound
+        }
+        if (sPowerArrayMap.containsKey(mCpuClusters[cluster].freqKey)) {
+            return sPowerArrayMap.get(mCpuClusters[cluster].freqKey).length;
         }
         return 1; // Only one speed
     }
 
-    public double getAveragePowerForCpu(int cluster, int step) {
+    public double getAveragePowerForCpuCluster(int cluster) {
         if (cluster >= 0 && cluster < mCpuClusters.length) {
-            return getAveragePower(mCpuClusters[cluster].powerKey, step);
+            return getAveragePower(mCpuClusters[cluster].clusterPowerKey);
+        }
+        return 0;
+    }
+
+    public double getAveragePowerForCpuCore(int cluster, int step) {
+        if (cluster >= 0 && cluster < mCpuClusters.length) {
+            return getAveragePower(mCpuClusters[cluster].corePowerKey, step);
         }
         return 0;
     }
@@ -379,14 +402,10 @@
      * @return the number of memory bandwidth buckets.
      */
     public int getNumElements(String key) {
-        if (sPowerMap.containsKey(key)) {
-            Object data = sPowerMap.get(key);
-            if (data instanceof Double[]) {
-                final Double[] values = (Double[]) data;
-                return values.length;
-            } else {
-                return 1;
-            }
+        if (sPowerItemMap.containsKey(key)) {
+            return 1;
+        } else if (sPowerArrayMap.containsKey(key)) {
+            return sPowerArrayMap.get(key).length;
         }
         return 0;
     }
@@ -399,13 +418,10 @@
      * @return the average current in milliAmps.
      */
     public double getAveragePowerOrDefault(String type, double defaultValue) {
-        if (sPowerMap.containsKey(type)) {
-            Object data = sPowerMap.get(type);
-            if (data instanceof Double[]) {
-                return ((Double[])data)[0];
-            } else {
-                return (Double) sPowerMap.get(type);
-            }
+        if (sPowerItemMap.containsKey(type)) {
+            return sPowerItemMap.get(type);
+        } else if (sPowerArrayMap.containsKey(type)) {
+            return sPowerArrayMap.get(type)[0];
         } else {
             return defaultValue;
         }
@@ -429,19 +445,16 @@
      * @return the average current in milliAmps.
      */
     public double getAveragePower(String type, int level) {
-        if (sPowerMap.containsKey(type)) {
-            Object data = sPowerMap.get(type);
-            if (data instanceof Double[]) {
-                final Double[] values = (Double[]) data;
-                if (values.length > level && level >= 0) {
-                    return values[level];
-                } else if (level < 0 || values.length == 0) {
-                    return 0;
-                } else {
-                    return values[values.length - 1];
-                }
+        if (sPowerItemMap.containsKey(type)) {
+            return sPowerItemMap.get(type);
+        } else if (sPowerArrayMap.containsKey(type)) {
+            final Double[] values = sPowerArrayMap.get(type);
+            if (values.length > level && level >= 0) {
+                return values[level];
+            } else if (level < 0 || values.length == 0) {
+                return 0;
             } else {
-                return (Double) data;
+                return values[values.length - 1];
             }
         } else {
             return 0;
diff --git a/core/java/com/android/internal/os/WakelockPowerCalculator.java b/core/java/com/android/internal/os/WakelockPowerCalculator.java
index c7897b2..486b584 100644
--- a/core/java/com/android/internal/os/WakelockPowerCalculator.java
+++ b/core/java/com/android/internal/os/WakelockPowerCalculator.java
@@ -26,7 +26,7 @@
     private long mTotalAppWakelockTimeMs = 0;
 
     public WakelockPowerCalculator(PowerProfile profile) {
-        mPowerWakelock = profile.getAveragePower(PowerProfile.POWER_CPU_AWAKE);
+        mPowerWakelock = profile.getAveragePower(PowerProfile.POWER_CPU_IDLE);
     }
 
     @Override
diff --git a/core/java/com/android/internal/os/logging/MetricsLoggerWrapper.java b/core/java/com/android/internal/os/logging/MetricsLoggerWrapper.java
new file mode 100644
index 0000000..245a66e
--- /dev/null
+++ b/core/java/com/android/internal/os/logging/MetricsLoggerWrapper.java
@@ -0,0 +1,99 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.os.logging;
+
+import android.content.Context;
+import android.util.StatsLog;
+
+import com.android.internal.logging.MetricsLogger;
+import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
+
+/**
+ * Used to wrap different logging calls in one, so that client side code base is clean and more
+ * readable.
+ */
+public class MetricsLoggerWrapper {
+
+    private static final int METRIC_VALUE_DISMISSED_BY_TAP = 0;
+    private static final int METRIC_VALUE_DISMISSED_BY_DRAG = 1;
+
+    public static void logPictureInPictureDismissByTap(Context context) {
+        MetricsLogger.action(context, MetricsEvent.ACTION_PICTURE_IN_PICTURE_DISMISSED,
+                METRIC_VALUE_DISMISSED_BY_TAP);
+        StatsLog.write(StatsLog.PICTURE_IN_PICTURE_STATE_CHANGED,
+                context.getUserId(),
+                context.getApplicationInfo().packageName,
+                context.getApplicationInfo().className,
+                StatsLog.PICTURE_IN_PICTURE_STATE_CHANGED__STATE__DISMISSED);
+    }
+
+    public static void logPictureInPictureDismissByDrag(Context context) {
+        MetricsLogger.action(context,
+                MetricsEvent.ACTION_PICTURE_IN_PICTURE_DISMISSED,
+                METRIC_VALUE_DISMISSED_BY_DRAG);
+        StatsLog.write(StatsLog.PICTURE_IN_PICTURE_STATE_CHANGED,
+                context.getUserId(),
+                context.getApplicationInfo().packageName,
+                context.getApplicationInfo().className,
+                StatsLog.PICTURE_IN_PICTURE_STATE_CHANGED__STATE__DISMISSED);
+    }
+
+    public static void logPictureInPictureMinimize(Context context, boolean isMinimized) {
+        MetricsLogger.action(context, MetricsEvent.ACTION_PICTURE_IN_PICTURE_MINIMIZED,
+                isMinimized);
+        if (isMinimized) {
+            StatsLog.write(StatsLog.PICTURE_IN_PICTURE_STATE_CHANGED,
+                    context.getUserId(),
+                    context.getApplicationInfo().packageName,
+                    context.getApplicationInfo().className,
+                    StatsLog.PICTURE_IN_PICTURE_STATE_CHANGED__STATE__MINIMIZED);
+        } else {
+            StatsLog.write(StatsLog.PICTURE_IN_PICTURE_STATE_CHANGED,
+                    context.getUserId(),
+                    context.getApplicationInfo().packageName,
+                    context.getApplicationInfo().className,
+                    StatsLog.PICTURE_IN_PICTURE_STATE_CHANGED__STATE__EXPANDED_TO_FULL_SCREEN);
+        }
+    }
+
+    public static void logPictureInPictureMenuVisible(Context context, boolean menuStateFull) {
+        MetricsLogger.visibility(context, MetricsEvent.ACTION_PICTURE_IN_PICTURE_MENU,
+                menuStateFull);
+    }
+
+    public static void logPictureInPictureEnter(Context context,
+            boolean supportsEnterPipOnTaskSwitch) {
+        MetricsLogger.action(context, MetricsEvent.ACTION_PICTURE_IN_PICTURE_ENTERED,
+                supportsEnterPipOnTaskSwitch);
+        if (supportsEnterPipOnTaskSwitch) {
+            StatsLog.write(StatsLog.PICTURE_IN_PICTURE_STATE_CHANGED, context.getUserId(),
+                    context.getApplicationInfo().packageName,
+                    context.getApplicationInfo().className,
+                    StatsLog.PICTURE_IN_PICTURE_STATE_CHANGED__STATE__ENTERED);
+        }
+    }
+
+    public static void logPictureInPictureFullScreen(Context context) {
+        MetricsLogger.action(context,
+                MetricsEvent.ACTION_PICTURE_IN_PICTURE_EXPANDED_TO_FULLSCREEN);
+        StatsLog.write(StatsLog.PICTURE_IN_PICTURE_STATE_CHANGED,
+                context.getUserId(),
+                context.getApplicationInfo().packageName,
+                context.getApplicationInfo().className,
+                StatsLog.PICTURE_IN_PICTURE_STATE_CHANGED__STATE__EXPANDED_TO_FULL_SCREEN);
+    }
+}
diff --git a/core/java/com/android/internal/statusbar/IStatusBar.aidl b/core/java/com/android/internal/statusbar/IStatusBar.aidl
index 5ec9094..e097362a 100644
--- a/core/java/com/android/internal/statusbar/IStatusBar.aidl
+++ b/core/java/com/android/internal/statusbar/IStatusBar.aidl
@@ -55,7 +55,7 @@
             boolean showImeSwitcher);
     void setWindowState(int window, int state);
 
-    void showRecentApps(boolean triggeredFromAltTab, boolean fromHome);
+    void showRecentApps(boolean triggeredFromAltTab);
     void hideRecentApps(boolean triggeredFromAltTab, boolean triggeredFromHomeKey);
     void toggleRecentApps();
     void toggleSplitScreen();
@@ -115,7 +115,7 @@
     /**
      * Notifies the status bar that a new rotation suggestion is available.
      */
-    void onProposedRotationChanged(int rotation);
+    void onProposedRotationChanged(int rotation, boolean isValid);
 
     /**
      * Set whether the top app currently hides the statusbar.
diff --git a/core/java/com/android/internal/util/ConcurrentUtils.java b/core/java/com/android/internal/util/ConcurrentUtils.java
index e35f9f4..e08eb58 100644
--- a/core/java/com/android/internal/util/ConcurrentUtils.java
+++ b/core/java/com/android/internal/util/ConcurrentUtils.java
@@ -18,11 +18,13 @@
 
 import android.os.Process;
 
+import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.ExecutionException;
 import java.util.concurrent.ExecutorService;
 import java.util.concurrent.Executors;
 import java.util.concurrent.Future;
 import java.util.concurrent.ThreadFactory;
+import java.util.concurrent.TimeUnit;
 import java.util.concurrent.atomic.AtomicInteger;
 
 /**
@@ -86,4 +88,27 @@
         }
     }
 
+    /**
+     * Waits for {@link CountDownLatch#countDown()} to be called on the {@param countDownLatch}.
+     * <p>If {@link CountDownLatch#countDown()} doesn't occur within {@param timeoutMs}, this
+     * method will throw {@code IllegalStateException}
+     * <p>If {@code InterruptedException} occurs, this method will interrupt the current thread
+     * and throw {@code IllegalStateException}
+     *
+     * @param countDownLatch the CountDownLatch which {@link CountDownLatch#countDown()} is
+     *                       being waited on.
+     * @param timeoutMs the maximum time waited for {@link CountDownLatch#countDown()}
+     * @param description a short description of the operation
+     */
+    public static void waitForCountDownNoInterrupt(CountDownLatch countDownLatch, long timeoutMs,
+            String description) {
+        try {
+            if (!countDownLatch.await(timeoutMs, TimeUnit.MILLISECONDS)) {
+                throw new IllegalStateException(description + " timed out.");
+            }
+        } catch (InterruptedException e) {
+            Thread.currentThread().interrupt();
+            throw new IllegalStateException(description + " interrupted.");
+        }
+    }
 }
diff --git a/core/java/com/android/internal/util/DumpUtils.java b/core/java/com/android/internal/util/DumpUtils.java
index 66b777e..2b51033 100644
--- a/core/java/com/android/internal/util/DumpUtils.java
+++ b/core/java/com/android/internal/util/DumpUtils.java
@@ -102,6 +102,7 @@
             case android.os.Process.ROOT_UID:
             case android.os.Process.SYSTEM_UID:
             case android.os.Process.SHELL_UID:
+            case android.os.Process.INCIDENTD_UID:
                 return true;
         }
 
diff --git a/core/java/com/android/internal/util/ScreenshotHelper.java b/core/java/com/android/internal/util/ScreenshotHelper.java
index f7ea787..7fd94c6 100644
--- a/core/java/com/android/internal/util/ScreenshotHelper.java
+++ b/core/java/com/android/internal/util/ScreenshotHelper.java
@@ -1,5 +1,6 @@
 package com.android.internal.util;
 
+import android.annotation.NonNull;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
@@ -32,8 +33,18 @@
         mContext = context;
     }
 
+    /**
+     * Request a screenshot be taken.
+     *
+     * @param screenshotType The type of screenshot, for example either
+     *                       {@link android.view.WindowManager.TAKE_SCREENSHOT_FULLSCREEN}
+     *                       or {@link android.view.WindowManager.TAKE_SCREENSHOT_SELECTED_REGION}
+     * @param hasStatus {@code true} if the status bar is currently showing. {@code false} if not.
+     * @param hasNav {@code true} if the navigation bar is currently showing. {@code false} if not.
+     * @param handler A handler used in case the screenshot times out
+     */
     public void takeScreenshot(final int screenshotType, final boolean hasStatus,
-            final boolean hasNav, Handler handler) {
+            final boolean hasNav, @NonNull Handler handler) {
         synchronized (mScreenshotLock) {
             if (mScreenshotConnection != null) {
                 return;
diff --git a/core/java/com/android/internal/widget/MessagingGroup.java b/core/java/com/android/internal/widget/MessagingGroup.java
index 33977f3..5577d6e 100644
--- a/core/java/com/android/internal/widget/MessagingGroup.java
+++ b/core/java/com/android/internal/widget/MessagingGroup.java
@@ -89,9 +89,12 @@
         mAvatarView = findViewById(R.id.message_icon);
     }
 
-    public void setSender(Notification.Person sender) {
+    public void setSender(Notification.Person sender, CharSequence nameOverride) {
         mSender = sender;
-        mSenderName.setText(sender.getName());
+        if (nameOverride == null) {
+            nameOverride = sender.getName();
+        }
+        mSenderName.setText(nameOverride);
         mNeedsGeneratedAvatar = sender.getIcon() == null;
         if (!mNeedsGeneratedAvatar) {
             setAvatar(sender.getIcon());
diff --git a/core/java/com/android/internal/widget/MessagingLayout.java b/core/java/com/android/internal/widget/MessagingLayout.java
index 7a64cad..d45c086 100644
--- a/core/java/com/android/internal/widget/MessagingLayout.java
+++ b/core/java/com/android/internal/widget/MessagingLayout.java
@@ -80,6 +80,7 @@
     private boolean mIsOneToOne;
     private ArrayList<MessagingGroup> mAddedGroups = new ArrayList<>();
     private Notification.Person mUser;
+    private CharSequence mNameReplacement;
 
     public MessagingLayout(@NonNull Context context) {
         super(context);
@@ -121,6 +122,11 @@
     }
 
     @RemotableViewMethod
+    public void setNameReplacement(CharSequence nameReplacement) {
+        mNameReplacement = nameReplacement;
+    }
+
+    @RemotableViewMethod
     public void setData(Bundle extras) {
         Parcelable[] messages = extras.getParcelableArray(Notification.EXTRA_MESSAGES);
         List<Notification.MessagingStyle.Message> newMessages
@@ -326,7 +332,12 @@
                 mAddedGroups.add(newGroup);
             }
             newGroup.setLayoutColor(mLayoutColor);
-            newGroup.setSender(senders.get(groupIndex));
+            Notification.Person sender = senders.get(groupIndex);
+            CharSequence nameOverride = null;
+            if (sender != mUser && mNameReplacement != null) {
+                nameOverride = mNameReplacement;
+            }
+            newGroup.setSender(sender, nameOverride);
             mGroups.add(newGroup);
 
             if (mMessagingLinearLayout.indexOfChild(newGroup) != groupIndex) {
diff --git a/core/jni/android/graphics/GraphicsJNI.h b/core/jni/android/graphics/GraphicsJNI.h
index 7f4b384..dcb81fa 100644
--- a/core/jni/android/graphics/GraphicsJNI.h
+++ b/core/jni/android/graphics/GraphicsJNI.h
@@ -66,10 +66,6 @@
     static SkPixelRef* refSkPixelRef(JNIEnv*, jobject bitmap);
     static SkRegion* getNativeRegion(JNIEnv*, jobject region);
 
-    // Given the 'native' long held by the Rasterizer.java object, return a
-    // ref to its SkRasterizer* (or NULL).
-    static sk_sp<SkRasterizer> refNativeRasterizer(jlong rasterizerHandle);
-
     /*
      *  LegacyBitmapConfig is the old enum in Skia that matched the enum int values
      *  in Bitmap.Config. Skia no longer supports this config, but has replaced it
diff --git a/core/jni/android/opengl/util.cpp b/core/jni/android/opengl/util.cpp
index f3aeb32..888db32 100644
--- a/core/jni/android/opengl/util.cpp
+++ b/core/jni/android/opengl/util.cpp
@@ -622,7 +622,7 @@
 
 // ---------------------------------------------------------------------------
 
-static int checkFormat(SkColorType colorType, int format, int type)
+static int checkInternalFormat(SkColorType colorType, int format, int type)
 {
     switch(colorType) {
         case kN32_SkColorType:
@@ -651,6 +651,20 @@
     return -1;
 }
 
+// The internal format is no longer the same as pixel format, per Table 2 in
+// https://www.khronos.org/registry/OpenGL-Refpages/es3.1/html/glTexImage2D.xhtml
+static int getPixelFormatFromInternalFormat(uint32_t internalFormat) {
+    switch (internalFormat) {
+        // For sized internal format.
+        case GL_RGBA16F:
+            return GL_RGBA;
+        // Base internal formats and pixel formats are still the same, see Table 1 in
+        // https://www.khronos.org/registry/OpenGL-Refpages/es3.1/html/glTexImage2D.xhtml
+        default:
+            return internalFormat;
+    }
+}
+
 static int getInternalFormat(SkColorType colorType)
 {
     switch(colorType) {
@@ -716,7 +730,7 @@
     if (type < 0) {
         type = getType(colorType);
     }
-    int err = checkFormat(colorType, internalformat, type);
+    int err = checkInternalFormat(colorType, internalformat, type);
     if (err)
         return err;
     const int w = bitmap.width();
@@ -725,7 +739,8 @@
     if (internalformat == GL_PALETTE8_RGBA8_OES) {
         err = -1;
     } else {
-        glTexImage2D(target, level, internalformat, w, h, border, internalformat, type, p);
+        glTexImage2D(target, level, internalformat, w, h, border,
+                     getPixelFormatFromInternalFormat(internalformat), type, p);
     }
     return err;
 }
@@ -737,12 +752,13 @@
     SkBitmap bitmap;
     GraphicsJNI::getSkBitmap(env, jbitmap, &bitmap);
     SkColorType colorType = bitmap.colorType();
+    int internalFormat = getInternalFormat(colorType);
     if (format < 0) {
-        format = getInternalFormat(colorType);
+        format = getPixelFormatFromInternalFormat(internalFormat);
         if (format == GL_PALETTE8_RGBA8_OES)
             return -1; // glCompressedTexSubImage2D() not supported
     }
-    int err = checkFormat(colorType, format, type);
+    int err = checkInternalFormat(colorType, internalFormat, type);
     if (err)
         return err;
     const int w = bitmap.width();
diff --git a/core/jni/android_database_SQLiteCommon.cpp b/core/jni/android_database_SQLiteCommon.cpp
index eefcb74..34544d3 100644
--- a/core/jni/android_database_SQLiteCommon.cpp
+++ b/core/jni/android_database_SQLiteCommon.cpp
@@ -18,8 +18,108 @@
 
 #include <utils/String8.h>
 
+#include <map>
+
 namespace android {
 
+static const std::map<int, std::string> sErrorCodesMap = {
+    // Primary Result Code List
+    {4,     "SQLITE_ABORT"},
+    {23,    "SQLITE_AUTH"},
+    {5,     "SQLITE_BUSY"},
+    {14,    "SQLITE_CANTOPEN"},
+    {19,    "SQLITE_CONSTRAINT"},
+    {11,    "SQLITE_CORRUPT"},
+    {101,   "SQLITE_DONE"},
+    {16,    "SQLITE_EMPTY"},
+    {1,     "SQLITE_ERROR"},
+    {24,    "SQLITE_FORMAT"},
+    {13,    "SQLITE_FULL"},
+    {2,     "SQLITE_INTERNAL"},
+    {9,     "SQLITE_INTERRUPT"},
+    {10,    "SQLITE_IOERR"},
+    {6,     "SQLITE_LOCKED"},
+    {20,    "SQLITE_MISMATCH"},
+    {21,    "SQLITE_MISUSE"},
+    {22,    "SQLITE_NOLFS"},
+    {7,     "SQLITE_NOMEM"},
+    {26,    "SQLITE_NOTADB"},
+    {12,    "SQLITE_NOTFOUND"},
+    {27,    "SQLITE_NOTICE"},
+    {0,     "SQLITE_OK"},
+    {3,     "SQLITE_PERM"},
+    {15,    "SQLITE_PROTOCOL"},
+    {25,    "SQLITE_RANGE"},
+    {8,     "SQLITE_READONLY"},
+    {100,   "SQLITE_ROW"},
+    {17,    "SQLITE_SCHEMA"},
+    {18,    "SQLITE_TOOBIG"},
+    {28,    "SQLITE_WARNING"},
+    // Extended Result Code List
+    {516,   "SQLITE_ABORT_ROLLBACK"},
+    {261,   "SQLITE_BUSY_RECOVERY"},
+    {517,   "SQLITE_BUSY_SNAPSHOT"},
+    {1038,  "SQLITE_CANTOPEN_CONVPATH"},
+    {782,   "SQLITE_CANTOPEN_FULLPATH"},
+    {526,   "SQLITE_CANTOPEN_ISDIR"},
+    {270,   "SQLITE_CANTOPEN_NOTEMPDIR"},
+    {275,   "SQLITE_CONSTRAINT_CHECK"},
+    {531,   "SQLITE_CONSTRAINT_COMMITHOOK"},
+    {787,   "SQLITE_CONSTRAINT_FOREIGNKEY"},
+    {1043,  "SQLITE_CONSTRAINT_FUNCTION"},
+    {1299,  "SQLITE_CONSTRAINT_NOTNULL"},
+    {1555,  "SQLITE_CONSTRAINT_PRIMARYKEY"},
+    {2579,  "SQLITE_CONSTRAINT_ROWID"},
+    {1811,  "SQLITE_CONSTRAINT_TRIGGER"},
+    {2067,  "SQLITE_CONSTRAINT_UNIQUE"},
+    {2323,  "SQLITE_CONSTRAINT_VTAB"},
+    {267,   "SQLITE_CORRUPT_VTAB"},
+    {3338,  "SQLITE_IOERR_ACCESS"},
+    {2826,  "SQLITE_IOERR_BLOCKED"},
+    {3594,  "SQLITE_IOERR_CHECKRESERVEDLOCK"},
+    {4106,  "SQLITE_IOERR_CLOSE"},
+    {6666,  "SQLITE_IOERR_CONVPATH"},
+    {2570,  "SQLITE_IOERR_DELETE"},
+    {5898,  "SQLITE_IOERR_DELETE_NOENT"},
+    {4362,  "SQLITE_IOERR_DIR_CLOSE"},
+    {1290,  "SQLITE_IOERR_DIR_FSYNC"},
+    {1802,  "SQLITE_IOERR_FSTAT"},
+    {1034,  "SQLITE_IOERR_FSYNC"},
+    {6410,  "SQLITE_IOERR_GETTEMPPATH"},
+    {3850,  "SQLITE_IOERR_LOCK"},
+    {6154,  "SQLITE_IOERR_MMAP"},
+    {3082,  "SQLITE_IOERR_NOMEM"},
+    {2314,  "SQLITE_IOERR_RDLOCK"},
+    {266,   "SQLITE_IOERR_READ"},
+    {5642,  "SQLITE_IOERR_SEEK"},
+    {5130,  "SQLITE_IOERR_SHMLOCK"},
+    {5386,  "SQLITE_IOERR_SHMMAP"},
+    {4618,  "SQLITE_IOERR_SHMOPEN"},
+    {4874,  "SQLITE_IOERR_SHMSIZE"},
+    {522,   "SQLITE_IOERR_SHORT_READ"},
+    {1546,  "SQLITE_IOERR_TRUNCATE"},
+    {2058,  "SQLITE_IOERR_UNLOCK"},
+    {778,   "SQLITE_IOERR_WRITE"},
+    {262,   "SQLITE_LOCKED_SHAREDCACHE"},
+    {539,   "SQLITE_NOTICE_RECOVER_ROLLBACK"},
+    {283,   "SQLITE_NOTICE_RECOVER_WAL"},
+    {256,   "SQLITE_OK_LOAD_PERMANENTLY"},
+    {520,   "SQLITE_READONLY_CANTLOCK"},
+    {1032,  "SQLITE_READONLY_DBMOVED"},
+    {264,   "SQLITE_READONLY_RECOVERY"},
+    {776,   "SQLITE_READONLY_ROLLBACK"},
+    {284,   "SQLITE_WARNING_AUTOINDEX"},
+};
+
+static std::string sqlite3_error_code_to_msg(int errcode) {
+    auto it = sErrorCodesMap.find(errcode);
+    if (it != sErrorCodesMap.end()) {
+        return std::to_string(errcode) + " " + it->second;
+    } else {
+        return std::to_string(errcode);
+    }
+}
+
 /* throw a SQLiteException with a message appropriate for the error in handle */
 void throw_sqlite3_exception(JNIEnv* env, sqlite3* handle) {
     throw_sqlite3_exception(env, handle, NULL);
@@ -123,7 +223,8 @@
     if (sqlite3Message) {
         String8 fullMessage;
         fullMessage.append(sqlite3Message);
-        fullMessage.appendFormat(" (code %d)", errcode); // print extended error code
+        const char* errcode_msg = sqlite3_error_code_to_msg(errcode).c_str();
+        fullMessage.appendFormat(" (code %s)", errcode_msg); // print extended error code
         if (message) {
             fullMessage.append(": ");
             fullMessage.append(message);
diff --git a/core/jni/android_media_AudioFormat.h b/core/jni/android_media_AudioFormat.h
index 092aaf6..c79f5bd 100644
--- a/core/jni/android_media_AudioFormat.h
+++ b/core/jni/android_media_AudioFormat.h
@@ -33,6 +33,9 @@
 #define ENCODING_AAC_HE_V2      12
 #define ENCODING_IEC61937       13
 #define ENCODING_DOLBY_TRUEHD   14
+#define ENCODING_AAC_ELD        15
+#define ENCODING_AAC_XHE        16
+#define ENCODING_AC4            17
 
 #define ENCODING_INVALID    0
 #define ENCODING_DEFAULT    1
@@ -71,6 +74,12 @@
         return AUDIO_FORMAT_DOLBY_TRUEHD;
     case ENCODING_IEC61937:
         return AUDIO_FORMAT_IEC61937;
+    case ENCODING_AAC_ELD:
+        return AUDIO_FORMAT_AAC_ELD;
+    case ENCODING_AAC_XHE:
+        return AUDIO_FORMAT_AAC; // FIXME temporary value, needs addition of xHE-AAC
+    case ENCODING_AC4:
+        return AUDIO_FORMAT_AC4;
     case ENCODING_DEFAULT:
         return AUDIO_FORMAT_DEFAULT;
     default:
@@ -114,6 +123,13 @@
         return ENCODING_IEC61937;
     case AUDIO_FORMAT_DOLBY_TRUEHD:
         return ENCODING_DOLBY_TRUEHD;
+    case AUDIO_FORMAT_AAC_ELD:
+            return ENCODING_AAC_ELD;
+    // FIXME needs addition of AUDIO_FORMAT_AAC_XHE
+    //case AUDIO_FORMAT_AAC_XHE:
+    //    return ENCODING_AAC_XHE;
+    case AUDIO_FORMAT_AC4:
+        return ENCODING_AC4;
     case AUDIO_FORMAT_DEFAULT:
         return ENCODING_DEFAULT;
     default:
@@ -121,6 +137,25 @@
     }
 }
 
+// This function converts Java channel masks to a native channel mask.
+// validity should be checked with audio_is_output_channel().
+static inline audio_channel_mask_t nativeChannelMaskFromJavaChannelMasks(
+        jint channelPositionMask, jint channelIndexMask)
+{
+    // 0 is the java android.media.AudioFormat.CHANNEL_INVALID value
+    if (channelIndexMask != 0) {  // channel index mask takes priority
+        // To convert to a native channel mask, the Java channel index mask
+        // requires adding the index representation.
+        return audio_channel_mask_from_representation_and_bits(
+                        AUDIO_CHANNEL_REPRESENTATION_INDEX,
+                        channelIndexMask);
+    }
+    // To convert to a native channel mask, the Java channel position mask
+    // requires a shift by 2 to skip the two deprecated channel
+    // configurations "default" and "mono".
+    return (audio_channel_mask_t)((uint32_t)channelPositionMask >> 2);
+}
+
 static inline audio_channel_mask_t outChannelMaskToNative(int channelMask)
 {
     switch (channelMask) {
diff --git a/core/jni/android_media_AudioSystem.cpp b/core/jni/android_media_AudioSystem.cpp
index 7ec68ed..2be9471 100644
--- a/core/jni/android_media_AudioSystem.cpp
+++ b/core/jni/android_media_AudioSystem.cpp
@@ -1770,6 +1770,24 @@
                                                   (audio_devices_t)device);
 }
 
+static jboolean
+android_media_AudioSystem_isOffloadSupported(JNIEnv *env, jobject thiz,
+        jint encoding, jint sampleRate, jint channelMask, jint channelIndexMask)
+{
+    audio_offload_info_t format = AUDIO_INFO_INITIALIZER;
+    format.format = (audio_format_t) audioFormatToNative(encoding);
+    format.sample_rate = (uint32_t) sampleRate;
+    format.channel_mask = nativeChannelMaskFromJavaChannelMasks(channelMask, channelIndexMask);
+    format.stream_type = AUDIO_STREAM_MUSIC;
+    format.has_video = false;
+    format.is_streaming = false;
+    // offload duration unknown at this point:
+    // client side code cannot access "audio.offload.min.duration.secs" property to make a query
+    // agnostic of duration, so using acceptable estimate of 2mn
+    format.duration_us = 120 * 1000000;
+    return AudioSystem::isOffloadSupported(format);
+}
+
 // ----------------------------------------------------------------------------
 
 static const JNINativeMethod gMethods[] = {
@@ -1823,6 +1841,7 @@
                                     (void *)android_media_AudioSystem_registerRecordingCallback},
     {"systemReady", "()I", (void *)android_media_AudioSystem_systemReady},
     {"getStreamVolumeDB", "(III)F", (void *)android_media_AudioSystem_getStreamVolumeDB},
+    {"native_is_offload_supported", "(IIII)Z", (void *)android_media_AudioSystem_isOffloadSupported},
 };
 
 
diff --git a/core/jni/android_media_AudioTrack.cpp b/core/jni/android_media_AudioTrack.cpp
index 556ac27..11011b1 100644
--- a/core/jni/android_media_AudioTrack.cpp
+++ b/core/jni/android_media_AudioTrack.cpp
@@ -73,6 +73,7 @@
     jobject     audioTrack_ref;
     bool        busy;
     Condition   cond;
+    bool        isOffload;
 };
 
 // keep these values in sync with AudioTrack.java
@@ -90,6 +91,7 @@
     AudioTrackJniStorage() {
         mCallbackData.audioTrack_class = 0;
         mCallbackData.audioTrack_ref = 0;
+        mCallbackData.isOffload = false;
     }
 
     ~AudioTrackJniStorage() {
@@ -132,27 +134,34 @@
     }
 
     switch (event) {
-    case AudioTrack::EVENT_MARKER: {
-        JNIEnv *env = AndroidRuntime::getJNIEnv();
-        if (user != NULL && env != NULL) {
-            env->CallStaticVoidMethod(
-                callbackInfo->audioTrack_class,
-                javaAudioTrackFields.postNativeEventInJava,
-                callbackInfo->audioTrack_ref, event, 0,0, NULL);
-            if (env->ExceptionCheck()) {
-                env->ExceptionDescribe();
-                env->ExceptionClear();
+    // Offload only events
+    case AudioTrack::EVENT_STREAM_END:
+    case AudioTrack::EVENT_MORE_DATA:
+    // a.k.a. tear down
+    case AudioTrack::EVENT_NEW_IAUDIOTRACK:
+        if (callbackInfo->isOffload) {
+            JNIEnv *env = AndroidRuntime::getJNIEnv();
+            if (user != NULL && env != NULL) {
+                env->CallStaticVoidMethod(
+                        callbackInfo->audioTrack_class,
+                        javaAudioTrackFields.postNativeEventInJava,
+                        callbackInfo->audioTrack_ref, event, 0,0, NULL);
+                if (env->ExceptionCheck()) {
+                    env->ExceptionDescribe();
+                    env->ExceptionClear();
+                }
             }
-        }
         } break;
 
+    // PCM and offload events
+    case AudioTrack::EVENT_MARKER:
     case AudioTrack::EVENT_NEW_POS: {
         JNIEnv *env = AndroidRuntime::getJNIEnv();
         if (user != NULL && env != NULL) {
             env->CallStaticVoidMethod(
-                callbackInfo->audioTrack_class,
-                javaAudioTrackFields.postNativeEventInJava,
-                callbackInfo->audioTrack_ref, event, 0,0, NULL);
+                    callbackInfo->audioTrack_class,
+                    javaAudioTrackFields.postNativeEventInJava,
+                    callbackInfo->audioTrack_ref, event, 0,0, NULL);
             if (env->ExceptionCheck()) {
                 env->ExceptionDescribe();
                 env->ExceptionClear();
@@ -198,30 +207,12 @@
     return getAudioTrack(env, audioTrackObj);
 }
 
-// This function converts Java channel masks to a native channel mask.
-// validity should be checked with audio_is_output_channel().
-static inline audio_channel_mask_t nativeChannelMaskFromJavaChannelMasks(
-        jint channelPositionMask, jint channelIndexMask)
-{
-    if (channelIndexMask != 0) {  // channel index mask takes priority
-        // To convert to a native channel mask, the Java channel index mask
-        // requires adding the index representation.
-        return audio_channel_mask_from_representation_and_bits(
-                        AUDIO_CHANNEL_REPRESENTATION_INDEX,
-                        channelIndexMask);
-    }
-    // To convert to a native channel mask, the Java channel position mask
-    // requires a shift by 2 to skip the two deprecated channel
-    // configurations "default" and "mono".
-    return (audio_channel_mask_t)(channelPositionMask >> 2);
-}
-
 // ----------------------------------------------------------------------------
 static jint
 android_media_AudioTrack_setup(JNIEnv *env, jobject thiz, jobject weak_this, jobject jaa,
         jintArray jSampleRate, jint channelPositionMask, jint channelIndexMask,
         jint audioFormat, jint buffSizeInBytes, jint memoryMode, jintArray jSession,
-        jlong nativeAudioTrack) {
+        jlong nativeAudioTrack, jboolean offload) {
 
     ALOGV("sampleRates=%p, channel mask=%x, index mask=%x, audioFormat(Java)=%d, buffSize=%d"
         "nativeAudioTrack=0x%" PRIX64,
@@ -322,8 +313,19 @@
         lpJniStorage->mCallbackData.audioTrack_class = (jclass)env->NewGlobalRef(clazz);
         // we use a weak reference so the AudioTrack object can be garbage collected.
         lpJniStorage->mCallbackData.audioTrack_ref = env->NewGlobalRef(weak_this);
+        lpJniStorage->mCallbackData.isOffload = offload;
         lpJniStorage->mCallbackData.busy = false;
 
+        audio_offload_info_t offloadInfo;
+        if (offload) {
+            offloadInfo = AUDIO_INFO_INITIALIZER;
+            offloadInfo.format = format;
+            offloadInfo.sample_rate = sampleRateInHertz;
+            offloadInfo.channel_mask = nativeChannelMask;
+            offloadInfo.has_video = false;
+            offloadInfo.stream_type = AUDIO_STREAM_MUSIC; //required for offload
+        }
+
         // initialize the native AudioTrack object
         status_t status = NO_ERROR;
         switch (memoryMode) {
@@ -342,7 +344,7 @@
                     true,// thread can call Java
                     sessionId,// audio session ID
                     AudioTrack::TRANSFER_SYNC,
-                    NULL,                         // default offloadInfo
+                    offload ? &offloadInfo : NULL,
                     -1, -1,                       // default uid, pid values
                     paa);
             break;
@@ -1234,7 +1236,7 @@
     {"native_stop",          "()V",      (void *)android_media_AudioTrack_stop},
     {"native_pause",         "()V",      (void *)android_media_AudioTrack_pause},
     {"native_flush",         "()V",      (void *)android_media_AudioTrack_flush},
-    {"native_setup",     "(Ljava/lang/Object;Ljava/lang/Object;[IIIIII[IJ)I",
+    {"native_setup",     "(Ljava/lang/Object;Ljava/lang/Object;[IIIIII[IJZ)I",
                                          (void *)android_media_AudioTrack_setup},
     {"native_finalize",      "()V",      (void *)android_media_AudioTrack_finalize},
     {"native_release",       "()V",      (void *)android_media_AudioTrack_release},
diff --git a/core/proto/android/os/incident.proto b/core/proto/android/os/incident.proto
index 2752a7e..e5efb90 100644
--- a/core/proto/android/os/incident.proto
+++ b/core/proto/android/os/incident.proto
@@ -21,7 +21,6 @@
 import "frameworks/base/core/proto/android/os/batterytype.proto";
 import "frameworks/base/core/proto/android/os/cpufreq.proto";
 import "frameworks/base/core/proto/android/os/cpuinfo.proto";
-import "frameworks/base/core/proto/android/os/incidentheader.proto";
 import "frameworks/base/core/proto/android/os/kernelwake.proto";
 import "frameworks/base/core/proto/android/os/pagetypeinfo.proto";
 import "frameworks/base/core/proto/android/os/procrank.proto";
@@ -46,6 +45,7 @@
 import "frameworks/base/core/proto/android/service/procstats.proto";
 import "frameworks/base/core/proto/android/util/event_log_tags.proto";
 import "frameworks/base/core/proto/android/util/log.proto";
+import "frameworks/base/libs/incident/proto/android/os/header.proto";
 import "frameworks/base/libs/incident/proto/android/privacy.proto";
 import "frameworks/base/libs/incident/proto/android/section.proto";
 
diff --git a/core/proto/android/os/incidentheader.proto b/core/proto/android/os/incidentheader.proto
deleted file mode 100644
index f0c736a..0000000
--- a/core/proto/android/os/incidentheader.proto
+++ /dev/null
@@ -1,49 +0,0 @@
-/*
- * Copyright (C) 2017 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.
- */
-
-syntax = "proto2";
-option java_multiple_files = true;
-
-package android.os;
-
-// IncidentHeaderProto contains extra information the caller of incidentd want to
-// attach in an incident report, the data should just be informative.
-message IncidentHeaderProto {
-
-    // From statsd config, the name of the anomaly, usually human readable.
-    optional string incident_name = 1;
-
-    // Format a human readable reason why an incident report is requested.
-    // It's optional and may directly come from a user clicking the bug-report button.
-    optional string reason = 2;
-
-    // From statsd, the metric which causes the anomaly triggered.
-    optional string metric_name = 3;
-
-    // From statsd, the metric value exceeds the threshold. This is useful for
-    // ValueMetric and GaugeMetric.
-    oneof metric_value {
-        int64 int64_value = 4;
-        double double_value = 5;
-    }
-
-    // Defines which stats config used to fire the request.
-    message StatsdConfigKey {
-        optional int32 uid = 1;
-        optional string name = 2;
-    }
-    optional StatsdConfigKey config_key = 6;
-}
diff --git a/core/proto/android/providers/settings.proto b/core/proto/android/providers/settings.proto
index 8d6df12..95eb889 100644
--- a/core/proto/android/providers/settings.proto
+++ b/core/proto/android/providers/settings.proto
@@ -391,8 +391,9 @@
     optional SettingProto zram_enabled = 347;
     optional SettingProto enable_smart_replies_in_notifications = 348;
     optional SettingProto show_first_crash_dialog = 349;
+    optional SettingProto wifi_connected_mac_randomization_enabled = 350;
 
-    // Next tag = 350;
+    // Next tag = 351;
 }
 
 message SecureSettingsProto {
diff --git a/core/proto/android/server/forceappstandbytracker.proto b/core/proto/android/server/forceappstandbytracker.proto
index 8753bf7..c9f7d52 100644
--- a/core/proto/android/server/forceappstandbytracker.proto
+++ b/core/proto/android/server/forceappstandbytracker.proto
@@ -41,4 +41,13 @@
 
   // Packages that are disallowed OP_RUN_ANY_IN_BACKGROUND.
   repeated RunAnyInBackgroundRestrictedPackages run_any_in_background_restricted_packages = 5;
+
+  // Whether device is a small battery device
+  optional bool is_small_battery_device = 6;
+
+  // Whether force app standby for small battery device setting is enabled
+  optional bool force_all_apps_standby_for_small_battery = 7;
+
+  // Whether device is charging
+  optional bool is_charging = 8;
 }
diff --git a/core/proto/android/service/netstats.proto b/core/proto/android/service/netstats.proto
index 23613fd..ad9191c 100644
--- a/core/proto/android/service/netstats.proto
+++ b/core/proto/android/service/netstats.proto
@@ -63,6 +63,8 @@
     optional bool roaming = 4;
 
     optional bool metered = 5;
+
+    optional bool default_network = 6;
 }
 
 // Corresponds to NetworkStatsRecorder.
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 547e83c..93d852c 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -1747,6 +1747,12 @@
     <permission android:name="android.permission.SEND_EMBMS_INTENTS"
         android:protectionLevel="signature|privileged" />
 
+
+    <!-- Allows internal management of the sensor framework
+         @hide -->
+    <permission android:name="android.permission.MANAGE_SENSORS"
+        android:protectionLevel="signature" />
+
     <!-- Must be required by an ImsService to ensure that only the
          system can bind to it.
          <p>Protection level: signature|privileged
@@ -2307,6 +2313,11 @@
     <permission android:name="android.permission.RECOVERY"
         android:protectionLevel="signature|privileged" />
 
+    <!-- @SystemApi Allows an application to read system update info.
+         @hide -->
+    <permission android:name="android.permission.READ_SYSTEM_UPDATE_INFO"
+        android:protectionLevel="signature" />
+
     <!-- Allows the system to bind to an application's task services
          @hide -->
     <permission android:name="android.permission.BIND_JOB_SERVICE"
@@ -2837,6 +2848,14 @@
     <permission android:name="android.permission.INSTALL_SELF_UPDATES"
         android:protectionLevel="signature|privileged" />
 
+    <!-- @SystemApi Allows an application to install updates. This is a limited version
+         of {@link android.Manifest.permission#INSTALL_PACKAGES}.
+        <p>Not for use by third-party applications.
+        @hide
+    -->
+    <permission android:name="android.permission.INSTALL_PACKAGE_UPDATES"
+        android:protectionLevel="signature|privileged" />
+
     <!-- @SystemApi Allows an application to clear user data.
          <p>Not for use by third-party applications
          @hide
@@ -3186,10 +3205,14 @@
     <permission android:name="android.permission.BIND_APPWIDGET"
         android:protectionLevel="signature|privileged" />
 
+    <!-- @hide Allows sysui to manage user grants of slice permissions. -->
+    <permission android:name="android.permission.MANAGE_SLICE_PERMISSIONS"
+        android:protectionLevel="signature" />
+
     <!-- Allows an application to bind app's slices and get their
          content. This content will be surfaced to the
          user and not to leave the device.
-         <p>Not for use by third-party applications. -->
+         <p>Not for use by third-party applications.-->
     <permission android:name="android.permission.BIND_SLICE"
         android:protectionLevel="signature|privileged|development" />
 
@@ -3692,11 +3715,20 @@
     <permission android:name="android.permission.READ_RUNTIME_PROFILES"
                 android:protectionLevel="signature|privileged" />
 
+    <!-- @hide Allows audio policy management. -->
+    <permission android:name="android.permission.MANAGE_AUDIO_POLICY"
+        android:protectionLevel="signature" />
+
     <!-- @SystemApi Allows an application to turn on / off quiet mode.
          @hide <p>Not for use by third-party applications. -->
     <permission android:name="android.permission.MODIFY_QUIET_MODE"
                 android:protectionLevel="signature|privileged" />
 
+    <!-- Allows internal management of the camera framework
+         @hide -->
+    <permission android:name="android.permission.MANAGE_CAMERA"
+        android:protectionLevel="signature" />
+
     <!-- Allows an application to control remote animations. See
          {@link ActivityOptions#makeRemoteAnimation}
          @hide <p>Not for use by third-party applications. -->
diff --git a/core/res/res/drawable/ic_screenshot.xml b/core/res/res/drawable/ic_screenshot.xml
index 3074b28..24dd4d8 100644
--- a/core/res/res/drawable/ic_screenshot.xml
+++ b/core/res/res/drawable/ic_screenshot.xml
@@ -17,10 +17,8 @@
     android:width="24.0dp"
     android:height="24.0dp"
     android:viewportWidth="24.0"
-    android:viewportHeight="24.0">
-    <path
-        android:pathData="M0,0h24v24H0V0z"
-        android:fillColor="#00000000"/>
+    android:viewportHeight="24.0"
+    android:tint="?attr/colorControlNormal">
     <path
         android:fillColor="#FF000000"
         android:pathData="M17.0,1.0L7.0,1.0C5.9,1.0 5.0,1.9 5.0,3.0l0.0,18.0c0.0,1.1 0.9,2.0 2.0,2.0l10.0,0.0c1.1,0.0 2.0,-0.9 2.0,-2.0L19.0,3.0C19.0,1.9 18.1,1.0 17.0,1.0zM17.0,20.0L7.0,20.0L7.0,4.0l10.0,0.0L17.0,20.0z"/>
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index f084159..c623c9a 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -430,7 +430,7 @@
     <integer translatable="false" name="config_mobile_hotspot_provision_check_period">24</integer>
 
     <!-- Activity name to enable wifi tethering after provisioning app succeeds -->
-    <string translatable="false" name="config_wifi_tether_enable">com.android.settings/.TetherService</string>
+    <string translatable="false" name="config_wifi_tether_enable">com.android.settings/.wifi.tether.TetherService</string>
 
     <!-- Controls the WiFi wakeup feature.
           0 = Not available.
@@ -640,18 +640,12 @@
     <!-- Wifi driver supports batched scan -->
     <bool translatable="false" name="config_wifi_batched_scan_supported">false</bool>
 
-    <!-- Idle Receive current for wifi radio. 0 by default-->
-    <integer translatable="false" name="config_wifi_idle_receive_cur_ma">0</integer>
+    <!-- Wifi driver supports Automatic channel selection (ACS) for softap -->
+    <bool translatable="false" name="config_wifi_softap_acs_supported">false</bool>
 
-    <!-- Rx current for wifi radio. 0 by default-->
-    <integer translatable="false" name="config_wifi_active_rx_cur_ma">0</integer>
-
-    <!-- Tx current for wifi radio. 0 by default-->
-    <integer translatable="false" name="config_wifi_tx_cur_ma">0</integer>
-
-    <!-- Operating volatage for wifi radio. 0 by default-->
-    <integer translatable="false" name="config_wifi_operating_voltage_mv">0</integer>
-
+    <!-- Wifi driver supports IEEE80211AC for softap -->
+    <bool translatable="false" name="config_wifi_softap_ieee80211ac_supported">false</bool>
+    
     <!-- Flag indicating whether the we should enable the automatic brightness in Settings.
          Software implementation will be used if config_hardware_auto_brightness_available is not set -->
     <bool name="config_automatic_brightness_available">false</bool>
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 2cfe919..170ba42 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -4817,4 +4817,11 @@
     <string name="harmful_app_warning_launch_anyway">Launch anyway</string>
     <!-- Title for the harmful app warning dialog. -->
     <string name="harmful_app_warning_title">Uninstall harmful app?</string>
+
+    <!-- Text describing a permission request for one app to show another app's
+         slices [CHAR LIMIT=NONE] -->
+    <string name="slices_permission_request"><xliff:g id="app" example="Example App">%1$s</xliff:g> wants to show <xliff:g id="app_2" example="Other Example App">%2$s</xliff:g> slices</string>
+
+    <!-- Notification action for editing a screenshot (drawing on it, cropping it, etc) -->
+    <string name="screenshot_edit">Edit</string>
 </resources>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 1993ab4..4ef0a6c 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -309,6 +309,8 @@
   <java-symbol type="bool" name="config_useFixedVolume" />
   <java-symbol type="bool" name="config_forceDefaultOrientation" />
   <java-symbol type="bool" name="config_wifi_batched_scan_supported" />
+  <java-symbol type="bool" name="config_wifi_softap_acs_supported" />
+  <java-symbol type="bool" name="config_wifi_softap_ieee80211ac_supported" />
   <java-symbol type="bool" name="config_enableMultiUserUI"/>
   <java-symbol type="bool" name="config_disableUsbPermissionDialogs"/>
   <java-symbol type="bool" name="config_hasRecents" />
@@ -382,10 +384,6 @@
   <java-symbol type="integer" name="config_wifi_framework_current_network_boost" />
   <java-symbol type="string"  name="config_wifi_random_mac_oui" />
   <java-symbol type="integer"  name="config_wifi_network_switching_blacklist_time" />
-  <java-symbol type="integer"  name="config_wifi_idle_receive_cur_ma" />
-  <java-symbol type="integer"  name="config_wifi_active_rx_cur_ma" />
-  <java-symbol type="integer"  name="config_wifi_tx_cur_ma" />
-  <java-symbol type="integer"  name="config_wifi_operating_voltage_mv" />
   <java-symbol type="string"  name="config_wifi_framework_sap_2G_channel_list" />
   <java-symbol type="integer" name="config_wifi_framework_max_tx_rate_for_full_scan" />
   <java-symbol type="integer" name="config_wifi_framework_max_rx_rate_for_full_scan" />
@@ -1508,6 +1506,7 @@
   <java-symbol type="xml" name="password_kbd_symbols" />
   <java-symbol type="xml" name="password_kbd_symbols_shift" />
   <java-symbol type="xml" name="power_profile" />
+  <java-symbol type="xml" name="power_profile_test" />
   <java-symbol type="xml" name="sms_short_codes" />
   <java-symbol type="xml" name="audio_assets" />
   <java-symbol type="xml" name="global_keys" />
@@ -3224,4 +3223,8 @@
   <java-symbol type="string" name="config_defaultAssistantAccessPackage" />
 
   <java-symbol type="bool" name="config_supportBluetoothPersistedState" />
+
+  <java-symbol type="string" name="slices_permission_request" />
+
+  <java-symbol type="string" name="screenshot_edit" />
 </resources>
diff --git a/core/res/res/xml/power_profile.xml b/core/res/res/xml/power_profile.xml
index 6e31cd2..bc4b10f 100644
--- a/core/res/res/xml/power_profile.xml
+++ b/core/res/res/xml/power_profile.xml
@@ -51,15 +51,6 @@
       <value>0.1</value> <!-- ~1mA -->
   </array>
 
-
-  <!-- Radio related values. For modems WITH energy reporting support in firmware, use
-       modem.controller.idle, modem.controller.tx, modem.controller.rx, modem.controller.voltage.
-       -->
-  <item name="modem.controller.idle">0</item>
-  <item name="modem.controller.rx">0</item>
-  <item name="modem.controller.tx">0</item>
-  <item name="modem.controller.voltage">0</item>
-
   <!-- A list of heterogeneous CPU clusters, where the value for each cluster represents the
        number of CPU cores for that cluster.
 
@@ -123,4 +114,17 @@
     <value>2</value>    <!-- 4097-/hr -->
   </array>
 
+  <!-- Cellular modem related values. Default is 0.-->
+  <item name="modem.controller.sleep">0</item>
+  <item name="modem.controller.idle">0</item>
+  <item name="modem.controller.rx">0</item>
+  <array name="modem.controller.tx"> <!-- Strength 0 to 4 -->
+    <value>0</value>
+    <value>0</value>
+    <value>0</value>
+    <value>0</value>
+    <value>0</value>
+  </array>
+  <item name="modem.controller.voltage">0</item>
+
 </device>
diff --git a/core/res/res/xml/power_profile_test.xml b/core/res/res/xml/power_profile_test.xml
new file mode 100644
index 0000000..cdb7134
--- /dev/null
+++ b/core/res/res/xml/power_profile_test.xml
@@ -0,0 +1,116 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+**
+** Copyright 2017, 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.
+*/
+-->
+<device name="Android">
+    <!-- All values are in mAh except as noted.
+         This file is for PowerProfileTest.java. Changes must be synced between these two. Since
+         power_profile.xml may be overridden by actual device's power_profile.xml at compile time,
+         this test config ensures we have something constant to test against. Values below are
+         sample values, not meant to reflect any real device.
+    -->
+
+    <!-- Nothing -->
+    <item name="none">0</item>
+
+    <!-- This is the battery capacity in mAh -->
+    <item name="battery.capacity">3000</item>
+
+    <!-- Number of cores each CPU cluster contains -->
+    <array name="cpu.clusters.cores">
+        <value>4</value> <!-- Cluster 0 has 4 cores (cpu0, cpu1, cpu2, cpu3) -->
+        <value>4</value> <!-- Cluster 1 has 4 cores (cpu4, cpu5, cpu5, cpu7) -->
+    </array>
+
+    <!-- Power consumption when CPU is suspended -->
+    <item name="cpu.suspend">5</item>
+    <!-- Additional power consumption when CPU is in a kernel idle loop -->
+    <item name="cpu.idle">1.11</item>
+    <!-- Additional power consumption by CPU excluding cluster and core when  running -->
+    <item name="cpu.active">2.55</item>
+
+    <!-- Additional power consumption by CPU cluster0 itself when running excluding cores in it -->
+    <item name="cpu.cluster_power.cluster0">2.11</item>
+    <!-- Additional power consumption by CPU cluster1 itself when running excluding cores in it -->
+    <item name="cpu.cluster_power.cluster1">2.22</item>
+
+    <!-- Different CPU speeds as reported in
+         /sys/devices/system/cpu/cpu0/cpufreq/stats/scaling_available_frequencies -->
+    <array name="cpu.core_speeds.cluster0">
+        <value>300000</value> <!-- 300 MHz CPU speed -->
+        <value>1000000</value> <!-- 1000 MHz CPU speed -->
+        <value>2000000</value> <!-- 2000 MHz CPU speed -->
+    </array>
+    <!-- Different CPU speeds as reported in
+         /sys/devices/system/cpu/cpu4/cpufreq/stats/scaling_available_frequencies -->
+    <array name="cpu.core_speeds.cluster1">
+        <value>300000</value> <!-- 300 MHz CPU speed -->
+        <value>1000000</value> <!-- 1000 MHz CPU speed -->
+        <value>2500000</value> <!-- 2500 MHz CPU speed -->
+        <value>3000000</value> <!-- 3000 MHz CPU speed -->
+    </array>
+
+    <!-- Additional power used by a CPU from cluster 0 when running at different
+         speeds. Currently this measurement also includes cluster cost. -->
+    <array name="cpu.core_power.cluster0">
+        <value>10</value> <!-- 300 MHz CPU speed -->
+        <value>20</value> <!-- 1000 MHz CPU speed -->
+        <value>30</value> <!-- 1900 MHz CPU speed -->
+    </array>
+    <!-- Additional power used by a CPU from cluster 1 when running at different
+         speeds. Currently this measurement also includes cluster cost. -->
+    <array name="cpu.core_power.cluster1">
+        <value>25</value> <!-- 300 MHz CPU speed -->
+        <value>35</value> <!-- 1000 MHz CPU speed -->
+        <value>50</value> <!-- 2500 MHz CPU speed -->
+        <value>60</value> <!-- 3000 MHz CPU speed -->
+    </array>
+
+    <!-- Additional power used when screen is turned on at minimum brightness -->
+    <item name="screen.on">100</item>
+    <!-- Additional power used when screen is at maximum brightness, compared to
+         screen at minimum brightness -->
+    <item name="screen.full">800</item>
+
+    <!-- Average power used by the camera flash module when on -->
+    <item name="camera.flashlight">500</item>
+    <!-- Average power use by the camera subsystem for a typical camera
+         application. Intended as a rough estimate for an application running a
+         preview and capturing approximately 10 full-resolution pictures per
+         minute. -->
+    <item name="camera.avg">600</item>
+
+    <!-- Additional power used when audio decoding/encoding via DSP -->
+    <item name="dsp.audio">100</item>
+
+    <!-- Additional power used when GPS is acquiring a signal -->
+    <item name="gps.on">10</item>
+
+    <!-- Additional power used when cellular radio is transmitting/receiving -->
+    <item name="radio.active">60</item>
+    <!-- Additional power used when cellular radio is paging the tower -->
+    <item name="radio.scanning">3</item>
+    <!-- Additional power used when the cellular radio is on. Multi-value entry,
+         one per signal strength (no signal, weak, moderate, strong) -->
+    <array name="radio.on"> <!-- Strength 0 to BINS-1 -->
+        <value>6</value>       <!-- none -->
+        <value>5</value>       <!-- poor -->
+        <value>4</value>       <!-- moderate -->
+        <value>3</value>       <!-- good -->
+        <value>3</value>       <!-- great -->
+    </array>
+</device>
diff --git a/core/tests/coretests/src/android/content/pm/dex/DexMetadataHelperTest.java b/core/tests/coretests/src/android/content/pm/dex/DexMetadataHelperTest.java
index 4b84429..584257b 100644
--- a/core/tests/coretests/src/android/content/pm/dex/DexMetadataHelperTest.java
+++ b/core/tests/coretests/src/android/content/pm/dex/DexMetadataHelperTest.java
@@ -24,7 +24,9 @@
 import android.content.Context;
 import android.content.pm.PackageManager;
 import android.content.pm.PackageParser;
+import android.content.pm.PackageParser.ApkLite;
 import android.content.pm.PackageParser.Package;
+import android.content.pm.PackageParser.PackageLite;
 import android.content.pm.PackageParser.PackageParserException;
 import android.os.FileUtils;
 import android.support.test.InstrumentationRegistry;
@@ -34,6 +36,7 @@
 import com.android.frameworks.coretests.R;
 
 import java.io.File;
+import java.io.FileInputStream;
 import java.io.FileOutputStream;
 import java.io.IOException;
 import java.io.InputStream;
@@ -45,6 +48,7 @@
 import libcore.io.IoUtils;
 
 import org.junit.After;
+import org.junit.Assert;
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -201,6 +205,31 @@
         }
     }
 
+    @Test
+    public void testPackageSizeWithDmFile()
+            throws IOException, PackageParserException {
+        copyApkToToTmpDir("install_split_base.apk", R.raw.install_split_base);
+        File dm = createDexMetadataFile("install_split_base.apk");
+        PackageParser.PackageLite pkg = new PackageParser().parsePackageLite(mTmpDir,
+                0 /* flags */);
+
+        Assert.assertEquals(dm.length(), DexMetadataHelper.getPackageDexMetadataSize(pkg));
+    }
+
+    // This simulates the 'adb shell pm install' flow.
+    @Test
+    public void testPackageSizeWithPartialPackageLite() throws IOException, PackageParserException {
+        File base = copyApkToToTmpDir("install_split_base", R.raw.install_split_base);
+        File dm = createDexMetadataFile("install_split_base.apk");
+        try (FileInputStream is = new FileInputStream(base)) {
+            ApkLite baseApk = PackageParser.parseApkLite(is.getFD(), base.getAbsolutePath(), 0);
+            PackageLite pkgLite = new PackageLite(null, baseApk, null, null, null, null,
+                    null, null);
+            Assert.assertEquals(dm.length(), DexMetadataHelper.getPackageDexMetadataSize(pkgLite));
+        }
+
+    }
+
     private static boolean isDexMetadataForApk(String dmaPath, String apkPath) {
         return apkPath.substring(0, apkPath.length() - APK_FILE_EXTENSION.length()).equals(
                 dmaPath.substring(0, dmaPath.length() - DEX_METADATA_FILE_EXTENSION.length()));
diff --git a/core/tests/coretests/src/android/provider/SettingsBackupTest.java b/core/tests/coretests/src/android/provider/SettingsBackupTest.java
index 8da7ced..7403c26 100644
--- a/core/tests/coretests/src/android/provider/SettingsBackupTest.java
+++ b/core/tests/coretests/src/android/provider/SettingsBackupTest.java
@@ -213,6 +213,7 @@
                     Settings.Global.FANCY_IME_ANIMATIONS,
                     Settings.Global.FORCE_ALLOW_ON_EXTERNAL,
                     Settings.Global.FORCED_APP_STANDBY_ENABLED,
+                    Settings.Global.FORCED_APP_STANDBY_FOR_SMALL_BATTERY_ENABLED,
                     Settings.Global.FSTRIM_MANDATORY_INTERVAL,
                     Settings.Global.GLOBAL_HTTP_PROXY_EXCLUSION_LIST,
                     Settings.Global.GLOBAL_HTTP_PROXY_HOST,
@@ -353,6 +354,7 @@
                     Settings.Global.SYS_STORAGE_THRESHOLD_MAX_BYTES,
                     Settings.Global.SYS_STORAGE_THRESHOLD_PERCENTAGE,
                     Settings.Global.SYS_VDSO,
+                    Settings.Global.FPS_DEVISOR,
                     Settings.Global.TCP_DEFAULT_INIT_RWND,
                     Settings.Global.TETHER_DUN_APN,
                     Settings.Global.TETHER_DUN_REQUIRED,
@@ -389,6 +391,7 @@
                     Settings.Global.WFC_IMS_ROAMING_MODE,
                     Settings.Global.WIFI_BADGING_THRESHOLDS,
                     Settings.Global.WIFI_BOUNCE_DELAY_OVERRIDE_MS,
+                    Settings.Global.WIFI_CONNECTED_MAC_RANDOMIZATION_ENABLED,
                     Settings.Global.WIFI_COUNTRY_CODE,
                     Settings.Global.WIFI_DEVICE_OWNER_CONFIGS_LOCKDOWN,
                     Settings.Global.WIFI_DISPLAY_CERTIFICATION_ON,
@@ -533,7 +536,9 @@
                  Settings.Secure.VOICE_RECOGNITION_SERVICE,
                  Settings.Secure.INSTANT_APPS_ENABLED,
                  Settings.Secure.BACKUP_MANAGER_CONSTANTS,
-                 Settings.Secure.KEYGUARD_SLICE_URI);
+                 Settings.Secure.KEYGUARD_SLICE_URI,
+                 Settings.Secure.PARENTAL_CONTROL_ENABLED,
+                 Settings.Secure.PARENTAL_CONTROL_REDIRECT_URL);
 
     @Test
     public void systemSettingsBackedUpOrBlacklisted() {
diff --git a/core/tests/coretests/src/android/provider/SettingsValidatorsTest.java b/core/tests/coretests/src/android/provider/SettingsValidatorsTest.java
index 00732b0..4c4aeaf 100644
--- a/core/tests/coretests/src/android/provider/SettingsValidatorsTest.java
+++ b/core/tests/coretests/src/android/provider/SettingsValidatorsTest.java
@@ -36,20 +36,28 @@
 
     @Test
     public void ensureAllBackedUpSystemSettingsHaveValidators() {
-        String offenders = getOffenders(Settings.System.SETTINGS_TO_BACKUP,
-                Settings.System.VALIDATORS);
+        String offenders = getOffenders(concat(Settings.System.SETTINGS_TO_BACKUP,
+                Settings.System.LEGACY_RESTORE_SETTINGS), Settings.System.VALIDATORS);
 
         failIfOffendersPresent(offenders, "Settings.System");
     }
 
     @Test
     public void ensureAllBackedUpGlobalSettingsHaveValidators() {
-        String offenders = getOffenders(Settings.Global.SETTINGS_TO_BACKUP,
-                Settings.Global.VALIDATORS);
+        String offenders = getOffenders(concat(Settings.Global.SETTINGS_TO_BACKUP,
+                Settings.Global.LEGACY_RESTORE_SETTINGS), Settings.Global.VALIDATORS);
 
         failIfOffendersPresent(offenders, "Settings.Global");
     }
 
+    @Test
+    public void ensureAllBackedUpSecureSettingsHaveValidators() {
+        String offenders = getOffenders(concat(Settings.Secure.SETTINGS_TO_BACKUP,
+                Settings.Secure.LEGACY_RESTORE_SETTINGS), Settings.Secure.VALIDATORS);
+
+        failIfOffendersPresent(offenders, "Settings.Secure");
+    }
+
     private void failIfOffendersPresent(String offenders, String settingsType) {
         if (offenders.length() > 0) {
             fail("All " + settingsType + " settings that are backed up have to have a non-null"
@@ -66,4 +74,16 @@
         }
         return offenders.toString();
     }
+
+    private String[] concat(String[] first, String[] second) {
+        if (second == null || second.length == 0) {
+            return first;
+        }
+        final int firstLen = first.length;
+        final int secondLen = second.length;
+        String[] both = new String[firstLen + secondLen];
+        System.arraycopy(first, 0, both, 0, firstLen);
+        System.arraycopy(second, 0, both, firstLen, secondLen);
+        return both;
+    }
 }
diff --git a/core/tests/coretests/src/com/android/internal/os/BatteryStatsCpuTimesTest.java b/core/tests/coretests/src/com/android/internal/os/BatteryStatsCpuTimesTest.java
index b5a7bec..32053e3 100644
--- a/core/tests/coretests/src/com/android/internal/os/BatteryStatsCpuTimesTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/BatteryStatsCpuTimesTest.java
@@ -62,9 +62,9 @@
  *
  * Build: m FrameworksCoreTests
  * Install: adb install -r \
- *     ${ANDROID_PRODUCT_OUT}/data/app/FrameworksCoreTests/FrameworksCoreTests.apk
+ * ${ANDROID_PRODUCT_OUT}/data/app/FrameworksCoreTests/FrameworksCoreTests.apk
  * Run: adb shell am instrument -e class com.android.internal.os.BatteryStatsCpuTimesTest -w \
- *     com.android.frameworks.coretests/android.support.test.runner.AndroidJUnitRunner
+ * com.android.frameworks.coretests/android.support.test.runner.AndroidJUnitRunner
  *
  * or
  *
@@ -73,10 +73,18 @@
 @SmallTest
 @RunWith(AndroidJUnit4.class)
 public class BatteryStatsCpuTimesTest {
-    @Mock KernelUidCpuTimeReader mKernelUidCpuTimeReader;
-    @Mock KernelUidCpuFreqTimeReader mKernelUidCpuFreqTimeReader;
-    @Mock BatteryStatsImpl.UserInfoProvider mUserInfoProvider;
-    @Mock PowerProfile mPowerProfile;
+    @Mock
+    KernelUidCpuTimeReader mKernelUidCpuTimeReader;
+    @Mock
+    KernelUidCpuFreqTimeReader mKernelUidCpuFreqTimeReader;
+    @Mock
+    KernelUidCpuActiveTimeReader mKernelUidCpuActiveTimeReader;
+    @Mock
+    KernelUidCpuClusterTimeReader mKernelUidCpuClusterTimeReader;
+    @Mock
+    BatteryStatsImpl.UserInfoProvider mUserInfoProvider;
+    @Mock
+    PowerProfile mPowerProfile;
 
     private MockClocks mClocks;
     private MockBatteryStatsImpl mBatteryStatsImpl;
@@ -90,6 +98,8 @@
         mBatteryStatsImpl = new MockBatteryStatsImpl(mClocks)
                 .setKernelUidCpuTimeReader(mKernelUidCpuTimeReader)
                 .setKernelUidCpuFreqTimeReader(mKernelUidCpuFreqTimeReader)
+                .setKernelUidCpuActiveTimeReader(mKernelUidCpuActiveTimeReader)
+                .setKernelUidCpuClusterTimeReader(mKernelUidCpuClusterTimeReader)
                 .setUserInfoProvider(mUserInfoProvider);
     }
 
@@ -134,6 +144,10 @@
         verify(mKernelUidCpuFreqTimeReader, times(2)).perClusterTimesAvailable();
         verify(mKernelUidCpuFreqTimeReader).readDelta(
                 any(KernelUidCpuFreqTimeReader.Callback.class));
+        verify(mKernelUidCpuActiveTimeReader).readDelta(
+                any(KernelUidCpuActiveTimeReader.Callback.class));
+        verify(mKernelUidCpuClusterTimeReader).readDelta(
+                any(KernelUidCpuClusterTimeReader.Callback.class));
         verifyNoMoreInteractions(mKernelUidCpuFreqTimeReader);
         for (int i = 0; i < numClusters; ++i) {
             verify(mKernelCpuSpeedReaders[i]).readDelta();
@@ -228,7 +242,7 @@
         updateTimeBasesLocked(true, Display.STATE_ON, 0, 0);
         final int testUserId = 11;
         when(mUserInfoProvider.exists(testUserId)).thenReturn(true);
-        final int[] testUids = getUids(testUserId, new int[] {
+        final int[] testUids = getUids(testUserId, new int[]{
                 FIRST_APPLICATION_UID + 22,
                 FIRST_APPLICATION_UID + 27,
                 FIRST_APPLICATION_UID + 33
@@ -301,7 +315,7 @@
         when(mUserInfoProvider.exists(testUserId)).thenReturn(true);
         final int isolatedAppId = FIRST_ISOLATED_UID + 27;
         final int isolatedUid = UserHandle.getUid(testUserId, isolatedAppId);
-        final int[] testUids = getUids(testUserId, new int[] {
+        final int[] testUids = getUids(testUserId, new int[]{
                 FIRST_APPLICATION_UID + 22,
                 isolatedAppId,
                 FIRST_APPLICATION_UID + 33
@@ -389,7 +403,7 @@
         final int invalidUid = UserHandle.getUid(invalidUserId, FIRST_APPLICATION_UID + 99);
         when(mUserInfoProvider.exists(testUserId)).thenReturn(true);
         when(mUserInfoProvider.exists(invalidUserId)).thenReturn(false);
-        final int[] testUids = getUids(testUserId, new int[] {
+        final int[] testUids = getUids(testUserId, new int[]{
                 FIRST_APPLICATION_UID + 22,
                 FIRST_APPLICATION_UID + 27,
                 FIRST_APPLICATION_UID + 33
@@ -431,7 +445,7 @@
         updateTimeBasesLocked(true, Display.STATE_ON, 0, 0);
         final int testUserId = 11;
         when(mUserInfoProvider.exists(testUserId)).thenReturn(true);
-        final int[] testUids = getUids(testUserId, new int[] {
+        final int[] testUids = getUids(testUserId, new int[]{
                 FIRST_APPLICATION_UID + 22,
                 FIRST_APPLICATION_UID + 27,
                 FIRST_APPLICATION_UID + 33
@@ -514,10 +528,10 @@
 
         final int testUserId = 11;
         when(mUserInfoProvider.exists(testUserId)).thenReturn(true);
-        final int[] testUids = getUids(testUserId, new int[] {
-            FIRST_APPLICATION_UID + 22,
-            FIRST_APPLICATION_UID + 27,
-            FIRST_APPLICATION_UID + 33
+        final int[] testUids = getUids(testUserId, new int[]{
+                FIRST_APPLICATION_UID + 22,
+                FIRST_APPLICATION_UID + 27,
+                FIRST_APPLICATION_UID + 33
         });
         final long[][] uidTimesMs = {
                 {4, 10, 5, 9, 4},
@@ -589,7 +603,7 @@
 
         final int testUserId = 11;
         when(mUserInfoProvider.exists(testUserId)).thenReturn(true);
-        final int[] testUids = getUids(testUserId, new int[] {
+        final int[] testUids = getUids(testUserId, new int[]{
                 FIRST_APPLICATION_UID + 22,
                 FIRST_APPLICATION_UID + 27,
                 FIRST_APPLICATION_UID + 33
@@ -693,7 +707,7 @@
 
         final int testUserId = 11;
         when(mUserInfoProvider.exists(testUserId)).thenReturn(true);
-        final int[] testUids = getUids(testUserId, new int[] {
+        final int[] testUids = getUids(testUserId, new int[]{
                 FIRST_APPLICATION_UID + 22,
                 FIRST_APPLICATION_UID + 27,
                 FIRST_APPLICATION_UID + 33
@@ -782,7 +796,7 @@
             }
         }
         for (int cluster = 0; cluster < clusterFreqs.length; ++cluster) {
-            for (int speed = 0 ; speed < clusterFreqs[cluster]; ++speed) {
+            for (int speed = 0; speed < clusterFreqs[cluster]; ++speed) {
                 assertEquals("There shouldn't be any left-overs: "
                                 + Arrays.deepToString(expectedWakeLockUidTimesUs),
                         0, expectedWakeLockUidTimesUs[cluster][speed]);
@@ -797,7 +811,7 @@
 
         final int testUserId = 11;
         when(mUserInfoProvider.exists(testUserId)).thenReturn(true);
-        final int[] testUids = getUids(testUserId, new int[] {
+        final int[] testUids = getUids(testUserId, new int[]{
                 FIRST_APPLICATION_UID + 22,
                 FIRST_APPLICATION_UID + 27,
                 FIRST_APPLICATION_UID + 33
@@ -874,7 +888,7 @@
         when(mUserInfoProvider.exists(testUserId)).thenReturn(true);
         final int isolatedAppId = FIRST_ISOLATED_UID + 27;
         final int isolatedUid = UserHandle.getUid(testUserId, isolatedAppId);
-        final int[] testUids = getUids(testUserId, new int[] {
+        final int[] testUids = getUids(testUserId, new int[]{
                 FIRST_APPLICATION_UID + 22,
                 isolatedAppId,
                 FIRST_APPLICATION_UID + 33
@@ -969,7 +983,7 @@
         final int invalidUid = UserHandle.getUid(invalidUserId, FIRST_APPLICATION_UID + 99);
         when(mUserInfoProvider.exists(testUserId)).thenReturn(true);
         when(mUserInfoProvider.exists(invalidUserId)).thenReturn(false);
-        final int[] testUids = getUids(testUserId, new int[] {
+        final int[] testUids = getUids(testUserId, new int[]{
                 FIRST_APPLICATION_UID + 22,
                 FIRST_APPLICATION_UID + 27,
                 FIRST_APPLICATION_UID + 33
@@ -986,7 +1000,7 @@
                 callback.onUidCpuFreqTime(testUids[i], uidTimesMs[i]);
             }
             // And one for the invalid uid
-            callback.onUidCpuFreqTime(invalidUid, new long[] {12, 839, 32, 34, 21});
+            callback.onUidCpuFreqTime(invalidUid, new long[]{12, 839, 32, 34, 21});
             return null;
         }).when(mKernelUidCpuFreqTimeReader).readDelta(
                 any(KernelUidCpuFreqTimeReader.Callback.class));
@@ -1009,6 +1023,136 @@
         verify(mKernelUidCpuFreqTimeReader).removeUid(invalidUid);
     }
 
+    @Test
+    public void testReadKernelUidCpuActiveTimesLocked() {
+        // PRECONDITIONS
+        updateTimeBasesLocked(true, Display.STATE_ON, 0, 0);
+
+        final int testUserId = 11;
+        when(mUserInfoProvider.exists(testUserId)).thenReturn(true);
+        final int[] testUids = getUids(testUserId, new int[]{
+                FIRST_APPLICATION_UID + 22,
+                FIRST_APPLICATION_UID + 27,
+                FIRST_APPLICATION_UID + 33
+        });
+        final long[] uidTimesMs = {8000, 25000, 3000, 0, 42000};
+        doAnswer(invocation -> {
+            final KernelUidCpuActiveTimeReader.Callback callback =
+                    (KernelUidCpuActiveTimeReader.Callback) invocation.getArguments()[0];
+            for (int i = 0; i < testUids.length; ++i) {
+                callback.onUidCpuActiveTime(testUids[i], uidTimesMs[i]);
+            }
+            return null;
+        }).when(mKernelUidCpuActiveTimeReader).readDelta(
+                any(KernelUidCpuActiveTimeReader.Callback.class));
+
+        // RUN
+        mBatteryStatsImpl.readKernelUidCpuActiveTimesLocked();
+
+        // VERIFY
+        for (int i = 0; i < testUids.length; ++i) {
+            final BatteryStats.Uid u = mBatteryStatsImpl.getUidStats().get(testUids[i]);
+            assertNotNull("No entry for uid=" + testUids[i], u);
+            assertEquals("Unexpected cpu active time for uid=" + testUids[i], uidTimesMs[i],
+                    u.getCpuActiveTime());
+        }
+
+        // Repeat the test when the screen is off.
+
+        // PRECONDITIONS
+        updateTimeBasesLocked(true, Display.STATE_OFF, 0, 0);
+        final long[] deltasMs = {43000, 3345000, 2143000, 123000, 4554000};
+        doAnswer(invocation -> {
+            final KernelUidCpuActiveTimeReader.Callback callback =
+                    (KernelUidCpuActiveTimeReader.Callback) invocation.getArguments()[0];
+            for (int i = 0; i < testUids.length; ++i) {
+                callback.onUidCpuActiveTime(testUids[i], deltasMs[i]);
+            }
+            return null;
+        }).when(mKernelUidCpuActiveTimeReader).readDelta(
+                any(KernelUidCpuActiveTimeReader.Callback.class));
+
+        // RUN
+        mBatteryStatsImpl.readKernelUidCpuActiveTimesLocked();
+
+        // VERIFY
+        for (int i = 0; i < testUids.length; ++i) {
+            final BatteryStats.Uid u = mBatteryStatsImpl.getUidStats().get(testUids[i]);
+            assertNotNull("No entry for uid=" + testUids[i], u);
+            assertEquals("Unexpected cpu active time for uid=" + testUids[i],
+                    uidTimesMs[i] + deltasMs[i], u.getCpuActiveTime());
+        }
+    }
+
+    @Test
+    public void testReadKernelUidCpuClusterTimesLocked() {
+        // PRECONDITIONS
+        updateTimeBasesLocked(true, Display.STATE_ON, 0, 0);
+
+        final int testUserId = 11;
+        when(mUserInfoProvider.exists(testUserId)).thenReturn(true);
+        final int[] testUids = getUids(testUserId, new int[]{
+                FIRST_APPLICATION_UID + 22,
+                FIRST_APPLICATION_UID + 27,
+                FIRST_APPLICATION_UID + 33
+        });
+        final long[][] uidTimesMs = {
+                {4000, 10000},
+                {5000, 1000},
+                {8000, 0}
+        };
+        doAnswer(invocation -> {
+            final KernelUidCpuClusterTimeReader.Callback callback =
+                    (KernelUidCpuClusterTimeReader.Callback) invocation.getArguments()[0];
+            for (int i = 0; i < testUids.length; ++i) {
+                callback.onUidCpuPolicyTime(testUids[i], uidTimesMs[i]);
+            }
+            return null;
+        }).when(mKernelUidCpuClusterTimeReader).readDelta(
+                any(KernelUidCpuClusterTimeReader.Callback.class));
+
+        // RUN
+        mBatteryStatsImpl.readKernelUidCpuClusterTimesLocked();
+
+        // VERIFY
+        for (int i = 0; i < testUids.length; ++i) {
+            final BatteryStats.Uid u = mBatteryStatsImpl.getUidStats().get(testUids[i]);
+            assertNotNull("No entry for uid=" + testUids[i], u);
+            assertArrayEquals("Unexpected cpu cluster time for uid=" + testUids[i], uidTimesMs[i],
+                    u.getCpuClusterTimes());
+        }
+
+        // Repeat the test when the screen is off.
+
+        // PRECONDITIONS
+        updateTimeBasesLocked(true, Display.STATE_OFF, 0, 0);
+        final long[][] deltasMs = {
+                {3000, 12000},
+                {3248327490475L, 0},
+                {43000, 3345000}
+        };
+        doAnswer(invocation -> {
+            final KernelUidCpuClusterTimeReader.Callback callback =
+                    (KernelUidCpuClusterTimeReader.Callback) invocation.getArguments()[0];
+            for (int i = 0; i < testUids.length; ++i) {
+                callback.onUidCpuPolicyTime(testUids[i], deltasMs[i]);
+            }
+            return null;
+        }).when(mKernelUidCpuClusterTimeReader).readDelta(
+                any(KernelUidCpuClusterTimeReader.Callback.class));
+
+        // RUN
+        mBatteryStatsImpl.readKernelUidCpuClusterTimesLocked();
+
+        // VERIFY
+        for (int i = 0; i < testUids.length; ++i) {
+            final BatteryStats.Uid u = mBatteryStatsImpl.getUidStats().get(testUids[i]);
+            assertNotNull("No entry for uid=" + testUids[i], u);
+            assertArrayEquals("Unexpected cpu cluster time for uid=" + testUids[i], sum(uidTimesMs[i], deltasMs[i]),
+                    u.getCpuClusterTimes());
+        }
+    }
+
     private void updateTimeBasesLocked(boolean unplugged, int screenState,
             long upTime, long realTime) {
         // Set PowerProfile=null before calling updateTimeBasesLocked to avoid execution of
diff --git a/core/tests/coretests/src/com/android/internal/os/BatteryStatsTests.java b/core/tests/coretests/src/com/android/internal/os/BatteryStatsTests.java
index e8f2456..702f4b8 100644
--- a/core/tests/coretests/src/com/android/internal/os/BatteryStatsTests.java
+++ b/core/tests/coretests/src/com/android/internal/os/BatteryStatsTests.java
@@ -39,8 +39,11 @@
         KernelMemoryBandwidthStatsTest.class,
         KernelSingleUidTimeReaderTest.class,
         KernelUidCpuFreqTimeReaderTest.class,
+        KernelUidCpuActiveTimeReaderTest.class,
+        KernelUidCpuClusterTimeReaderTest.class,
         KernelWakelockReaderTest.class,
-        LongSamplingCounterArrayTest.class
+        LongSamplingCounterArrayTest.class,
+        PowerProfileTest.class
     })
 public class BatteryStatsTests {
 }
diff --git a/core/tests/coretests/src/com/android/internal/os/KernelUidCpuActiveTimeReaderTest.java b/core/tests/coretests/src/com/android/internal/os/KernelUidCpuActiveTimeReaderTest.java
new file mode 100644
index 0000000..1ac82bd
--- /dev/null
+++ b/core/tests/coretests/src/com/android/internal/os/KernelUidCpuActiveTimeReaderTest.java
@@ -0,0 +1,240 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.os;
+
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
+import static org.mockito.Mockito.when;
+
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.MockitoAnnotations;
+
+import java.io.BufferedReader;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Random;
+
+/**
+ * Test class for {@link KernelUidCpuActiveTimeReader}.
+ *
+ * To run it:
+ * bit FrameworksCoreTests:com.android.internal.os.KernelUidCpuActiveTimeReaderTest
+ *
+ */
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class KernelUidCpuActiveTimeReaderTest {
+    @Mock private BufferedReader mBufferedReader;
+    @Mock private KernelUidCpuActiveTimeReader.Callback mCallback;
+
+    private KernelUidCpuActiveTimeReader mReader;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+        mReader = new KernelUidCpuActiveTimeReader();
+    }
+
+    public class Temp {
+
+        public void method() {
+            method1(new long[][]{{1,2,3}, {2,3,4}});
+            method1(new long[][]{{2,2,3}, {2,3,4}});
+        }
+        public int method1(long[][] array) {
+            return array.length * array[0].length;
+        }
+    }
+
+    @Test
+    public void testReadDelta() throws Exception {
+        final int cores = 8;
+        final String info = "active: 8";
+        final int[] uids = {1, 22, 333, 4444, 5555};
+
+        final long[][] times = increaseTime(new long[uids.length][cores]);
+        when(mBufferedReader.readLine()).thenReturn(info, formatTime(uids, times));
+        mReader.readDeltaInternal(mBufferedReader, mCallback);
+        for(int i=0;i<uids.length;i++){
+            verify(mCallback).onUidCpuActiveTime(uids[i], getTotal(times[i]));
+        }
+        verifyNoMoreInteractions(mCallback);
+
+        // Verify that a second call will only return deltas.
+        Mockito.reset(mCallback, mBufferedReader);
+        final long[][] times1 = increaseTime(times);
+        when(mBufferedReader.readLine()).thenReturn(info, formatTime(uids, times1));
+        mReader.readDeltaInternal(mBufferedReader, mCallback);
+        for(int i=0;i<uids.length;i++){
+            verify(mCallback).onUidCpuActiveTime(uids[i], getTotal(subtract(times1[i], times[i])));
+        }
+        verifyNoMoreInteractions(mCallback);
+
+        // Verify that there won't be a callback if the proc file values didn't change.
+        Mockito.reset(mCallback, mBufferedReader);
+        when(mBufferedReader.readLine()).thenReturn(info, formatTime(uids, times1));
+        mReader.readDeltaInternal(mBufferedReader, mCallback);
+        verifyNoMoreInteractions(mCallback);
+
+        // Verify that calling with a null callback doesn't result in any crashes
+        Mockito.reset(mCallback, mBufferedReader);
+        final long[][] times2 = increaseTime(times1);
+        when(mBufferedReader.readLine()).thenReturn(info, formatTime(uids, times2));
+        mReader.readDeltaInternal(mBufferedReader, null);
+
+        // Verify that the readDelta call will only return deltas when
+        // the previous call had null callback.
+        Mockito.reset(mCallback, mBufferedReader);
+        final long[][] times3 = increaseTime(times2);
+        when(mBufferedReader.readLine()).thenReturn(info, formatTime(uids, times3));
+        mReader.readDeltaInternal(mBufferedReader, mCallback);
+        for (int i = 0; i < uids.length; ++i) {
+            verify(mCallback).onUidCpuActiveTime(uids[i], getTotal(subtract(times3[i], times2[i])));
+        }
+        verifyNoMoreInteractions(mCallback);
+    }
+
+    @Test
+    public void testReadDelta_malformedData() throws Exception {
+        final int cores = 8;
+        final String info = "active: 8";
+        final int[] uids = {1, 22, 333, 4444, 5555};
+        final long[][] times = increaseTime(new long[uids.length][cores]);
+        when(mBufferedReader.readLine()).thenReturn(info, formatTime(uids, times));
+        mReader.readDeltaInternal(mBufferedReader, mCallback);
+        for(int i=0;i<uids.length;i++){
+            verify(mCallback).onUidCpuActiveTime(uids[i], getTotal(times[i]));
+        }
+        verifyNoMoreInteractions(mCallback);
+
+        // Verify that there is no callback if subsequent call provides wrong # of entries.
+        Mockito.reset(mCallback, mBufferedReader);
+        final long[][] temp = increaseTime(times);
+        final long[][] times1 = new long[uids.length][];
+        for(int i=0;i<temp.length;i++){
+            times1[i] = Arrays.copyOfRange(temp[i], 0, 6);
+        }
+        when(mBufferedReader.readLine()).thenReturn(info, formatTime(uids, times1));
+        mReader.readDeltaInternal(mBufferedReader, mCallback);
+        verifyNoMoreInteractions(mCallback);
+
+        // Verify that the internal state was not modified if the given core count does not match
+        // the following # of entries.
+        Mockito.reset(mCallback, mBufferedReader);
+        final long[][] times2 = increaseTime(times);
+        when(mBufferedReader.readLine()).thenReturn(info, formatTime(uids, times2));
+        mReader.readDeltaInternal(mBufferedReader, mCallback);
+        for(int i=0;i<uids.length;i++){
+            verify(mCallback).onUidCpuActiveTime(uids[i], getTotal(subtract(times2[i], times[i])));
+        }
+        verifyNoMoreInteractions(mCallback);
+
+        // Verify that there is no callback if any value in the proc file is -ve.
+        Mockito.reset(mCallback, mBufferedReader);
+        final long[][] times3 = increaseTime(times2);
+        times3[uids.length - 1][cores - 1] *= -1;
+        when(mBufferedReader.readLine()).thenReturn(info, formatTime(uids, times3));
+        mReader.readDeltaInternal(mBufferedReader, mCallback);
+        for (int i = 0; i < uids.length - 1; ++i) {
+            verify(mCallback).onUidCpuActiveTime(uids[i], getTotal(subtract(times3[i], times2[i])));
+        }
+        verifyNoMoreInteractions(mCallback);
+
+        // Verify that the internal state was not modified when the proc file had -ve value.
+        Mockito.reset(mCallback, mBufferedReader);
+        for (int i = 0; i < cores; i++) {
+            times3[uids.length - 1][i] = times2[uids.length - 1][i] + uids[uids.length - 1] * 1000;
+        }
+        when(mBufferedReader.readLine()).thenReturn(info, formatTime(uids, times3));
+        mReader.readDeltaInternal(mBufferedReader, mCallback);
+        verify(mCallback).onUidCpuActiveTime(uids[uids.length - 1], getTotal(subtract(times3[uids.length - 1], times2[uids.length - 1])));
+        verifyNoMoreInteractions(mCallback);
+
+        // Verify that there is no callback if the values in the proc file are decreased.
+        Mockito.reset(mCallback, mBufferedReader);
+        final long[][] times4 = increaseTime(times3);
+        times4[uids.length - 1][cores - 1] = times3[uids.length - 1][cores - 1] - 1;
+        when(mBufferedReader.readLine()).thenReturn(info, formatTime(uids, times4));
+        mReader.readDeltaInternal(mBufferedReader, mCallback);
+        for (int i = 0; i < uids.length - 1; ++i) {
+            verify(mCallback).onUidCpuActiveTime(uids[i], getTotal(subtract(times4[i], times3[i])));
+        }
+        verifyNoMoreInteractions(mCallback);
+
+        // Verify that the internal state was not modified when the proc file had decreased values.
+        Mockito.reset(mCallback, mBufferedReader);
+        for (int i = 0; i < cores; i++) {
+            times4[uids.length - 1][i] = times3[uids.length - 1][i] + uids[uids.length - 1] * 1000;
+        }
+        when(mBufferedReader.readLine()).thenReturn(info, formatTime(uids, times4));
+        mReader.readDeltaInternal(mBufferedReader, mCallback);
+        verify(mCallback).onUidCpuActiveTime(uids[uids.length - 1], getTotal(subtract(times4[uids.length - 1], times3[uids.length - 1])));
+        verifyNoMoreInteractions(mCallback);
+    }
+
+    private long[] subtract(long[] a1, long[] a2) {
+        long[] val = new long[a1.length];
+        for (int i = 0; i < val.length; ++i) {
+            val[i] = a1[i] - a2[i];
+        }
+        return val;
+    }
+
+    private String[] formatTime(int[] uids, long[][] times) {
+        String[] lines = new String[uids.length + 1];
+        for (int i=0;i<uids.length;i++){
+            StringBuilder sb = new StringBuilder();
+            sb.append(uids[i]).append(':');
+            for(int j=0;j<times[i].length;j++){
+                sb.append(' ').append(times[i][j]);
+            }
+            lines[i] = sb.toString();
+        }
+        lines[uids.length] = null;
+        return lines;
+    }
+
+    private long[][] increaseTime(long[][] original) {
+        long[][] newTime = new long[original.length][original[0].length];
+        Random rand = new Random();
+        for(int i = 0;i<original.length;i++){
+            for(int j=0;j<original[0].length;j++){
+                newTime[i][j] = original[i][j] + rand.nextInt(1000_000) + 10000;
+            }
+        }
+        return newTime;
+    }
+
+    private long getTotal(long[] times) {
+        long sum = 0;
+        for(int i=0;i<times.length;i++){
+            sum+=times[i] * 10 / (i+1);
+        }
+        return sum;
+    }
+}
diff --git a/core/tests/coretests/src/com/android/internal/os/KernelUidCpuClusterTimeReaderTest.java b/core/tests/coretests/src/com/android/internal/os/KernelUidCpuClusterTimeReaderTest.java
new file mode 100644
index 0000000..0d1f852
--- /dev/null
+++ b/core/tests/coretests/src/com/android/internal/os/KernelUidCpuClusterTimeReaderTest.java
@@ -0,0 +1,245 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.os;
+
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
+import static org.mockito.Mockito.when;
+
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import static org.junit.Assert.*;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.MockitoAnnotations;
+
+import java.io.BufferedReader;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Random;
+
+/**
+ * Test class for {@link KernelUidCpuClusterTimeReader}.
+ *
+ * To run it:
+ * bit FrameworksCoreTests:com.android.internal.os.KernelUidCpuClusterTimeReaderTest
+ *
+ */
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class KernelUidCpuClusterTimeReaderTest {
+    @Mock private BufferedReader mBufferedReader;
+    @Mock private KernelUidCpuClusterTimeReader.Callback mCallback;
+
+    private KernelUidCpuClusterTimeReader mReader;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+        mReader = new KernelUidCpuClusterTimeReader();
+    }
+
+    @Test
+    public void testReadDelta() throws Exception {
+        final String info = "policy0: 2 policy4: 4";
+        final int cores = 6;
+        final int[] cluster = {2, 4};
+        final int[] uids = {1, 22, 333, 4444, 5555};
+
+        // Verify initial call
+        final long[][] times = increaseTime(new long[uids.length][cores]);
+        when(mBufferedReader.readLine()).thenReturn(info, formatTime(uids, times));
+        mReader.readDeltaInternal(mBufferedReader, mCallback);
+        for (int i=0;i<uids.length;i++){
+            verify(mCallback).onUidCpuPolicyTime(uids[i], getTotal(cluster, times[i]));
+        }
+
+        // Verify that a second call will only return deltas.
+        Mockito.reset(mCallback, mBufferedReader);
+        final long[][] times1 = increaseTime(times);
+        when(mBufferedReader.readLine()).thenReturn(info, formatTime(uids, times1));
+        mReader.readDeltaInternal(mBufferedReader, mCallback);
+        for (int i=0;i<uids.length;i++){
+            verify(mCallback).onUidCpuPolicyTime(uids[i], getTotal(cluster, subtract(times1[i], times[i])));
+        }
+
+        // Verify that there won't be a callback if the proc file values didn't change.
+        Mockito.reset(mCallback, mBufferedReader);
+        when(mBufferedReader.readLine()).thenReturn(info, formatTime(uids, times1));
+        mReader.readDeltaInternal(mBufferedReader, mCallback);
+        verifyNoMoreInteractions(mCallback);
+
+        // Verify that calling with a null callback doesn't result in any crashes
+        Mockito.reset(mCallback, mBufferedReader);
+        final long[][] times2 = increaseTime(times1);
+        when(mBufferedReader.readLine()).thenReturn(info, formatTime(uids, times2));
+        mReader.readDeltaInternal(mBufferedReader, null);
+
+        // Verify that the readDelta call will only return deltas when
+        // the previous call had null callback.
+        Mockito.reset(mCallback, mBufferedReader);
+        final long[][] times3 = increaseTime(times2);
+        when(mBufferedReader.readLine()).thenReturn(info, formatTime(uids, times3));
+        mReader.readDeltaInternal(mBufferedReader, mCallback);
+        for (int i=0;i<uids.length;i++){
+            verify(mCallback).onUidCpuPolicyTime(uids[i], getTotal(cluster, subtract(times3[i], times2[i])));
+        }
+
+    }
+
+    @Test
+    public void testReadDelta_malformedData() throws Exception {
+        final String info = "policy0: 2 policy4: 4";
+        final int cores = 6;
+        final int[] cluster = {2, 4};
+        final int[] uids = {1, 22, 333, 4444, 5555};
+
+        // Verify initial call
+        final long[][] times = increaseTime(new long[uids.length][cores]);
+        when(mBufferedReader.readLine()).thenReturn(info, formatTime(uids, times));
+        mReader.readDeltaInternal(mBufferedReader, mCallback);
+        for (int i=0;i<uids.length;i++){
+            verify(mCallback).onUidCpuPolicyTime(uids[i], getTotal(cluster, times[i]));
+        }
+
+        // Verify that there is no callback if subsequent call provides wrong # of entries.
+        Mockito.reset(mCallback, mBufferedReader);
+        final long[][] temp = increaseTime(times);
+        final long[][] times1 = new long[uids.length][];
+        for(int i=0;i<temp.length;i++){
+            times1[i] = Arrays.copyOfRange(temp[i], 0, 4);
+        }
+        when(mBufferedReader.readLine()).thenReturn(info, formatTime(uids, times1));
+        mReader.readDeltaInternal(mBufferedReader, mCallback);
+        verifyNoMoreInteractions(mCallback);
+
+        // Verify that the internal state was not modified if the given core count does not match
+        // the following # of entries.
+        Mockito.reset(mCallback, mBufferedReader);
+        final long[][] times2 = increaseTime(times);
+        when(mBufferedReader.readLine()).thenReturn(info, formatTime(uids, times2));
+        mReader.readDeltaInternal(mBufferedReader, mCallback);
+        for (int i=0;i<uids.length;i++){
+            verify(mCallback).onUidCpuPolicyTime(uids[i], getTotal(cluster, subtract(times2[i], times[i])));
+        }
+
+        // Verify that there is no callback if any value in the proc file is -ve.
+        Mockito.reset(mCallback, mBufferedReader);
+        final long[][] times3 = increaseTime(times2);
+        times3[uids.length - 1][cores - 1] *= -1;
+        when(mBufferedReader.readLine()).thenReturn(info, formatTime(uids, times3));
+        mReader.readDeltaInternal(mBufferedReader, mCallback);
+        for (int i=0;i<uids.length-1;i++){
+            verify(mCallback).onUidCpuPolicyTime(uids[i], getTotal(cluster, subtract(times3[i], times2[i])));
+        }
+        verifyNoMoreInteractions(mCallback);
+
+        // Verify that the internal state was not modified when the proc file had -ve value.
+        Mockito.reset(mCallback, mBufferedReader);
+        for(int i=0;i<cores;i++){
+            times3[uids.length -1][i] = times2[uids.length -1][i] + uids[uids.length -1]*1000;
+        }
+        when(mBufferedReader.readLine()).thenReturn(info, formatTime(uids, times3));
+        mReader.readDeltaInternal(mBufferedReader, mCallback);
+        verify(mCallback, times(1)).onUidCpuPolicyTime(uids[uids.length-1], getTotal(cluster, subtract(times3[uids.length -1], times2[uids.length -1])));
+
+        // // Verify that there is no callback if the values in the proc file are decreased.
+        Mockito.reset(mCallback, mBufferedReader);
+        final long[][] times4 = increaseTime(times3);
+        times4[uids.length - 1][cores - 1] = times3[uids.length - 1][cores - 1] - 1;
+        when(mBufferedReader.readLine()).thenReturn(info, formatTime(uids, times4));
+        mReader.readDeltaInternal(mBufferedReader, mCallback);
+        for (int i=0;i<uids.length-1;i++){
+            verify(mCallback).onUidCpuPolicyTime(uids[i], getTotal(cluster, subtract(times4[i], times3[i])));
+        }
+        verifyNoMoreInteractions(mCallback);
+
+        // Verify that the internal state was not modified when the proc file had decreased values.
+        Mockito.reset(mCallback, mBufferedReader);
+        for(int i=0;i<cores;i++){
+            times4[uids.length -1][i] = times3[uids.length -1][i] + uids[uids.length -1]*1000;
+        }
+        when(mBufferedReader.readLine()).thenReturn(info, formatTime(uids, times4));
+        mReader.readDeltaInternal(mBufferedReader, mCallback);
+        verify(mCallback, times(1))
+                .onUidCpuPolicyTime(uids[uids.length-1], getTotal(cluster, subtract(times3[uids.length -1], times2[uids.length -1])));
+
+    }
+
+
+    private long[] subtract(long[] a1, long[] a2) {
+        long[] val = new long[a1.length];
+        for (int i = 0; i < val.length; ++i) {
+            val[i] = a1[i] - a2[i];
+        }
+        return val;
+    }
+
+    private String[] formatTime(int[] uids, long[][] times) {
+        String[] lines = new String[uids.length + 1];
+        for (int i=0;i<uids.length;i++){
+            StringBuilder sb = new StringBuilder();
+            sb.append(uids[i]).append(':');
+            for(int j=0;j<times[i].length;j++){
+                sb.append(' ').append(times[i][j]);
+            }
+            lines[i] = sb.toString();
+        }
+        lines[uids.length] = null;
+        return lines;
+    }
+
+    private long[][] increaseTime(long[][] original) {
+        long[][] newTime = new long[original.length][original[0].length];
+        Random rand = new Random();
+        for(int i = 0;i<original.length;i++){
+            for(int j=0;j<original[0].length;j++){
+                newTime[i][j] = original[i][j] + rand.nextInt(1000_000) + 10000;
+            }
+        }
+        return newTime;
+    }
+
+    // Format an array of cluster times according to the algorithm in KernelUidCpuClusterTimeReader
+    private long[] getTotal(int[] cluster, long[] times) {
+        int core = 0;
+        long[] sum = new long[cluster.length];
+        for(int i=0;i<cluster.length;i++){
+            for(int j=0;j<cluster[i];j++){
+                sum[i] += times[core++] * 10 / (j+1);
+            }
+        }
+        return sum;
+    }
+
+    // Compare array1 against flattened 2d array array2 element by element
+    private boolean testEqual(long[] array1, long[][] array2) {
+        int k=0;
+        for(int i=0;i<array2.length;i++){
+            for(int j=0;j<array2[i].length;j++){
+                if (k >= array1.length || array1[k++]!=array2[i][j])return false;
+            }
+        }
+        return k == array1.length;
+    }
+}
diff --git a/core/tests/coretests/src/com/android/internal/os/MockBatteryStatsImpl.java b/core/tests/coretests/src/com/android/internal/os/MockBatteryStatsImpl.java
index 6c5a2aa..660c744 100644
--- a/core/tests/coretests/src/com/android/internal/os/MockBatteryStatsImpl.java
+++ b/core/tests/coretests/src/com/android/internal/os/MockBatteryStatsImpl.java
@@ -88,6 +88,16 @@
         return this;
     }
 
+    public MockBatteryStatsImpl setKernelUidCpuActiveTimeReader(KernelUidCpuActiveTimeReader reader) {
+        mKernelUidCpuActiveTimeReader = reader;
+        return this;
+    }
+
+    public MockBatteryStatsImpl setKernelUidCpuClusterTimeReader(KernelUidCpuClusterTimeReader reader) {
+        mKernelUidCpuClusterTimeReader = reader;
+        return this;
+    }
+
     public MockBatteryStatsImpl setKernelUidCpuTimeReader(KernelUidCpuTimeReader reader) {
         mKernelUidCpuTimeReader = reader;
         return this;
diff --git a/core/tests/coretests/src/com/android/internal/os/PowerProfileTest.java b/core/tests/coretests/src/com/android/internal/os/PowerProfileTest.java
new file mode 100644
index 0000000..eb7da9c
--- /dev/null
+++ b/core/tests/coretests/src/com/android/internal/os/PowerProfileTest.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ *
+ */
+
+package com.android.internal.os;
+
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.SmallTest;
+
+import junit.framework.TestCase;
+
+import org.junit.Before;
+import org.junit.Test;
+
+/*
+ * Keep this file in sync with frameworks/base/core/res/res/xml/power_profile_test.xml
+ */
+@SmallTest
+public class PowerProfileTest extends TestCase {
+
+    private PowerProfile mProfile;
+
+    @Before
+    public void setUp() {
+        mProfile = new PowerProfile(InstrumentationRegistry.getContext(), true);
+    }
+
+    @Test
+    public void testPowerProfile() {
+        assertEquals(2, mProfile.getNumCpuClusters());
+        assertEquals(4, mProfile.getNumCoresInCpuCluster(0));
+        assertEquals(4, mProfile.getNumCoresInCpuCluster(1));
+        assertEquals(5.0, mProfile.getAveragePower(PowerProfile.POWER_CPU_SUSPEND));
+        assertEquals(1.11, mProfile.getAveragePower(PowerProfile.POWER_CPU_IDLE));
+        assertEquals(2.55, mProfile.getAveragePower(PowerProfile.POWER_CPU_ACTIVE));
+        assertEquals(2.11, mProfile.getAveragePowerForCpuCluster(0));
+        assertEquals(2.22, mProfile.getAveragePowerForCpuCluster(1));
+        assertEquals(3, mProfile.getNumSpeedStepsInCpuCluster(0));
+        assertEquals(30.0, mProfile.getAveragePowerForCpuCore(0, 2));
+        assertEquals(4, mProfile.getNumSpeedStepsInCpuCluster(1));
+        assertEquals(60.0, mProfile.getAveragePowerForCpuCore(1, 3));
+        assertEquals(3000.0, mProfile.getBatteryCapacity());
+    }
+
+}
diff --git a/core/tests/privacytests/src/android/privacy/LongitudinalReportingEncoderTest.java b/core/tests/privacytests/src/android/privacy/LongitudinalReportingEncoderTest.java
index 9166438..6fe19a2 100644
--- a/core/tests/privacytests/src/android/privacy/LongitudinalReportingEncoderTest.java
+++ b/core/tests/privacytests/src/android/privacy/LongitudinalReportingEncoderTest.java
@@ -72,26 +72,26 @@
         final LongitudinalReportingEncoder encoder =
                 LongitudinalReportingEncoder.createInsecureEncoderForTest(
                         config);
+        assertEquals(0, encoder.encodeBoolean(true)[0]);
+        assertEquals(0, encoder.encodeBoolean(true)[0]);
         assertEquals(1, encoder.encodeBoolean(true)[0]);
         assertEquals(0, encoder.encodeBoolean(true)[0]);
         assertEquals(1, encoder.encodeBoolean(true)[0]);
         assertEquals(1, encoder.encodeBoolean(true)[0]);
         assertEquals(1, encoder.encodeBoolean(true)[0]);
         assertEquals(1, encoder.encodeBoolean(true)[0]);
-        assertEquals(0, encoder.encodeBoolean(true)[0]);
-        assertEquals(1, encoder.encodeBoolean(true)[0]);
         assertEquals(1, encoder.encodeBoolean(true)[0]);
         assertEquals(1, encoder.encodeBoolean(true)[0]);
 
         assertEquals(0, encoder.encodeBoolean(false)[0]);
         assertEquals(1, encoder.encodeBoolean(false)[0]);
         assertEquals(1, encoder.encodeBoolean(false)[0]);
-        assertEquals(0, encoder.encodeBoolean(false)[0]);
+        assertEquals(1, encoder.encodeBoolean(false)[0]);
         assertEquals(0, encoder.encodeBoolean(false)[0]);
         assertEquals(0, encoder.encodeBoolean(false)[0]);
         assertEquals(1, encoder.encodeBoolean(false)[0]);
         assertEquals(0, encoder.encodeBoolean(false)[0]);
-        assertEquals(0, encoder.encodeBoolean(false)[0]);
+        assertEquals(1, encoder.encodeBoolean(false)[0]);
         assertEquals(1, encoder.encodeBoolean(false)[0]);
 
         // Test if IRR returns original result when f = 0
diff --git a/core/tests/privacytests/src/android/privacy/RapporEncoderTest.java b/core/tests/privacytests/src/android/privacy/RapporEncoderTest.java
index dad98b8..fa0343d 100644
--- a/core/tests/privacytests/src/android/privacy/RapporEncoderTest.java
+++ b/core/tests/privacytests/src/android/privacy/RapporEncoderTest.java
@@ -80,7 +80,7 @@
         int numBits = 8;
         final long inputValue = 254L;
         final long prrValue = 250L;
-        final long prrAndIrrValue = 184L;
+        final long prrAndIrrValue = 244L;
 
         final RapporConfig config1 = new RapporConfig(
                 "Foo", // encoderId
diff --git a/data/etc/platform.xml b/data/etc/platform.xml
index 993bae1..4be6408 100644
--- a/data/etc/platform.xml
+++ b/data/etc/platform.xml
@@ -162,9 +162,13 @@
     <assign-permission name="android.permission.UPDATE_DEVICE_STATS" uid="cameraserver" />
     <assign-permission name="android.permission.UPDATE_APP_OPS_STATS" uid="cameraserver" />
     <assign-permission name="android.permission.GET_PROCESS_STATE_AND_OOM_SCORE" uid="cameraserver" />
+    <assign-permission name="android.permission.PACKAGE_USAGE_STATS" uid="cameraserver" />
 
     <assign-permission name="android.permission.ACCESS_SURFACE_FLINGER" uid="graphics" />
 
+    <assign-permission name="android.permission.DUMP" uid="incidentd" />
+    <assign-permission name="android.permission.PACKAGE_USAGE_STATS" uid="incidentd" />
+
     <assign-permission name="android.permission.ACCESS_LOWPAN_STATE" uid="lowpan" />
     <assign-permission name="android.permission.MANAGE_LOWPAN_INTERFACES" uid="lowpan" />
 
diff --git a/docs/html/reference/images/text/style/absolutesizespan.png b/docs/html/reference/images/text/style/absolutesizespan.png
new file mode 100644
index 0000000..40d5a79
--- /dev/null
+++ b/docs/html/reference/images/text/style/absolutesizespan.png
Binary files differ
diff --git a/docs/html/reference/images/text/style/backgroundcolorspan.png b/docs/html/reference/images/text/style/backgroundcolorspan.png
new file mode 100644
index 0000000..e7e7271
--- /dev/null
+++ b/docs/html/reference/images/text/style/backgroundcolorspan.png
Binary files differ
diff --git a/docs/html/reference/images/text/style/custombulletspan.png b/docs/html/reference/images/text/style/custombulletspan.png
new file mode 100644
index 0000000..251f8a1
--- /dev/null
+++ b/docs/html/reference/images/text/style/custombulletspan.png
Binary files differ
diff --git a/docs/html/reference/images/text/style/defaultbulletspan.png b/docs/html/reference/images/text/style/defaultbulletspan.png
new file mode 100644
index 0000000..854143f
--- /dev/null
+++ b/docs/html/reference/images/text/style/defaultbulletspan.png
Binary files differ
diff --git a/docs/html/reference/images/text/style/foregroundcolorspan.png b/docs/html/reference/images/text/style/foregroundcolorspan.png
new file mode 100644
index 0000000..f60db6c
--- /dev/null
+++ b/docs/html/reference/images/text/style/foregroundcolorspan.png
Binary files differ
diff --git a/docs/html/reference/images/text/style/relativesizespan.png b/docs/html/reference/images/text/style/relativesizespan.png
new file mode 100644
index 0000000..eaca5ad
--- /dev/null
+++ b/docs/html/reference/images/text/style/relativesizespan.png
Binary files differ
diff --git a/docs/html/reference/images/text/style/scalexspan.png b/docs/html/reference/images/text/style/scalexspan.png
new file mode 100644
index 0000000..a5ca26f
--- /dev/null
+++ b/docs/html/reference/images/text/style/scalexspan.png
Binary files differ
diff --git a/docs/html/reference/images/text/style/strikethroughspan.png b/docs/html/reference/images/text/style/strikethroughspan.png
new file mode 100644
index 0000000..a49ecad
--- /dev/null
+++ b/docs/html/reference/images/text/style/strikethroughspan.png
Binary files differ
diff --git a/docs/html/reference/images/text/style/subscriptspan.png b/docs/html/reference/images/text/style/subscriptspan.png
new file mode 100644
index 0000000..aac7092
--- /dev/null
+++ b/docs/html/reference/images/text/style/subscriptspan.png
Binary files differ
diff --git a/docs/html/reference/images/text/style/superscriptspan.png b/docs/html/reference/images/text/style/superscriptspan.png
new file mode 100644
index 0000000..996f59d
--- /dev/null
+++ b/docs/html/reference/images/text/style/superscriptspan.png
Binary files differ
diff --git a/docs/html/reference/images/text/style/underlinespan.png b/docs/html/reference/images/text/style/underlinespan.png
new file mode 100644
index 0000000..dbcd0d9
--- /dev/null
+++ b/docs/html/reference/images/text/style/underlinespan.png
Binary files differ
diff --git a/graphics/java/android/graphics/ImageDecoder.java b/graphics/java/android/graphics/ImageDecoder.java
index 05dadc9..4d715d1 100644
--- a/graphics/java/android/graphics/ImageDecoder.java
+++ b/graphics/java/android/graphics/ImageDecoder.java
@@ -33,6 +33,8 @@
 import android.net.Uri;
 import android.system.ErrnoException;
 import android.system.Os;
+import android.util.DisplayMetrics;
+import android.util.TypedValue;
 
 import libcore.io.IoUtils;
 import dalvik.system.CloseGuard;
@@ -64,6 +66,19 @@
         Resources getResources() { return null; }
 
         /* @hide */
+        int getDensity() { return Bitmap.DENSITY_NONE; }
+
+        /* @hide */
+        int computeDstDensity() {
+            Resources res = getResources();
+            if (res == null) {
+                return Bitmap.getDefaultDensity();
+            }
+
+            return res.getDisplayMetrics().densityDpi;
+        }
+
+        /* @hide */
         abstract ImageDecoder createImageDecoder() throws IOException;
     };
 
@@ -170,26 +185,73 @@
         return decoder;
     }
 
+    private static class InputStreamSource extends Source {
+        InputStreamSource(Resources res, InputStream is, int inputDensity) {
+            if (is == null) {
+                throw new IllegalArgumentException("The InputStream cannot be null");
+            }
+            mResources = res;
+            mInputStream = is;
+            mInputDensity = res != null ? inputDensity : Bitmap.DENSITY_NONE;
+        }
+
+        final Resources mResources;
+        InputStream mInputStream;
+        final int mInputDensity;
+
+        @Override
+        public Resources getResources() { return mResources; }
+
+        @Override
+        public int getDensity() { return mInputDensity; }
+
+        @Override
+        public ImageDecoder createImageDecoder() throws IOException {
+
+            synchronized (this) {
+                if (mInputStream == null) {
+                    throw new IOException("Cannot reuse InputStreamSource");
+                }
+                InputStream is = mInputStream;
+                mInputStream = null;
+                return createFromStream(is);
+            }
+        }
+    }
+
     private static class ResourceSource extends Source {
         ResourceSource(Resources res, int resId) {
             mResources = res;
             mResId = resId;
+            mResDensity = Bitmap.DENSITY_NONE;
         }
 
         final Resources mResources;
         final int       mResId;
+        int             mResDensity;
 
         @Override
         public Resources getResources() { return mResources; }
 
         @Override
+        public int getDensity() { return mResDensity; }
+
+        @Override
         public ImageDecoder createImageDecoder() throws IOException {
             // This is just used in order to access the underlying Asset and
             // keep it alive. FIXME: Can we skip creating this object?
             InputStream is = null;
             ImageDecoder decoder = null;
+            TypedValue value = new TypedValue();
             try {
-                is = mResources.openRawResource(mResId);
+                is = mResources.openRawResource(mResId, value);
+
+                if (value.density == TypedValue.DENSITY_DEFAULT) {
+                    mResDensity = DisplayMetrics.DENSITY_DEFAULT;
+                } else if (value.density != TypedValue.DENSITY_NONE) {
+                    mResDensity = value.density;
+                }
+
                 if (!(is instanceof AssetManager.AssetInputStream)) {
                     // This should never happen.
                     throw new RuntimeException("Resource is not an asset?");
@@ -421,6 +483,22 @@
     }
 
     /**
+     * Internal API used to generate bitmaps for use by Drawables (i.e. BitmapDrawable)
+     * @hide
+     */
+    public static Source createSource(Resources res, InputStream is) {
+        return new InputStreamSource(res, is, Bitmap.getDefaultDensity());
+    }
+
+    /**
+     * Internal API used to generate bitmaps for use by Drawables (i.e. BitmapDrawable)
+     * @hide
+     */
+    public static Source createSource(Resources res, InputStream is, int density) {
+        return new InputStreamSource(res, is, density);
+    }
+
+    /**
      *  Return the width and height of a given sample size.
      *
      *  This takes an input that functions like
@@ -476,6 +554,10 @@
         this.resize(dimensions.x, dimensions.y);
     }
 
+    private boolean requestedResize() {
+        return mWidth != mDesiredWidth || mHeight != mDesiredHeight;
+    }
+
     // These need to stay in sync with ImageDecoder.cpp's Allocator enum.
     /**
      *  Use the default allocation for the pixel memory.
@@ -730,6 +812,9 @@
                                                 "Drawable!");
             }
 
+            // this call potentially manipulates the decoder so it must be performed prior to
+            // decoding the bitmap and after decode set the density on the resulting bitmap
+            final int srcDensity = computeDensity(src, decoder);
             if (decoder.mAnimated) {
                 // AnimatedImageDrawable calls postProcessAndRelease only if
                 // mPostProcess exists.
@@ -737,7 +822,8 @@
                         null : decoder;
                 Drawable d = new AnimatedImageDrawable(decoder.mNativePtr,
                         postProcessPtr, decoder.mDesiredWidth,
-                        decoder.mDesiredHeight, decoder.mCropRect,
+                        decoder.mDesiredHeight, srcDensity,
+                        src.computeDstDensity(), decoder.mCropRect,
                         decoder.mInputStream, decoder.mAssetFd);
                 // d has taken ownership of these objects.
                 decoder.mInputStream = null;
@@ -746,13 +832,15 @@
             }
 
             Bitmap bm = decoder.decodeBitmap();
-            Resources res = src.getResources();
-            if (res == null) {
-                bm.setDensity(Bitmap.DENSITY_NONE);
-            }
+            bm.setDensity(srcDensity);
 
+            Resources res = src.getResources();
             byte[] np = bm.getNinePatchChunk();
             if (np != null && NinePatch.isNinePatchChunk(np)) {
+                if (res != null) {
+                    bm.setDensity(res.getDisplayMetrics().densityDpi);
+                }
+
                 Rect opticalInsets = new Rect();
                 bm.getOpticalInsets(opticalInsets);
                 Rect padding = new Rect();
@@ -799,10 +887,48 @@
                 }
             }
 
-            return decoder.decodeBitmap();
+            // this call potentially manipulates the decoder so it must be performed prior to
+            // decoding the bitmap
+            final int srcDensity = computeDensity(src, decoder);
+            Bitmap bm = decoder.decodeBitmap();
+            bm.setDensity(srcDensity);
+            return bm;
         }
     }
 
+    // This method may modify the decoder so it must be called prior to performing the decode
+    private static int computeDensity(@NonNull Source src, @NonNull ImageDecoder decoder) {
+        // if the caller changed the size then we treat the density as unknown
+        if (decoder.requestedResize()) {
+            return Bitmap.DENSITY_NONE;
+        }
+
+        // Special stuff for compatibility mode: if the target density is not
+        // the same as the display density, but the resource -is- the same as
+        // the display density, then don't scale it down to the target density.
+        // This allows us to load the system's density-correct resources into
+        // an application in compatibility mode, without scaling those down
+        // to the compatibility density only to have them scaled back up when
+        // drawn to the screen.
+        Resources res = src.getResources();
+        final int srcDensity = src.getDensity();
+        if (res != null && res.getDisplayMetrics().noncompatDensityDpi == srcDensity) {
+            return srcDensity;
+        }
+
+        // downscale the bitmap if the asset has a higher density than the default
+        final int dstDensity = src.computeDstDensity();
+        if (srcDensity != Bitmap.DENSITY_NONE && srcDensity > dstDensity) {
+            float scale = (float) dstDensity / srcDensity;
+            int scaledWidth = (int) (decoder.mWidth * scale + 0.5f);
+            int scaledHeight = (int) (decoder.mHeight * scale + 0.5f);
+            decoder.resize(scaledWidth, scaledHeight);
+            return dstDensity;
+        }
+
+        return srcDensity;
+    }
+
     private String getMimeType() {
         return nGetMimeType(mNativePtr);
     }
diff --git a/graphics/java/android/graphics/drawable/AnimatedImageDrawable.java b/graphics/java/android/graphics/drawable/AnimatedImageDrawable.java
index ce3bd9a..da170c0 100644
--- a/graphics/java/android/graphics/drawable/AnimatedImageDrawable.java
+++ b/graphics/java/android/graphics/drawable/AnimatedImageDrawable.java
@@ -20,12 +20,14 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.content.res.AssetFileDescriptor;
+import android.graphics.Bitmap;
 import android.graphics.Canvas;
 import android.graphics.ColorFilter;
 import android.graphics.ImageDecoder;
 import android.graphics.PixelFormat;
 import android.graphics.Rect;
 import android.os.SystemClock;
+import android.util.DisplayMetrics;
 
 import libcore.io.IoUtils;
 import libcore.util.NativeAllocationRegistry;
@@ -59,22 +61,31 @@
      * decoder is only non-null if it has a PostProcess
      */
     public AnimatedImageDrawable(long nativeImageDecoder,
-            @Nullable ImageDecoder decoder, int width, int height, Rect cropRect,
+            @Nullable ImageDecoder decoder, int width, int height,
+            int srcDensity, int dstDensity, Rect cropRect,
             InputStream inputStream, AssetFileDescriptor afd)
             throws IOException {
-        mNativePtr = nCreate(nativeImageDecoder, decoder, width, height, cropRect);
-        mInputStream = inputStream;
-        mAssetFd = afd;
+        width = Bitmap.scaleFromDensity(width, srcDensity, dstDensity);
+        height = Bitmap.scaleFromDensity(height, srcDensity, dstDensity);
 
         if (cropRect == null) {
             mIntrinsicWidth  = width;
             mIntrinsicHeight = height;
         } else {
+            cropRect.set(Bitmap.scaleFromDensity(cropRect.left, srcDensity, dstDensity),
+                    Bitmap.scaleFromDensity(cropRect.top, srcDensity, dstDensity),
+                    Bitmap.scaleFromDensity(cropRect.right, srcDensity, dstDensity),
+                    Bitmap.scaleFromDensity(cropRect.bottom, srcDensity, dstDensity));
             mIntrinsicWidth  = cropRect.width();
             mIntrinsicHeight = cropRect.height();
         }
 
-        long nativeSize = nNativeByteSize(mNativePtr);
+        mNativePtr = nCreate(nativeImageDecoder, decoder, width, height, cropRect);
+        mInputStream = inputStream;
+        mAssetFd = afd;
+
+        // FIXME: Use the right size for the native allocation.
+        long nativeSize = 200;
         NativeAllocationRegistry registry = new NativeAllocationRegistry(
                 AnimatedImageDrawable.class.getClassLoader(), nGetNativeFinalizer(), nativeSize);
         registry.registerNativeAllocation(this, mNativePtr);
diff --git a/graphics/java/android/graphics/drawable/BitmapDrawable.java b/graphics/java/android/graphics/drawable/BitmapDrawable.java
index e3740e3..f74c39d 100644
--- a/graphics/java/android/graphics/drawable/BitmapDrawable.java
+++ b/graphics/java/android/graphics/drawable/BitmapDrawable.java
@@ -27,6 +27,7 @@
 import android.graphics.BitmapShader;
 import android.graphics.Canvas;
 import android.graphics.ColorFilter;
+import android.graphics.ImageDecoder;
 import android.graphics.Insets;
 import android.graphics.Matrix;
 import android.graphics.Outline;
@@ -49,6 +50,7 @@
 import org.xmlpull.v1.XmlPullParser;
 import org.xmlpull.v1.XmlPullParserException;
 
+import java.io.FileInputStream;
 import java.io.IOException;
 import java.io.InputStream;
 
@@ -111,7 +113,7 @@
      */
     @Deprecated
     public BitmapDrawable() {
-        mBitmapState = new BitmapState((Bitmap) null);
+        init(new BitmapState((Bitmap) null), null);
     }
 
     /**
@@ -124,8 +126,7 @@
     @SuppressWarnings("unused")
     @Deprecated
     public BitmapDrawable(Resources res) {
-        mBitmapState = new BitmapState((Bitmap) null);
-        mBitmapState.mTargetDensity = mTargetDensity;
+        init(new BitmapState((Bitmap) null), res);
     }
 
     /**
@@ -135,7 +136,7 @@
      */
     @Deprecated
     public BitmapDrawable(Bitmap bitmap) {
-        this(new BitmapState(bitmap), null);
+        init(new BitmapState(bitmap), null);
     }
 
     /**
@@ -143,8 +144,7 @@
      * the display metrics of the resources.
      */
     public BitmapDrawable(Resources res, Bitmap bitmap) {
-        this(new BitmapState(bitmap), res);
-        mBitmapState.mTargetDensity = mTargetDensity;
+        init(new BitmapState(bitmap), res);
     }
 
     /**
@@ -154,10 +154,7 @@
      */
     @Deprecated
     public BitmapDrawable(String filepath) {
-        this(new BitmapState(BitmapFactory.decodeFile(filepath)), null);
-        if (mBitmapState.mBitmap == null) {
-            android.util.Log.w("BitmapDrawable", "BitmapDrawable cannot decode " + filepath);
-        }
+        this(null, filepath);
     }
 
     /**
@@ -165,10 +162,21 @@
      */
     @SuppressWarnings("unused")
     public BitmapDrawable(Resources res, String filepath) {
-        this(new BitmapState(BitmapFactory.decodeFile(filepath)), null);
-        mBitmapState.mTargetDensity = mTargetDensity;
-        if (mBitmapState.mBitmap == null) {
-            android.util.Log.w("BitmapDrawable", "BitmapDrawable cannot decode " + filepath);
+        Bitmap bitmap = null;
+        try (FileInputStream stream = new FileInputStream(filepath)) {
+            bitmap = ImageDecoder.decodeBitmap(ImageDecoder.createSource(res, stream),
+                    (info, decoder) -> {
+                decoder.setAllocator(ImageDecoder.SOFTWARE_ALLOCATOR);
+            });
+        } catch (Exception e) {
+            /*  do nothing. This matches the behavior of BitmapFactory.decodeFile()
+                If the exception happened on decode, mBitmapState.mBitmap will be null.
+            */
+        } finally {
+            init(new BitmapState(bitmap), res);
+            if (mBitmapState.mBitmap == null) {
+                android.util.Log.w("BitmapDrawable", "BitmapDrawable cannot decode " + filepath);
+            }
         }
     }
 
@@ -179,10 +187,7 @@
      */
     @Deprecated
     public BitmapDrawable(java.io.InputStream is) {
-        this(new BitmapState(BitmapFactory.decodeStream(is)), null);
-        if (mBitmapState.mBitmap == null) {
-            android.util.Log.w("BitmapDrawable", "BitmapDrawable cannot decode " + is);
-        }
+        this(null, is);
     }
 
     /**
@@ -190,10 +195,21 @@
      */
     @SuppressWarnings("unused")
     public BitmapDrawable(Resources res, java.io.InputStream is) {
-        this(new BitmapState(BitmapFactory.decodeStream(is)), null);
-        mBitmapState.mTargetDensity = mTargetDensity;
-        if (mBitmapState.mBitmap == null) {
-            android.util.Log.w("BitmapDrawable", "BitmapDrawable cannot decode " + is);
+        Bitmap bitmap = null;
+        try {
+            bitmap = ImageDecoder.decodeBitmap(ImageDecoder.createSource(res, is),
+                    (info, decoder) -> {
+                decoder.setAllocator(ImageDecoder.SOFTWARE_ALLOCATOR);
+            });
+        } catch (Exception e) {
+            /*  do nothing. This matches the behavior of BitmapFactory.decodeStream()
+                If the exception happened on decode, mBitmapState.mBitmap will be null.
+            */
+        } finally {
+            init(new BitmapState(bitmap), res);
+            if (mBitmapState.mBitmap == null) {
+                android.util.Log.w("BitmapDrawable", "BitmapDrawable cannot decode " + is);
+            }
         }
     }
 
@@ -812,9 +828,19 @@
                 }
             }
 
+            int density = Bitmap.DENSITY_NONE;
+            if (value.density == TypedValue.DENSITY_DEFAULT) {
+                density = DisplayMetrics.DENSITY_DEFAULT;
+            } else if (value.density != TypedValue.DENSITY_NONE) {
+                density = value.density;
+            }
+
             Bitmap bitmap = null;
             try (InputStream is = r.openRawResource(srcResId, value)) {
-                bitmap = BitmapFactory.decodeResourceStream(r, value, is, null, null);
+                ImageDecoder.Source source = ImageDecoder.createSource(r, is, density);
+                bitmap = ImageDecoder.decodeBitmap(source, (info, decoder) -> {
+                    decoder.setAllocator(ImageDecoder.SOFTWARE_ALLOCATOR);
+                });
             } catch (Exception e) {
                 // Do nothing and pick up the error below.
             }
@@ -1013,14 +1039,21 @@
         }
     }
 
+    private BitmapDrawable(BitmapState state, Resources res) {
+        init(state, res);
+    }
+
     /**
-     * The one constructor to rule them all. This is called by all public
+     * The one helper to rule them all. This is called by all public & private
      * constructors to set the state and initialize local properties.
      */
-    private BitmapDrawable(BitmapState state, Resources res) {
+    private void init(BitmapState state, Resources res) {
         mBitmapState = state;
-
         updateLocalState(res);
+
+        if (mBitmapState != null && res != null) {
+            mBitmapState.mTargetDensity = mTargetDensity;
+        }
     }
 
     /**
diff --git a/graphics/java/android/graphics/drawable/Drawable.java b/graphics/java/android/graphics/drawable/Drawable.java
index f17cd76..291b0a0 100644
--- a/graphics/java/android/graphics/drawable/Drawable.java
+++ b/graphics/java/android/graphics/drawable/Drawable.java
@@ -37,6 +37,7 @@
 import android.graphics.Canvas;
 import android.graphics.Color;
 import android.graphics.ColorFilter;
+import android.graphics.ImageDecoder;
 import android.graphics.Insets;
 import android.graphics.NinePatch;
 import android.graphics.Outline;
@@ -50,11 +51,13 @@
 import android.os.Trace;
 import android.util.AttributeSet;
 import android.util.DisplayMetrics;
+import android.util.Log;
 import android.util.StateSet;
 import android.util.TypedValue;
 import android.util.Xml;
 import android.view.View;
 
+import java.io.FileInputStream;
 import java.io.IOException;
 import java.io.InputStream;
 import java.lang.ref.WeakReference;
@@ -1175,6 +1178,10 @@
             return null;
         }
 
+        if (opts == null) {
+            return getBitmapDrawable(res, value, is);
+        }
+
         /*  ugh. The decodeStream contract is that we have already allocated
             the pad rect, but if the bitmap does not had a ninepatch chunk,
             then the pad will be ignored. If we could change this to lazily
@@ -1207,6 +1214,33 @@
         return null;
     }
 
+    private static Drawable getBitmapDrawable(Resources res, TypedValue value, InputStream is) {
+        try {
+            ImageDecoder.Source source = null;
+            if (value != null) {
+                int density = Bitmap.DENSITY_NONE;
+                if (value.density == TypedValue.DENSITY_DEFAULT) {
+                    density = DisplayMetrics.DENSITY_DEFAULT;
+                } else if (value.density != TypedValue.DENSITY_NONE) {
+                    density = value.density;
+                }
+                source = ImageDecoder.createSource(res, is, density);
+            } else {
+                source = ImageDecoder.createSource(res, is);
+            }
+
+            return ImageDecoder.decodeDrawable(source, (info, decoder) -> {
+                decoder.setAllocator(ImageDecoder.SOFTWARE_ALLOCATOR);
+            });
+        } catch (IOException e) {
+            /*  do nothing.
+                If the exception happened on decode, the drawable will be null.
+            */
+            Log.e("Drawable", "Unable to decode stream: " + e);
+        }
+        return null;
+    }
+
     /**
      * Create a drawable from an XML document. For more information on how to
      * create resources in XML, see
@@ -1306,11 +1340,10 @@
         }
 
         Trace.traceBegin(Trace.TRACE_TAG_RESOURCES, pathName);
-        try {
-            Bitmap bm = BitmapFactory.decodeFile(pathName);
-            if (bm != null) {
-                return drawableFromBitmap(null, bm, null, null, null, pathName);
-            }
+        try (FileInputStream stream = new FileInputStream(pathName)) {
+            return getBitmapDrawable(null, null, stream);
+        } catch(IOException e) {
+            // Do nothing; we will just return null if the FileInputStream had an error
         } finally {
             Trace.traceEnd(Trace.TRACE_TAG_RESOURCES);
         }
diff --git a/libs/hwui/pipeline/skia/RenderNodeDrawable.cpp b/libs/hwui/pipeline/skia/RenderNodeDrawable.cpp
index e2f02df..77925fd 100644
--- a/libs/hwui/pipeline/skia/RenderNodeDrawable.cpp
+++ b/libs/hwui/pipeline/skia/RenderNodeDrawable.cpp
@@ -142,6 +142,7 @@
 
 static bool layerNeedsPaint(const LayerProperties& properties, float alphaMultiplier,
                             SkPaint* paint) {
+    paint->setFilterQuality(kLow_SkFilterQuality);
     if (alphaMultiplier < 1.0f || properties.alpha() < 255 ||
         properties.xferMode() != SkBlendMode::kSrcOver || properties.colorFilter() != nullptr) {
         paint->setAlpha(properties.alpha() * alphaMultiplier);
@@ -200,18 +201,15 @@
         // composing a hardware layer
         if (renderNode->getLayerSurface() && mComposeLayer) {
             SkASSERT(properties.effectiveLayerType() == LayerType::RenderLayer);
-            SkPaint* paint = nullptr;
-            SkPaint tmpPaint;
-            if (layerNeedsPaint(layerProperties, alphaMultiplier, &tmpPaint)) {
-                paint = &tmpPaint;
-            }
+            SkPaint paint;
+            layerNeedsPaint(layerProperties, alphaMultiplier, &paint);
 
             // surfaces for layers are created on LAYER_SIZE boundaries (which are >= layer size) so
             // we need to restrict the portion of the surface drawn to the size of the renderNode.
             SkASSERT(renderNode->getLayerSurface()->width() >= bounds.width());
             SkASSERT(renderNode->getLayerSurface()->height() >= bounds.height());
             canvas->drawImageRect(renderNode->getLayerSurface()->makeImageSnapshot().get(),
-                                  bounds, bounds, paint);
+                    bounds, bounds, &paint);
 
             if (!renderNode->getSkiaLayer()->hasRenderedSinceRepaint) {
                 renderNode->getSkiaLayer()->hasRenderedSinceRepaint = true;
diff --git a/libs/hwui/tests/unit/RenderNodeDrawableTests.cpp b/libs/hwui/tests/unit/RenderNodeDrawableTests.cpp
index c7f57fe..2953ea8 100644
--- a/libs/hwui/tests/unit/RenderNodeDrawableTests.cpp
+++ b/libs/hwui/tests/unit/RenderNodeDrawableTests.cpp
@@ -1046,6 +1046,40 @@
     EXPECT_EQ(2, canvas.mDrawCounter);
 }
 
+// Verify that layers are composed with kLow_SkFilterQuality filter quality.
+RENDERTHREAD_SKIA_PIPELINE_TEST(RenderNodeDrawable, layerComposeQuality) {
+    static const int CANVAS_WIDTH = 1;
+    static const int CANVAS_HEIGHT = 1;
+    static const int LAYER_WIDTH = 1;
+    static const int LAYER_HEIGHT = 1;
+    class FrameTestCanvas : public TestCanvasBase {
+    public:
+        FrameTestCanvas() : TestCanvasBase(CANVAS_WIDTH, CANVAS_HEIGHT) {}
+        void onDrawImageRect(const SkImage* image, const SkRect* src, const SkRect& dst,
+                const SkPaint* paint, SrcRectConstraint constraint) override {
+            mDrawCounter++;
+            EXPECT_EQ(kLow_SkFilterQuality, paint->getFilterQuality());
+        }
+    };
+
+    auto layerNode = TestUtils::createSkiaNode(
+            0, 0, LAYER_WIDTH, LAYER_HEIGHT,
+            [](RenderProperties& properties, SkiaRecordingCanvas& canvas) {
+                canvas.drawPaint(SkPaint());
+            });
+
+    layerNode->animatorProperties().mutateLayerProperties().setType(LayerType::RenderLayer);
+    layerNode->setLayerSurface(SkSurface::MakeRasterN32Premul(LAYER_WIDTH, LAYER_HEIGHT));
+
+    FrameTestCanvas canvas;
+    RenderNodeDrawable drawable(layerNode.get(), &canvas, true);
+    canvas.drawDrawable(&drawable);
+    EXPECT_EQ(1, canvas.mDrawCounter);  //make sure the layer was composed
+
+    // clean up layer pointer, so we can safely destruct RenderNode
+    layerNode->setLayerSurface(nullptr);
+}
+
 TEST(ReorderBarrierDrawable, testShadowMatrix) {
     static const int CANVAS_WIDTH = 100;
     static const int CANVAS_HEIGHT = 100;
diff --git a/libs/incident/Android.mk b/libs/incident/Android.mk
index 5f3e407..b63400f 100644
--- a/libs/incident/Android.mk
+++ b/libs/incident/Android.mk
@@ -32,6 +32,7 @@
 LOCAL_SRC_FILES := \
         ../../core/java/android/os/IIncidentManager.aidl \
         ../../core/java/android/os/IIncidentReportStatusListener.aidl \
+        proto/android/os/header.proto \
         src/IncidentReportArgs.cpp
 
 LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
diff --git a/libs/incident/include/android/os/IncidentReportArgs.h b/libs/incident/include/android/os/IncidentReportArgs.h
index 2849d58..c56f689 100644
--- a/libs/incident/include/android/os/IncidentReportArgs.h
+++ b/libs/incident/include/android/os/IncidentReportArgs.h
@@ -24,6 +24,8 @@
 #include <set>
 #include <vector>
 
+#include "frameworks/base/libs/incident/proto/android/os/header.pb.h"
+
 namespace android {
 namespace os {
 
@@ -47,7 +49,7 @@
     void setAll(bool all);
     void setDest(int dest);
     void addSection(int section);
-    void addHeader(const vector<uint8_t>& header);
+    void addHeader(const IncidentHeaderProto& headerProto);
 
     inline bool all() const { return mAll; }
     bool containsSection(int section) const;
diff --git a/libs/incident/proto/android/os/header.proto b/libs/incident/proto/android/os/header.proto
new file mode 100644
index 0000000..a84dc48
--- /dev/null
+++ b/libs/incident/proto/android/os/header.proto
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+syntax = "proto2";
+option java_multiple_files = true;
+
+package android.os;
+
+// IncidentHeaderProto contains extra information the caller of incidentd wants
+// to attach in an incident report, the data should just be informative.
+message IncidentHeaderProto {
+    // From statsd config, the id of the anomaly alert, unique among alerts.
+    optional int64 alert_id = 1;
+
+    // Format a human readable reason why an incident report is requested.
+    // It's optional and may directly come from a user input clicking the
+    // bug-report button.
+    optional string reason = 2;
+
+    // Defines which stats config used to fire the request, incident report will
+    // only be uploaded if this value is given.
+    message StatsdConfigKey {
+      optional int32 uid = 1; // The uid pushes the config to statsd.
+      optional int64 id = 2; // The unique id of the statsd config.
+    }
+    optional StatsdConfigKey config_key = 3;
+}
diff --git a/libs/incident/src/IncidentReportArgs.cpp b/libs/incident/src/IncidentReportArgs.cpp
index bd9c8ee..fbc21e5 100644
--- a/libs/incident/src/IncidentReportArgs.cpp
+++ b/libs/incident/src/IncidentReportArgs.cpp
@@ -161,8 +161,14 @@
 }
 
 void
-IncidentReportArgs::addHeader(const vector<uint8_t>& header)
+IncidentReportArgs::addHeader(const IncidentHeaderProto& headerProto)
 {
+    vector<uint8_t> header;
+    auto serialized = headerProto.SerializeAsString();
+    if (serialized.empty()) return;
+    for (auto it = serialized.begin(); it != serialized.end(); it++) {
+        header.push_back((uint8_t)*it);
+    }
     mHeaders.push_back(header);
 }
 
diff --git a/location/java/com/android/internal/location/gnssmetrics/GnssMetrics.java b/location/java/com/android/internal/location/gnssmetrics/GnssMetrics.java
index 833376c..603926f 100644
--- a/location/java/com/android/internal/location/gnssmetrics/GnssMetrics.java
+++ b/location/java/com/android/internal/location/gnssmetrics/GnssMetrics.java
@@ -19,10 +19,12 @@
 import android.os.SystemClock;
 
 import android.util.Base64;
+import android.util.Log;
 import android.util.TimeUtils;
 
 import java.util.Arrays;
 
+import com.android.internal.app.IBatteryStats;
 import com.android.internal.location.nano.GnssLogsProto.GnssLog;
 
 /**
@@ -31,14 +33,29 @@
  */
 public class GnssMetrics {
 
+  private static final String TAG = GnssMetrics.class.getSimpleName();
+
+  /* Constant which indicates GPS signal quality is poor */
+  public static final int GPS_SIGNAL_QUALITY_POOR = 0;
+
+  /* Constant which indicates GPS signal quality is good */
+  public static final int GPS_SIGNAL_QUALITY_GOOD = 1;
+
+  /* Number of GPS signal quality levels */
+  public static final int NUM_GPS_SIGNAL_QUALITY_LEVELS = GPS_SIGNAL_QUALITY_GOOD + 1;
+
   /** Default time between location fixes (in millisecs) */
   private static final int DEFAULT_TIME_BETWEEN_FIXES_MILLISECS = 1000;
 
   /* The time since boot when logging started */
   private String logStartInElapsedRealTime;
 
+  /* GNSS power metrics */
+  private GnssPowerMetrics mGnssPowerMetrics;
+
   /** Constructor */
-  public GnssMetrics() {
+  public GnssMetrics(IBatteryStats stats) {
+    mGnssPowerMetrics = new GnssPowerMetrics(stats);
     locationFailureStatistics = new Statistics();
     timeToFirstFixSecStatistics = new Statistics();
     positionAccuracyMeterStatistics = new Statistics();
@@ -103,11 +120,18 @@
   *
   */
   public void logCn0(float[] cn0s, int numSv) {
-    if (numSv < 4) {
+    if (numSv == 0 || cn0s == null || cn0s.length == 0 || cn0s.length < numSv) {
+      if (numSv == 0) {
+         mGnssPowerMetrics.reportSignalQuality(null, 0);
+      }
       return;
     }
     float[] cn0Array = Arrays.copyOf(cn0s, numSv);
     Arrays.sort(cn0Array);
+    mGnssPowerMetrics.reportSignalQuality(cn0Array, numSv);
+    if (numSv < 4) {
+      return;
+    }
     if (cn0Array[numSv - 4] > 0.0) {
       double top4AvgCn0 = 0.0;
       for (int i = numSv - 4; i < numSv; i++) {
@@ -265,4 +289,62 @@
     topFourAverageCn0Statistics.reset();
     return;
   }
+
+  /* Class for handling GNSS power related metrics */
+  private class GnssPowerMetrics {
+
+    /* Threshold for Top Four Average CN0 below which GNSS signal quality is declared poor */
+    private static final double POOR_TOP_FOUR_AVG_CN0_THRESHOLD_DB_HZ = 20.0;
+
+    /* Minimum change in Top Four Average CN0 needed to trigger a report */
+    private static final double REPORTING_THRESHOLD_DB_HZ = 1.0;
+
+    /* BatteryStats API */
+    private final IBatteryStats mBatteryStats;
+
+    /* Last reported Top Four Average CN0 */
+    private double mLastAverageCn0;
+
+    public GnssPowerMetrics(IBatteryStats stats) {
+      mBatteryStats = stats;
+      // Used to initialize the variable to a very small value (unachievable in practice) so that
+      // the first CNO report will trigger an update to BatteryStats
+      mLastAverageCn0 = -100.0;
+    }
+
+    /**
+     * Reports signal quality to BatteryStats. Signal quality is based on Top four average CN0. If
+     * the number of SVs seen is less than 4, then signal quality is the average CN0.
+     * Changes are reported only if the average CN0 changes by more than REPORTING_THRESHOLD_DB_HZ.
+     */
+    public void reportSignalQuality(float[] ascendingCN0Array, int numSv) {
+      double avgCn0 = 0.0;
+      if (numSv > 0) {
+        for (int i = Math.max(0, numSv - 4); i < numSv; i++) {
+          avgCn0 += (double) ascendingCN0Array[i];
+        }
+        avgCn0 /= Math.min(numSv, 4);
+      }
+      if (Math.abs(avgCn0 - mLastAverageCn0) < REPORTING_THRESHOLD_DB_HZ) {
+        return;
+      }
+      try {
+        mBatteryStats.noteGpsSignalQuality(getSignalLevel(avgCn0));
+        mLastAverageCn0 = avgCn0;
+      } catch (Exception e) {
+        Log.w(TAG, "Exception", e);
+      }
+      return;
+    }
+
+    /**
+     * Obtains signal level based on CN0
+     */
+    private int getSignalLevel(double cn0) {
+      if (cn0 > POOR_TOP_FOUR_AVG_CN0_THRESHOLD_DB_HZ) {
+        return GnssMetrics.GPS_SIGNAL_QUALITY_GOOD;
+      }
+      return GnssMetrics.GPS_SIGNAL_QUALITY_POOR;
+    }
+  }
 }
\ No newline at end of file
diff --git a/media/java/android/media/AudioFormat.java b/media/java/android/media/AudioFormat.java
index 93fc3da..b07d042 100644
--- a/media/java/android/media/AudioFormat.java
+++ b/media/java/android/media/AudioFormat.java
@@ -238,22 +238,15 @@
     public static final int ENCODING_DTS = 7;
     /** Audio data format: DTS HD compressed */
     public static final int ENCODING_DTS_HD = 8;
-    /** Audio data format: MP3 compressed
-     * @hide
-     * */
+    /** Audio data format: MP3 compressed */
     public static final int ENCODING_MP3 = 9;
-    /** Audio data format: AAC LC compressed
-     * @hide
-     * */
+    /** Audio data format: AAC LC compressed */
     public static final int ENCODING_AAC_LC = 10;
-    /** Audio data format: AAC HE V1 compressed
-     * @hide
-     * */
+    /** Audio data format: AAC HE V1 compressed */
     public static final int ENCODING_AAC_HE_V1 = 11;
-    /** Audio data format: AAC HE V2 compressed
-     * @hide
-     * */
+    /** Audio data format: AAC HE V2 compressed */
     public static final int ENCODING_AAC_HE_V2 = 12;
+
     /** Audio data format: compressed audio wrapped in PCM for HDMI
      * or S/PDIF passthrough.
      * IEC61937 uses a stereo stream of 16-bit samples as the wrapper.
@@ -266,6 +259,12 @@
     /** Audio data format: DOLBY TRUEHD compressed
      **/
     public static final int ENCODING_DOLBY_TRUEHD = 14;
+    /** Audio data format: AAC ELD compressed */
+    public static final int ENCODING_AAC_ELD = 15;
+    /** Audio data format: AAC xHE compressed */
+    public static final int ENCODING_AAC_XHE = 16;
+    /** Audio data format: AC-4 sync frame transport format */
+    public static final int ENCODING_AC4 = 17;
 
     /** @hide */
     public static String toLogFriendlyEncoding(int enc) {
@@ -298,6 +297,12 @@
                 return "ENCODING_IEC61937";
             case ENCODING_DOLBY_TRUEHD:
                 return "ENCODING_DOLBY_TRUEHD";
+            case ENCODING_AAC_ELD:
+                return "ENCODING_AAC_ELD";
+            case ENCODING_AAC_XHE:
+                return "ENCODING_AAC_XHE";
+            case ENCODING_AC4:
+                return "ENCODING_AC4";
             default :
                 return "invalid encoding " + enc;
         }
@@ -514,6 +519,9 @@
         case ENCODING_AAC_HE_V1:
         case ENCODING_AAC_HE_V2:
         case ENCODING_IEC61937:
+        case ENCODING_AAC_ELD:
+        case ENCODING_AAC_XHE:
+        case ENCODING_AC4:
             return true;
         default:
             return false;
@@ -532,6 +540,13 @@
         case ENCODING_DTS:
         case ENCODING_DTS_HD:
         case ENCODING_IEC61937:
+        case ENCODING_MP3:
+        case ENCODING_AAC_LC:
+        case ENCODING_AAC_HE_V1:
+        case ENCODING_AAC_HE_V2:
+        case ENCODING_AAC_ELD:
+        case ENCODING_AAC_XHE:
+        case ENCODING_AC4:
             return true;
         default:
             return false;
@@ -556,6 +571,9 @@
         case ENCODING_AAC_HE_V1:
         case ENCODING_AAC_HE_V2:
         case ENCODING_IEC61937: // wrapped in PCM but compressed
+        case ENCODING_AAC_ELD:
+        case ENCODING_AAC_XHE:
+        case ENCODING_AC4:
             return false;
         case ENCODING_INVALID:
         default:
@@ -581,6 +599,9 @@
         case ENCODING_AAC_LC:
         case ENCODING_AAC_HE_V1:
         case ENCODING_AAC_HE_V2:
+        case ENCODING_AAC_ELD:
+        case ENCODING_AAC_XHE:
+        case ENCODING_AC4:
             return false;
         case ENCODING_INVALID:
         default:
@@ -794,14 +815,7 @@
 
         /**
          * Sets the data encoding format.
-         * @param encoding one of {@link AudioFormat#ENCODING_DEFAULT},
-         *     {@link AudioFormat#ENCODING_PCM_8BIT},
-         *     {@link AudioFormat#ENCODING_PCM_16BIT},
-         *     {@link AudioFormat#ENCODING_PCM_FLOAT},
-         *     {@link AudioFormat#ENCODING_AC3},
-         *     {@link AudioFormat#ENCODING_E_AC3}.
-         *     {@link AudioFormat#ENCODING_DTS},
-         *     {@link AudioFormat#ENCODING_DTS_HD}.
+         * @param encoding the specified encoding or default.
          * @return the same Builder instance.
          * @throws java.lang.IllegalArgumentException
          */
@@ -818,6 +832,13 @@
                 case ENCODING_DTS:
                 case ENCODING_DTS_HD:
                 case ENCODING_IEC61937:
+                case ENCODING_MP3:
+                case ENCODING_AAC_LC:
+                case ENCODING_AAC_HE_V1:
+                case ENCODING_AAC_HE_V2:
+                case ENCODING_AAC_ELD:
+                case ENCODING_AAC_XHE:
+                case ENCODING_AC4:
                     mEncoding = encoding;
                     break;
                 case ENCODING_INVALID:
@@ -1016,7 +1037,7 @@
     }
 
     /** @hide */
-    @IntDef({
+    @IntDef(flag = false, prefix = "ENCODING", value = {
         ENCODING_DEFAULT,
         ENCODING_PCM_8BIT,
         ENCODING_PCM_16BIT,
@@ -1025,8 +1046,14 @@
         ENCODING_E_AC3,
         ENCODING_DTS,
         ENCODING_DTS_HD,
-        ENCODING_IEC61937
-    })
+        ENCODING_IEC61937,
+        ENCODING_AAC_HE_V1,
+        ENCODING_AAC_HE_V2,
+        ENCODING_AAC_LC,
+        ENCODING_AAC_ELD,
+        ENCODING_AAC_XHE,
+        ENCODING_AC4 }
+    )
     @Retention(RetentionPolicy.SOURCE)
     public @interface Encoding {}
 
diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java
index 913b5e8..2ac4063 100644
--- a/media/java/android/media/AudioManager.java
+++ b/media/java/android/media/AudioManager.java
@@ -1329,6 +1329,19 @@
      }
 
     //====================================================================
+    // Offload query
+    /**
+     * Returns whether offloaded playback of an audio format is supported on the device.
+     * Offloaded playback is where the decoding of an audio stream is not competing with other
+     * software resources. In general, it is supported by dedicated hardware, such as audio DSPs.
+     * @param format the audio format (codec, sample rate, channels) being checked.
+     * @return true if the given audio format can be offloaded.
+     */
+    public boolean isOffloadedPlaybackSupported(@NonNull AudioFormat format) {
+        return AudioSystem.isOffloadSupported(format);
+    }
+
+    //====================================================================
     // Bluetooth SCO control
     /**
      * Sticky broadcast intent action indicating that the Bluetooth SCO audio
@@ -3746,6 +3759,33 @@
     }
 
      /**
+     * Indicate A2DP source or sink connection state change and eventually suppress
+     * the {@link AudioManager.ACTION_AUDIO_BECOMING_NOISY} intent.
+     * @param device Bluetooth device connected/disconnected
+     * @param state  new connection state (BluetoothProfile.STATE_xxx)
+     * @param profile profile for the A2DP device
+     * (either {@link android.bluetooth.BluetoothProfile.A2DP} or
+     * {@link android.bluetooth.BluetoothProfile.A2DP_SINK})
+     * @param suppressNoisyIntent if true the
+     * {@link AudioManager.ACTION_AUDIO_BECOMING_NOISY} intent will not be sent.
+     * @return a delay in ms that the caller should wait before broadcasting
+     * BluetoothA2dp.ACTION_CONNECTION_STATE_CHANGED intent.
+     * {@hide}
+     */
+    public int setBluetoothA2dpDeviceConnectionStateSuppressNoisyIntent(
+                BluetoothDevice device, int state, int profile, boolean suppressNoisyIntent) {
+        final IAudioService service = getService();
+        int delay = 0;
+        try {
+            delay = service.setBluetoothA2dpDeviceConnectionStateSuppressNoisyIntent(device,
+                state, profile, suppressNoisyIntent);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+        return delay;
+    }
+
+     /**
      * Indicate A2DP device configuration has changed.
      * @param device Bluetooth device whose configuration has changed.
      * {@hide}
diff --git a/media/java/android/media/AudioSystem.java b/media/java/android/media/AudioSystem.java
index e56944d..b4316ba 100644
--- a/media/java/android/media/AudioSystem.java
+++ b/media/java/android/media/AudioSystem.java
@@ -16,6 +16,7 @@
 
 package android.media;
 
+import android.annotation.NonNull;
 import android.content.Context;
 import android.content.pm.PackageManager;
 import android.media.audiopolicy.AudioMix;
@@ -818,6 +819,14 @@
 
     public static native float getStreamVolumeDB(int stream, int index, int device);
 
+    static boolean isOffloadSupported(@NonNull AudioFormat format) {
+        return native_is_offload_supported(format.getEncoding(), format.getSampleRate(),
+                format.getChannelMask(), format.getChannelIndexMask());
+    }
+
+    private static native boolean native_is_offload_supported(int encoding, int sampleRate,
+            int channelMask, int channelIndexMask);
+
     // Items shared with audio service
 
     /**
@@ -914,7 +923,8 @@
             (1 << STREAM_MUSIC) |
             (1 << STREAM_RING) |
             (1 << STREAM_NOTIFICATION) |
-            (1 << STREAM_SYSTEM);
+            (1 << STREAM_SYSTEM) |
+            (1 << STREAM_VOICE_CALL);
 
     /**
      * Event posted by AudioTrack and AudioRecord JNI (JNIDeviceCallback) when routing changes.
diff --git a/media/java/android/media/AudioTrack.java b/media/java/android/media/AudioTrack.java
index e535fdf..5928d03 100644
--- a/media/java/android/media/AudioTrack.java
+++ b/media/java/android/media/AudioTrack.java
@@ -24,7 +24,9 @@
 import java.nio.ByteOrder;
 import java.nio.NioUtils;
 import java.util.Collection;
+import java.util.concurrent.Executor;
 
+import android.annotation.CallbackExecutor;
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -185,6 +187,22 @@
      * Event id denotes when previously set update period has elapsed during playback.
      */
     private static final int NATIVE_EVENT_NEW_POS = 4;
+    /**
+     * Callback for more data
+     * TODO only for offload
+     */
+    private static final int NATIVE_EVENT_MORE_DATA = 0;
+    /**
+     * IAudioTrack tear down for offloaded tracks
+     * TODO: when received, java AudioTrack must be released
+     */
+    private static final int NATIVE_EVENT_NEW_IAUDIOTRACK = 6;
+    /**
+     * Event id denotes when all the buffers queued in AF and HW are played
+     * back (after stop is called) for an offloaded track.
+     * TODO: not just for offload
+     */
+    private static final int NATIVE_EVENT_STREAM_END = 7;
 
     private final static String TAG = "android.media.AudioTrack";
 
@@ -540,6 +558,12 @@
     public AudioTrack(AudioAttributes attributes, AudioFormat format, int bufferSizeInBytes,
             int mode, int sessionId)
                     throws IllegalArgumentException {
+        this(attributes, format, bufferSizeInBytes, mode, sessionId, false /*offload*/);
+    }
+
+    private AudioTrack(AudioAttributes attributes, AudioFormat format, int bufferSizeInBytes,
+            int mode, int sessionId, boolean offload)
+                    throws IllegalArgumentException {
         super(attributes, AudioPlaybackConfiguration.PLAYER_TYPE_JAM_AUDIOTRACK);
         // mState already == STATE_UNINITIALIZED
 
@@ -601,7 +625,8 @@
         // native initialization
         int initResult = native_setup(new WeakReference<AudioTrack>(this), mAttributes,
                 sampleRate, mChannelMask, mChannelIndexMask, mAudioFormat,
-                mNativeBufferSizeInBytes, mDataLoadMode, session, 0 /*nativeTrackInJavaObj*/);
+                mNativeBufferSizeInBytes, mDataLoadMode, session, 0 /*nativeTrackInJavaObj*/,
+                offload);
         if (initResult != SUCCESS) {
             loge("Error code "+initResult+" when initializing AudioTrack.");
             return; // with mState == STATE_UNINITIALIZED
@@ -681,7 +706,8 @@
                     0 /*mNativeBufferSizeInBytes - NA*/,
                     0 /*mDataLoadMode - NA*/,
                     session,
-                    nativeTrackInJavaObj);
+                    nativeTrackInJavaObj,
+                    false /*offload*/);
             if (initResult != SUCCESS) {
                 loge("Error code "+initResult+" when initializing AudioTrack.");
                 return; // with mState == STATE_UNINITIALIZED
@@ -729,6 +755,7 @@
      * <code>MODE_STREAM</code> will be used.
      * <br>If the session ID is not specified with {@link #setSessionId(int)}, a new one will
      * be generated.
+     * <br>Offload is false by default.
      */
     public static class Builder {
         private AudioAttributes mAttributes;
@@ -737,6 +764,7 @@
         private int mSessionId = AudioManager.AUDIO_SESSION_ID_GENERATE;
         private int mMode = MODE_STREAM;
         private int mPerformanceMode = PERFORMANCE_MODE_NONE;
+        private boolean mOffload = false;
 
         /**
          * Constructs a new Builder with the default values as described above.
@@ -867,6 +895,21 @@
         }
 
         /**
+         * Sets whether this track will play through the offloaded audio path.
+         * When set to true, at build time, the audio format will be checked against
+         * {@link AudioManager#isOffloadedPlaybackSupported(AudioFormat)} to verify the audio format
+         * used by this track is supported on the device's offload path (if any).
+         * <br>Offload is only supported for media audio streams, and therefore requires that
+         * the usage be {@link AudioAttributes#USAGE_MEDIA}.
+         * @param offload true to require the offload path for playback.
+         * @return the same Builder instance.
+         */
+        public @NonNull Builder setOffloadedPlayback(boolean offload) {
+            mOffload = offload;
+            return this;
+        }
+
+        /**
          * Builds an {@link AudioTrack} instance initialized with all the parameters set
          * on this <code>Builder</code>.
          * @return a new successfully initialized {@link AudioTrack} instance.
@@ -909,6 +952,19 @@
                         .setEncoding(AudioFormat.ENCODING_DEFAULT)
                         .build();
             }
+
+            //TODO tie offload to PERFORMANCE_MODE_POWER_SAVING?    
+            if (mOffload) {
+                if (mAttributes.getUsage() != AudioAttributes.USAGE_MEDIA) {
+                    throw new UnsupportedOperationException(
+                            "Cannot create AudioTrack, offload requires USAGE_MEDIA");
+                }
+                if (!AudioSystem.isOffloadSupported(mFormat)) {
+                    throw new UnsupportedOperationException(
+                            "Cannot create AudioTrack, offload format not supported");
+                }
+            }
+
             try {
                 // If the buffer size is not specified in streaming mode,
                 // use a single frame for the buffer size and let the
@@ -918,7 +974,7 @@
                             * mFormat.getBytesPerSample(mFormat.getEncoding());
                 }
                 final AudioTrack track = new AudioTrack(
-                        mAttributes, mFormat, mBufferSizeInBytes, mMode, mSessionId);
+                        mAttributes, mFormat, mBufferSizeInBytes, mMode, mSessionId, mOffload);
                 if (track.getState() == STATE_UNINITIALIZED) {
                     // release is not necessary
                     throw new UnsupportedOperationException("Cannot create AudioTrack");
@@ -2882,6 +2938,69 @@
         void onPeriodicNotification(AudioTrack track);
     }
 
+    /**
+     * Abstract class to receive event notification about the stream playback.
+     * See {@link AudioTrack#setStreamEventCallback(Executor, StreamEventCallback)} to register
+     * the callback on the given {@link AudioTrack} instance.
+     */
+    public abstract static class StreamEventCallback {
+        /** @hide */ // add hidden empty constructor so it doesn't show in SDK
+        public StreamEventCallback() { }
+        /**
+         * Called when an offloaded track is no longer valid and has been discarded by the system.
+         * An example of this happening is when an offloaded track has been paused too long, and
+         * gets invalidated by the system to prevent any other offload.
+         * @param track the {@link AudioTrack} on which the event happened
+         */
+        public void onTearDown(AudioTrack track) { }
+        /**
+         * Called when all the buffers of an offloaded track that were queued in the audio system
+         * (e.g. the combination of the Android audio framework and the device's audio hardware)
+         * have been played after {@link AudioTrack#stop()} has been called.
+         * @param track the {@link AudioTrack} on which the event happened
+         */
+        public void onStreamPresentationEnd(AudioTrack track) { }
+        /**
+         * Called when more audio data can be written without blocking on an offloaded track.
+         * @param track the {@link AudioTrack} on which the event happened
+         */
+        public void onStreamDataRequest(AudioTrack track) { }
+    }
+
+    private Executor mStreamEventExec;
+    private StreamEventCallback mStreamEventCb;
+    private final Object mStreamEventCbLock = new Object();
+
+    /**
+     * Sets the callback for the notification of stream events.
+     * @param executor {@link Executor} to handle the callbacks
+     * @param eventCallback the callback to receive the stream event notifications
+     */
+    public void setStreamEventCallback(@NonNull @CallbackExecutor Executor executor,
+            @NonNull StreamEventCallback eventCallback) {
+        if (eventCallback == null) {
+            throw new IllegalArgumentException("Illegal null StreamEventCallback");
+        }
+        if (executor == null) {
+            throw new IllegalArgumentException("Illegal null Executor for the StreamEventCallback");
+        }
+        synchronized (mStreamEventCbLock) {
+            mStreamEventExec = executor;
+            mStreamEventCb = eventCallback;
+        }
+    }
+
+    /**
+     * Unregisters the callback for notification of stream events, previously set
+     * by {@link #setStreamEventCallback(Executor, StreamEventCallback)}.
+     */
+    public void removeStreamEventCallback() {
+        synchronized (mStreamEventCbLock) {
+            mStreamEventExec = null;
+            mStreamEventCb = null;
+        }
+    }
+
     //---------------------------------------------------------
     // Inner classes
     //--------------------
@@ -2965,7 +3084,7 @@
     private static void postEventFromNative(Object audiotrack_ref,
             int what, int arg1, int arg2, Object obj) {
         //logd("Event posted from the native side: event="+ what + " args="+ arg1+" "+arg2);
-        AudioTrack track = (AudioTrack)((WeakReference)audiotrack_ref).get();
+        final AudioTrack track = (AudioTrack)((WeakReference)audiotrack_ref).get();
         if (track == null) {
             return;
         }
@@ -2974,6 +3093,32 @@
             track.broadcastRoutingChange();
             return;
         }
+
+        if (what == NATIVE_EVENT_MORE_DATA || what == NATIVE_EVENT_NEW_IAUDIOTRACK
+                || what == NATIVE_EVENT_STREAM_END) {
+            final Executor exec;
+            final StreamEventCallback cb;
+            synchronized (track.mStreamEventCbLock) {
+                exec = track.mStreamEventExec;
+                cb = track.mStreamEventCb;
+            }
+            if ((exec == null) || (cb == null)) {
+                return;
+            }
+            switch (what) {
+                case NATIVE_EVENT_MORE_DATA:
+                    exec.execute(() -> cb.onStreamDataRequest(track));
+                    return;
+                case NATIVE_EVENT_NEW_IAUDIOTRACK:
+                    // TODO also release track as it's not longer usable
+                    exec.execute(() -> cb.onTearDown(track));
+                    return;
+                case NATIVE_EVENT_STREAM_END:
+                    exec.execute(() -> cb.onStreamPresentationEnd(track));
+                    return;
+            }
+        }
+
         NativePositionEventHandlerDelegate delegate = track.mEventHandlerDelegate;
         if (delegate != null) {
             Handler handler = delegate.getHandler();
@@ -2995,7 +3140,8 @@
     private native final int native_setup(Object /*WeakReference<AudioTrack>*/ audiotrack_this,
             Object /*AudioAttributes*/ attributes,
             int[] sampleRate, int channelMask, int channelIndexMask, int audioFormat,
-            int buffSizeInBytes, int mode, int[] sessionId, long nativeAudioTrack);
+            int buffSizeInBytes, int mode, int[] sessionId, long nativeAudioTrack,
+            boolean offload);
 
     private native final void native_finalize();
 
diff --git a/media/java/android/media/IAudioService.aidl b/media/java/android/media/IAudioService.aidl
index bb6ae98..6c65223 100644
--- a/media/java/android/media/IAudioService.aidl
+++ b/media/java/android/media/IAudioService.aidl
@@ -203,5 +203,8 @@
 
     oneway void playerHasOpPlayAudio(in int piid, in boolean hasOpPlayAudio);
 
+    int setBluetoothA2dpDeviceConnectionStateSuppressNoisyIntent(in BluetoothDevice device,
+            int state, int profile, boolean suppressNoisyIntent);
+
     // WARNING: read warning at top of file, it is recommended to add new methods at the end
 }
diff --git a/media/java/android/media/IMediaSession2.aidl b/media/java/android/media/IMediaSession2.aidl
new file mode 100644
index 0000000..bc8121e
--- /dev/null
+++ b/media/java/android/media/IMediaSession2.aidl
@@ -0,0 +1,65 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media;
+
+import android.media.session.PlaybackState;
+import android.media.IMediaSession2Callback;
+import android.os.Bundle;
+
+/**
+ * Interface to MediaSession2. Framework MUST only call oneway APIs.
+ *
+ * @hide
+ */
+// TODO(jaewan): Make this oneway interface.
+//               Malicious app can fake session binder and holds commands from controller.
+interface IMediaSession2 {
+    // TODO(jaewan): add onCommand() to send private command
+    // TODO(jaewan): Due to the nature of oneway calls, APIs can be called in out of order
+    //               Add id for individual calls to address this.
+
+    // TODO(jaewan): We may consider to add another binder just for the connection
+    //               not to expose other methods to the controller whose connection wasn't accepted.
+    //               But this would be enough for now because it's the same as existing
+    //               MediaBrowser and MediaBrowserService.
+    oneway void connect(String callingPackage, IMediaSession2Callback callback);
+    oneway void release(IMediaSession2Callback caller);
+
+    //////////////////////////////////////////////////////////////////////////////////////////////
+    // send command
+    //////////////////////////////////////////////////////////////////////////////////////////////
+    oneway void sendCommand(IMediaSession2Callback caller, in Bundle command, in Bundle args);
+
+    PlaybackState getPlaybackState();
+
+    //////////////////////////////////////////////////////////////////////////////////////////////
+    // Callbacks -- remove them
+    //////////////////////////////////////////////////////////////////////////////////////////////
+    /**
+     * @param callbackBinder binder to be used to notify changes.
+     * @param callbackFlag one of {@link MediaController2#FLAG_CALLBACK_PLAYBACK} or
+     *     {@link MediaController2#FLAG_CALLBACK_SESSION_ACTIVENESS}
+     * @param requestCode If >= 0, this code will be called back by the callback after the callback
+     *     is registered.
+     */
+    // TODO(jaewan): Due to the nature of the binder, calls can be called out of order.
+    //               Need a way to ensure calling of unregisterCallback unregisters later
+    //               registerCallback.
+    oneway void registerCallback(IMediaSession2Callback callbackBinder,
+            int callbackFlag, int requestCode);
+    oneway void unregisterCallback(IMediaSession2Callback callbackBinder, int callbackFlag);
+}
diff --git a/media/java/android/media/IMediaSession2Callback.aidl b/media/java/android/media/IMediaSession2Callback.aidl
new file mode 100644
index 0000000..0058e79
--- /dev/null
+++ b/media/java/android/media/IMediaSession2Callback.aidl
@@ -0,0 +1,47 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media;
+
+import android.os.Bundle;
+import android.media.session.PlaybackState;
+import android.media.IMediaSession2;
+
+/**
+ * Interface from MediaSession2 to MediaSession2Record.
+ * <p>
+ * Keep this interface oneway. Otherwise a malicious app may implement fake version of this,
+ * and holds calls from session to make session owner(s) frozen.
+ *
+ * @hide
+ */
+oneway interface IMediaSession2Callback {
+    void onPlaybackStateChanged(in PlaybackState state);
+
+    /**
+     * Called only when the controller is created with service's token.
+     *
+     * @param sessionBinder {@code null} if the connect is rejected or is disconnected. a session
+     *     binder if the connect is accepted.
+     * @param commands initially allowed commands.
+     */
+    // TODO(jaewan): Also need to pass flags for allowed actions for permission check.
+    //               For example, a media can allow setRating only for whitelisted apps
+    //               it's better for controller to know such information in advance.
+    //               Follow-up TODO: Add similar functions to the session.
+    // TODO(jaewan): Is term 'accepted/rejected' correct? For permission, 'grant' is used.
+    void onConnectionChanged(IMediaSession2 sessionBinder, in Bundle commandGroup);
+}
diff --git a/media/java/android/media/MediaController2.java b/media/java/android/media/MediaController2.java
new file mode 100644
index 0000000..550eee2
--- /dev/null
+++ b/media/java/android/media/MediaController2.java
@@ -0,0 +1,197 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.Context;
+import android.media.MediaSession2.CommandGroup;
+import android.media.MediaSession2.ControllerInfo;
+import android.media.session.MediaSessionManager;
+import android.media.session.PlaybackState;
+import android.media.update.ApiLoader;
+import android.media.update.MediaController2Provider;
+import android.os.Handler;
+import java.util.concurrent.Executor;
+
+/**
+ * Allows an app to interact with an active {@link MediaSession2} or a
+ * {@link MediaSessionService2} in any status. Media buttons and other commands can be sent to
+ * the session.
+ * <p>
+ * When you're done, use {@link #release()} to clean up resources. This also helps session service
+ * to be destroyed when there's no controller associated with it.
+ * <p>
+ * When controlling {@link MediaSession2}, the controller will be available immediately after
+ * the creation.
+ * <p>
+ * When controlling {@link MediaSessionService2}, the {@link MediaController2} would be
+ * available only if the session service allows this controller by
+ * {@link MediaSession2.SessionCallback#onConnect(ControllerInfo)} for the service. Wait
+ * {@link ControllerCallback#onConnected(CommandGroup)} or
+ * {@link ControllerCallback#onDisconnected()} for the result.
+ * <p>
+ * A controller can be created through token from {@link MediaSessionManager} if you hold the
+ * signature|privileged permission "android.permission.MEDIA_CONTENT_CONTROL" permission or are
+ * an enabled notification listener or by getting a {@link SessionToken} directly the
+ * the session owner.
+ * <p>
+ * MediaController2 objects are thread-safe.
+ * <p>
+ * @see MediaSession2
+ * @see MediaSessionService2
+ * @hide
+ */
+// TODO(jaewan): Unhide
+// TODO(jaewan): Revisit comments. Currently MediaBrowser case is missing.
+public class MediaController2 extends MediaPlayerBase {
+    /**
+     * Interface for listening to change in activeness of the {@link MediaSession2}.  It's
+     * active if and only if it has set a player.
+     */
+    public abstract static class ControllerCallback {
+        /**
+         * Called when the controller is successfully connected to the session. The controller
+         * becomes available afterwards.
+         *
+         * @param allowedCommands commands that's allowed by the session.
+         */
+        public void onConnected(CommandGroup allowedCommands) { }
+
+        /**
+         * Called when the session refuses the controller or the controller is disconnected from
+         * the session. The controller becomes unavailable afterwards and the callback wouldn't
+         * be called.
+         * <p>
+         * It will be also called after the {@link #release()}, so you can put clean up code here.
+         * You don't need to call {@link #release()} after this.
+         */
+        public void onDisconnected() { }
+    }
+
+    private final MediaController2Provider mProvider;
+
+    /**
+     * Create a {@link MediaController2} from the {@link SessionToken}. This connects to the session
+     * and may wake up the service if it's not available.
+     *
+     * @param context Context
+     * @param token token to connect to
+     * @param callback controller callback to receive changes in
+     * @param executor executor to run callbacks on.
+     */
+    // TODO(jaewan): Put @CallbackExecutor to the constructor.
+    public MediaController2(@NonNull Context context, @NonNull SessionToken token,
+            @NonNull ControllerCallback callback, @NonNull Executor executor) {
+        super();
+
+        // This also connects to the token.
+        // Explicit connect() isn't added on purpose because retrying connect() is impossible with
+        // session whose session binder is only valid while it's active.
+        // prevent a controller from reusable after the
+        // session is released and recreated.
+        mProvider = ApiLoader.getProvider(context)
+                .createMediaController2(this, context, token, callback, executor);
+    }
+
+    /**
+     * Release this object, and disconnect from the session. After this, callbacks wouldn't be
+     * received.
+     */
+    public void release() {
+        mProvider.release_impl();
+    }
+
+    /**
+     * @hide
+     */
+    public MediaController2Provider getProvider() {
+        return mProvider;
+    }
+
+    /**
+     * @return token
+     */
+    public @NonNull
+    SessionToken getSessionToken() {
+        return mProvider.getSessionToken_impl();
+    }
+
+    /**
+     * Returns whether this class is connected to active {@link MediaSession2} or not.
+     */
+    public boolean isConnected() {
+        return mProvider.isConnected_impl();
+    }
+
+    @Override
+    public void play() {
+        mProvider.play_impl();
+    }
+
+    @Override
+    public void pause() {
+        mProvider.pause_impl();
+    }
+
+    @Override
+    public void stop() {
+        mProvider.stop_impl();
+    }
+
+    @Override
+    public void skipToPrevious() {
+        mProvider.skipToPrevious_impl();
+    }
+
+    @Override
+    public void skipToNext() {
+        mProvider.skipToNext_impl();
+    }
+
+    @Override
+    public @Nullable PlaybackState getPlaybackState() {
+        return mProvider.getPlaybackState_impl();
+    }
+
+    /**
+     * Add a {@link PlaybackListener} to listen changes in the
+     * {@link MediaSession2}.
+     *
+     * @param listener the listener that will be run
+     * @param handler the Handler that will receive the listener
+     * @throws IllegalArgumentException Called when either the listener or handler is {@code null}.
+     */
+    // TODO(jaewan): Match with the addSessionAvailabilityListener() that tells the current state
+    //               through the listener.
+    // TODO(jaewan): Can handler be null? Follow the API guideline after it's finalized.
+    @Override
+    public void addPlaybackListener(@NonNull PlaybackListener listener, @NonNull Handler handler) {
+        mProvider.addPlaybackListener_impl(listener, handler);
+    }
+
+    /**
+     * Remove previously added {@link PlaybackListener}.
+     *
+     * @param listener the listener to be removed
+     * @throws IllegalArgumentException if the listener is {@code null}.
+     */
+    @Override
+    public void removePlaybackListener(@NonNull PlaybackListener listener) {
+        mProvider.removePlaybackListener_impl(listener);
+    }
+}
diff --git a/media/java/android/media/MediaPlayerBase.java b/media/java/android/media/MediaPlayerBase.java
new file mode 100644
index 0000000..980c70f
--- /dev/null
+++ b/media/java/android/media/MediaPlayerBase.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media;
+
+import android.media.session.PlaybackState;
+import android.os.Handler;
+
+/**
+ * Tentative interface for all media players that want media session.
+ * APIs are named to avoid conflicts with MediaPlayer APIs.
+ * All calls should be asynchrounous.
+ *
+ * @hide
+ */
+// TODO(wjia) Finalize the list of MediaPlayer2, which MediaPlayerBase's APIs will be come from.
+public abstract class MediaPlayerBase {
+    /**
+     * Listens change in {@link PlaybackState}.
+     */
+    public interface PlaybackListener {
+        /**
+         * Called when {@link PlaybackState} for this player is changed.
+         */
+        void onPlaybackChanged(PlaybackState state);
+    }
+
+    // TODO(jaewan): setDataSources()?
+    // TODO(jaewan): Add release() or do that in stop()?
+
+    // TODO(jaewan): Add set/getSupportedActions().
+    public abstract void play();
+    public abstract void pause();
+    public abstract void stop();
+    public abstract void skipToPrevious();
+    public abstract void skipToNext();
+
+    // Currently PlaybackState's error message is the content title (for testing only)
+    // TODO(jaewan): Add metadata support
+    public abstract PlaybackState getPlaybackState();
+
+    /**
+     * Add a {@link PlaybackListener} to be invoked when the playback state is changed.
+     *
+     * @param listener the listener that will be run
+     * @param handler the Handler that will receive the listener
+     */
+    public abstract void addPlaybackListener(PlaybackListener listener, Handler handler);
+
+    /**
+     * Remove previously added {@link PlaybackListener}.
+     *
+     * @param listener the listener to be removed
+     */
+    public abstract void removePlaybackListener(PlaybackListener listener);
+}
diff --git a/media/java/android/media/MediaRecorder.java b/media/java/android/media/MediaRecorder.java
index 3c49b80..78477f7 100644
--- a/media/java/android/media/MediaRecorder.java
+++ b/media/java/android/media/MediaRecorder.java
@@ -1380,7 +1380,8 @@
             if (listener != null && !mRoutingChangeListeners.containsKey(listener)) {
                 enableNativeRoutingCallbacksLocked(true);
                 mRoutingChangeListeners.put(
-                        listener, new NativeRoutingEventHandlerDelegate(this, listener, handler));
+                        listener, new NativeRoutingEventHandlerDelegate(this, listener,
+                                handler != null ? handler : mEventHandler));
             }
         }
     }
@@ -1401,36 +1402,6 @@
         }
     }
 
-    /**
-     * Helper class to handle the forwarding of native events to the appropriate listener
-     * (potentially) handled in a different thread
-     */
-    private class NativeRoutingEventHandlerDelegate {
-        private MediaRecorder mMediaRecorder;
-        private AudioRouting.OnRoutingChangedListener mOnRoutingChangedListener;
-        private Handler mHandler;
-
-        NativeRoutingEventHandlerDelegate(final MediaRecorder mediaRecorder,
-                final AudioRouting.OnRoutingChangedListener listener, Handler handler) {
-            mMediaRecorder = mediaRecorder;
-            mOnRoutingChangedListener = listener;
-            mHandler = handler != null ? handler : mEventHandler;
-        }
-
-        void notifyClient() {
-            if (mHandler != null) {
-                mHandler.post(new Runnable() {
-                    @Override
-                    public void run() {
-                        if (mOnRoutingChangedListener != null) {
-                            mOnRoutingChangedListener.onRoutingChanged(mMediaRecorder);
-                        }
-                    }
-                });
-            }
-        }
-    }
-
     private native final boolean native_setInputDevice(int deviceId);
     private native final int native_getRoutedDeviceId();
     private native final void native_enableDeviceCallback(boolean enabled);
diff --git a/media/java/android/media/MediaSession2.java b/media/java/android/media/MediaSession2.java
new file mode 100644
index 0000000..807535b
--- /dev/null
+++ b/media/java/android/media/MediaSession2.java
@@ -0,0 +1,607 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.Context;
+import android.media.session.MediaSession;
+import android.media.session.MediaSession.Callback;
+import android.media.session.PlaybackState;
+import android.media.update.ApiLoader;
+import android.media.update.MediaSession2Provider;
+import android.media.update.MediaSession2Provider.ControllerInfoProvider;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Parcelable;
+import android.os.Process;
+import android.text.TextUtils;
+import android.util.ArraySet;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Allows a media app to expose its transport controls and playback information in a process to
+ * other processes including the Android framework and other apps. Common use cases are as follows.
+ * <ul>
+ *     <li>Bluetooth/wired headset key events support</li>
+ *     <li>Android Auto/Wearable support</li>
+ *     <li>Separating UI process and playback process</li>
+ * </ul>
+ * <p>
+ * A MediaSession2 should be created when an app wants to publish media playback information or
+ * handle media keys. In general an app only needs one session for all playback, though multiple
+ * sessions can be created to provide finer grain controls of media.
+ * <p>
+ * If you want to support background playback, {@link MediaSessionService2} is preferred
+ * instead. With it, your playback can be revived even after you've finished playback. See
+ * {@link MediaSessionService2} for details.
+ * <p>
+ * A session can be obtained by {@link #getInstance(Context, Handler)}. The owner of the session may
+ * pass its session token to other processes to allow them to create a {@link MediaController2}
+ * to interact with the session.
+ * <p>
+ * To receive transport control commands, an underlying media player must be set with
+ * {@link #setPlayer(MediaPlayerBase)}. Commands will be sent to the underlying player directly
+ * on the thread that had been specified by {@link #getInstance(Context, Handler)}.
+ * <p>
+ * When an app is finished performing playback it must call
+ * {@link #setPlayer(MediaPlayerBase)} with {@code null} to clean up the session and notify any
+ * controllers. It's developers responsibility of cleaning the session and releasing resources.
+ * <p>
+ * MediaSession2 objects should be used on the handler's thread that is initially given by
+ * {@link #getInstance(Context, Handler)}.
+ *
+ * @see MediaSessionService2
+ * @hide
+ */
+// TODO(jaewan): Unhide
+// TODO(jaewan): Revisit comments. Currently it's borrowed from the MediaSession.
+// TODO(jaewan): Add explicit release(), and make token @NonNull. Session will be active while the
+//               session exists, and controllers will be invalidated when session becomes inactive.
+// TODO(jaewan): Should we support thread safe? It may cause tricky issue such as b/63797089
+// TODO(jaewan): Should we make APIs for MediaSessionService2 public? It's helpful for
+//               developers that doesn't want to override from Browser, but user may not use this
+//               correctly.
+public final class MediaSession2 extends MediaPlayerBase {
+    private final MediaSession2Provider mProvider;
+
+    // Note: Do not define IntDef because subclass can add more command code on top of these.
+    public static final int COMMAND_CODE_CUSTOM = 0;
+    public static final int COMMAND_CODE_PLAYBACK_START = 1;
+    public static final int COMMAND_CODE_PLAYBACK_PAUSE = 2;
+    public static final int COMMAND_CODE_PLAYBACK_STOP = 3;
+    public static final int COMMAND_CODE_PLAYBACK_SKIP_NEXT_ITEM = 4;
+    public static final int COMMAND_CODE_PLAYBACK_SKIP_PREV_ITEM = 5;
+
+    /**
+     * Define a command that a {@link MediaController2} can send to a {@link MediaSession2}.
+     * <p>
+     * If {@link #getCommandCode()} isn't {@link #COMMAND_CODE_CUSTOM}), it's predefined command.
+     * If {@link #getCommandCode()} is {@link #COMMAND_CODE_CUSTOM}), it's custom command and
+     * {@link #getCustomCommand()} shouldn't be {@code null}.
+     */
+    // TODO(jaewan): Move this into the updatable.
+    public static final class Command {
+        private static final String KEY_COMMAND_CODE
+                = "android.media.mediasession2.command.command_command";
+        private static final String KEY_COMMAND_CUSTOM_COMMAND
+                = "android.media.mediasession2.command.custom_command";
+        private static final String KEY_COMMAND_EXTRA
+                = "android.media.mediasession2.command.extra";
+
+        private final int mCommandCode;
+        // Nonnull if it's custom command
+        private final String mCustomCommand;
+        private final Bundle mExtra;
+
+        public Command(int commandCode) {
+            mCommandCode = commandCode;
+            mCustomCommand = null;
+            mExtra = null;
+        }
+
+        public Command(@NonNull String action, @Nullable Bundle extra) {
+            if (action == null) {
+                throw new IllegalArgumentException("action shouldn't be null");
+            }
+            mCommandCode = COMMAND_CODE_CUSTOM;
+            mCustomCommand = action;
+            mExtra = extra;
+        }
+
+        public int getCommandCode() {
+            return mCommandCode;
+        }
+
+        public @Nullable String getCustomCommand() {
+            return mCustomCommand;
+        }
+
+        public @Nullable Bundle getExtra() {
+            return mExtra;
+        }
+
+        /**
+         * @return a new Bundle instance from the Command
+         * @hide
+         */
+        public Bundle toBundle() {
+            Bundle bundle = new Bundle();
+            bundle.putInt(KEY_COMMAND_CODE, mCommandCode);
+            bundle.putString(KEY_COMMAND_CUSTOM_COMMAND, mCustomCommand);
+            bundle.putBundle(KEY_COMMAND_EXTRA, mExtra);
+            return bundle;
+        }
+
+        /**
+         * @return a new Command instance from the Bundle
+         * @hide
+         */
+        public static Command fromBundle(Bundle command) {
+            int code = command.getInt(KEY_COMMAND_CODE);
+            if (code != COMMAND_CODE_CUSTOM) {
+                return new Command(code);
+            } else {
+                String customCommand = command.getString(KEY_COMMAND_CUSTOM_COMMAND);
+                if (customCommand == null) {
+                    return null;
+                }
+                return new Command(customCommand, command.getBundle(KEY_COMMAND_EXTRA));
+            }
+        }
+
+        @Override
+        public boolean equals(Object obj) {
+            if (!(obj instanceof Command)) {
+                return false;
+            }
+            Command other = (Command) obj;
+            // TODO(jaewan): Should we also compare contents in bundle?
+            //               It may not be possible if the bundle contains private class.
+            return mCommandCode == other.mCommandCode
+                    && TextUtils.equals(mCustomCommand, other.mCustomCommand);
+        }
+
+        @Override
+        public int hashCode() {
+            final int prime = 31;
+            return ((mCustomCommand != null) ? mCustomCommand.hashCode() : 0) * prime + mCommandCode;
+        }
+    }
+
+    /**
+     * Represent set of {@link Command}.
+     */
+    // TODO(jaewan): Move this to updatable
+    public static class CommandGroup {
+        private static final String KEY_COMMANDS =
+                "android.media.mediasession2.commandgroup.commands";
+        private ArraySet<Command> mCommands = new ArraySet<>();
+
+        public CommandGroup() {
+        }
+
+        public CommandGroup(CommandGroup others) {
+            mCommands.addAll(others.mCommands);
+        }
+
+        public void addCommand(Command command) {
+            mCommands.add(command);
+        }
+
+        public void addAllPredefinedCommands() {
+            // TODO(jaewan): Is there any better way than this?
+            mCommands.add(new Command(COMMAND_CODE_PLAYBACK_START));
+            mCommands.add(new Command(COMMAND_CODE_PLAYBACK_PAUSE));
+            mCommands.add(new Command(COMMAND_CODE_PLAYBACK_STOP));
+            mCommands.add(new Command(COMMAND_CODE_PLAYBACK_SKIP_NEXT_ITEM));
+            mCommands.add(new Command(COMMAND_CODE_PLAYBACK_SKIP_PREV_ITEM));
+        }
+
+        public void removeCommand(Command command) {
+            mCommands.remove(command);
+        }
+
+        public boolean hasCommand(Command command) {
+            return mCommands.contains(command);
+        }
+
+        public boolean hasCommand(int code) {
+            if (code == COMMAND_CODE_CUSTOM) {
+                throw new IllegalArgumentException("Use hasCommand(Command) for custom command");
+            }
+            for (int i = 0; i < mCommands.size(); i++) {
+                if (mCommands.valueAt(i).getCommandCode() == code) {
+                    return true;
+                }
+            }
+            return false;
+        }
+
+        /**
+         * @return new bundle from the CommandGroup
+         * @hide
+         */
+        public Bundle toBundle() {
+            ArrayList<Bundle> list = new ArrayList<>();
+            for (int i = 0; i < mCommands.size(); i++) {
+                list.add(mCommands.valueAt(i).toBundle());
+            }
+            Bundle bundle = new Bundle();
+            bundle.putParcelableArrayList(KEY_COMMANDS, list);
+            return bundle;
+        }
+
+        /**
+         * @return new instance of CommandGroup from the bundle
+         * @hide
+         */
+        public static @Nullable CommandGroup fromBundle(Bundle commands) {
+            if (commands == null) {
+                return null;
+            }
+            List<Parcelable> list = commands.getParcelableArrayList(KEY_COMMANDS);
+            if (list == null) {
+                return null;
+            }
+            CommandGroup commandGroup = new CommandGroup();
+            for (int i = 0; i < list.size(); i++) {
+                Parcelable parcelable = list.get(i);
+                if (!(parcelable instanceof Bundle)) {
+                    continue;
+                }
+                Bundle commandBundle = (Bundle) parcelable;
+                Command command = Command.fromBundle(commandBundle);
+                if (command != null) {
+                    commandGroup.addCommand(command);
+                }
+            }
+            return commandGroup;
+        }
+    }
+
+    /**
+     * Callback to be called for all incoming commands from {@link MediaController2}s.
+     * <p>
+     * If it's not set, the session will accept all controllers and all incoming commands by
+     * default.
+     */
+    // TODO(jaewan): Can we move this inside of the updatable for default implementation.
+    public static class SessionCallback {
+        /**
+         * Called when a controller is created for this session. Return allowed commands for
+         * controller. By default it allows all connection requests and commands.
+         * <p>
+         * You can reject the connection by return {@code null}. In that case, controller receives
+         * {@link MediaController2.ControllerCallback#onDisconnected()} and cannot be usable.
+         *
+         * @param controller controller information.
+         * @return allowed commands. Can be {@code null} to reject coonnection.
+         */
+        // TODO(jaewan): Change return type. Once we do, null is for reject.
+        public @Nullable CommandGroup onConnect(@NonNull ControllerInfo controller) {
+            CommandGroup commands = new CommandGroup();
+            commands.addAllPredefinedCommands();
+            return commands;
+        }
+
+        /**
+         * Called when a controller sent a command to the session. You can also reject the request
+         * by return {@code false}.
+         * <p>
+         * This method will be called on the session handler.
+         *
+         * @param controller controller information.
+         * @param command a command. This method will be called for every single command.
+         * @return {@code true} if you want to accept incoming command. {@code false} otherwise.
+         */
+        public boolean onCommandRequest(@NonNull ControllerInfo controller,
+                @NonNull Command command) {
+            return true;
+        }
+    };
+
+    /**
+     * Builder for {@link MediaSession2}.
+     * <p>
+     * Any incoming event from the {@link MediaController2} will be handled on the thread
+     * that created session with the {@link Builder#build()}.
+     */
+    // TODO(jaewan): Move this to updatable
+    // TODO(jaewan): Add setRatingType()
+    // TODO(jaewan): Add setSessionActivity()
+    public final static class Builder {
+        private final Context mContext;
+        private final MediaPlayerBase mPlayer;
+        private String mId;
+        private SessionCallback mCallback;
+
+        /**
+         * Constructor.
+         *
+         * @param context a context
+         * @param player a player to handle incoming command from any controller.
+         * @throws IllegalArgumentException if any parameter is null, or the player is a
+         *      {@link MediaSession2} or {@link MediaController2}.
+         */
+        public Builder(@NonNull Context context, @NonNull MediaPlayerBase player) {
+            if (context == null) {
+                throw new IllegalArgumentException("context shouldn't be null");
+            }
+            if (player == null) {
+                throw new IllegalArgumentException("player shouldn't be null");
+            }
+            if (player instanceof MediaSession2 || player instanceof MediaController2) {
+                throw new IllegalArgumentException("player doesn't accept MediaSession2 nor"
+                        + " MediaController2");
+            }
+            mContext = context;
+            mPlayer = player;
+            // Ensure non-null
+            mId = "";
+        }
+
+        /**
+         * Set ID of the session. If it's not set, an empty string with used to create a session.
+         * <p>
+         * Use this if and only if your app supports multiple playback at the same time and also
+         * wants to provide external apps to have finer controls of them.
+         *
+         * @param id id of the session. Must be unique per package.
+         * @throws IllegalArgumentException if id is {@code null}
+         * @return
+         */
+        public Builder setId(@NonNull String id) {
+            if (id == null) {
+                throw new IllegalArgumentException("id shouldn't be null");
+            }
+            mId = id;
+            return this;
+        }
+
+        /**
+         * Set {@link SessionCallback}.
+         *
+         * @param callback session callback.
+         * @return
+         */
+        public Builder setSessionCallback(@Nullable SessionCallback callback) {
+            mCallback = callback;
+            return this;
+        }
+
+        /**
+         * Build {@link MediaSession2}.
+         *
+         * @return a new session
+         * @throws IllegalStateException if the session with the same id is already exists for the
+         *      package.
+         */
+        public MediaSession2 build() throws IllegalStateException {
+            if (mCallback == null) {
+                mCallback = new SessionCallback();
+            }
+            return new MediaSession2(mContext, mPlayer, mId, mCallback);
+        }
+    }
+
+    /**
+     * Information of a controller.
+     */
+    // TODO(jaewan): Move implementation to the updatable.
+    public static final class ControllerInfo {
+        private final ControllerInfoProvider mProvider;
+
+        /**
+         * @hide
+         */
+        // TODO(jaewan): SystemApi
+        // TODO(jaewan): Also accept componentName to check notificaiton listener.
+        public ControllerInfo(Context context, int uid, int pid, String packageName,
+                IMediaSession2Callback callback) {
+            mProvider = ApiLoader.getProvider(context)
+                    .createMediaSession2ControllerInfoProvider(
+                            this, context, uid, pid, packageName, callback);
+        }
+
+        /**
+         * @return package name of the controller
+         */
+        public String getPackageName() {
+            return mProvider.getPackageName_impl();
+        }
+
+        /**
+         * @return uid of the controller
+         */
+        public int getUid() {
+            return mProvider.getUid_impl();
+        }
+
+        /**
+         * Return if the controller has granted {@code android.permission.MEDIA_CONTENT_CONTROL} or
+         * has a enabled notification listener so can be trusted to accept connection and incoming
+         * command request.
+         *
+         * @return {@code true} if the controller is trusted.
+         */
+        public boolean isTrusted() {
+            return mProvider.isTrusted_impl();
+        }
+
+        /**
+         * @hide
+         * @return
+         */
+        // TODO(jaewan): SystemApi
+        public ControllerInfoProvider getProvider() {
+            return mProvider;
+        }
+
+        @Override
+        public int hashCode() {
+            return mProvider.hashCode_impl();
+        }
+
+        @Override
+        public boolean equals(Object obj) {
+            if (!(obj instanceof ControllerInfo)) {
+                return false;
+            }
+            ControllerInfo other = (ControllerInfo) obj;
+            return mProvider.equals_impl(other.mProvider);
+        }
+
+        @Override
+        public String toString() {
+            return "ControllerInfo {pkg=" + getPackageName() + ", uid=" + getUid() + ", trusted="
+                    + isTrusted() + "}";
+        }
+    }
+
+    /**
+     * Constructor is hidden and apps can only instantiate indirectly through {@link Builder}.
+     * <p>
+     * This intended behavior and here's the reasons.
+     *    1. Prevent multiple sessions with the same tag in a media app.
+     *       Whenever it happens only one session was properly setup and others were all dummies.
+     *       Android framework couldn't find the right session to dispatch media key event.
+     *    2. Simplify session's lifecycle.
+     *       {@link MediaSession} can be available after all of {@link MediaSession#setFlags(int)},
+     *       {@link MediaSession#setCallback(Callback)}, and
+     *       {@link MediaSession#setActive(boolean)}. It was common for an app to omit one, so
+     *       framework had to add heuristics to figure out if an app is
+     * @hide
+     */
+    private MediaSession2(Context context, MediaPlayerBase player, String id,
+            SessionCallback callback) {
+        super();
+        mProvider = ApiLoader.getProvider(context)
+                .createMediaSession2(this, context, player, id, callback);
+    }
+
+    /**
+     * @hide
+     */
+    // TODO(jaewan): SystemApi
+    public MediaSession2Provider getProvider() {
+        return mProvider;
+    }
+
+    /**
+     * Set the underlying {@link MediaPlayerBase} for this session to dispatch incoming event to.
+     * Events from the {@link MediaController2} will be sent directly to the underlying
+     * player on the {@link Handler} where the session is created on.
+     * <p>
+     * If the new player is successfully set, {@link PlaybackListener}
+     * will be called to tell the current playback state of the new player.
+     * <p>
+     * Calling this method with {@code null} will disconnect binding connection between the
+     * controllers and also release this object.
+     *
+     * @param player a {@link MediaPlayerBase} that handles actual media playback in your app.
+     *      It shouldn't be {@link MediaSession2} nor {@link MediaController2}.
+     * @throws IllegalArgumentException if the player is either {@link MediaSession2}
+     *      or {@link MediaController2}.
+     */
+    // TODO(jaewan): Add release instead of setPlayer(null).
+    public void setPlayer(MediaPlayerBase player) throws IllegalArgumentException {
+        mProvider.setPlayer_impl(player);
+    }
+
+    /**
+     * @return player
+     */
+    public @Nullable MediaPlayerBase getPlayer() {
+        return mProvider.getPlayer_impl();
+    }
+
+    /**
+     * Returns the {@link SessionToken} for creating {@link MediaController2}.
+     */
+    public @NonNull
+    SessionToken getToken() {
+        return mProvider.getToken_impl();
+    }
+
+    public @NonNull List<ControllerInfo> getConnectedControllers() {
+        return mProvider.getConnectedControllers_impl();
+    }
+
+    @Override
+    public void play() {
+        mProvider.play_impl();
+    }
+
+    @Override
+    public void pause() {
+        mProvider.pause_impl();
+    }
+
+    @Override
+    public void stop() {
+        mProvider.stop_impl();
+    }
+
+    @Override
+    public void skipToPrevious() {
+        mProvider.skipToPrevious_impl();
+    }
+
+    @Override
+    public void skipToNext() {
+        mProvider.skipToNext_impl();
+    }
+
+    @Override
+    public @NonNull PlaybackState getPlaybackState() {
+        return mProvider.getPlaybackState_impl();
+    }
+
+    /**
+     * Add a {@link PlaybackListener} to listen changes in the
+     * underlying {@link MediaPlayerBase} which is previously set by
+     * {@link #setPlayer(MediaPlayerBase)}.
+     * <p>
+     * Added listeners will be also called when the underlying player is changed.
+     *
+     * @param listener the listener that will be run
+     * @param handler the Handler that will receive the listener
+     * @throws IllegalArgumentException when either the listener or handler is {@code null}.
+     */
+    // TODO(jaewan): Can handler be null? Follow API guideline after it's finalized.
+    @Override
+    public void addPlaybackListener(@NonNull PlaybackListener listener, @NonNull Handler handler) {
+        mProvider.addPlaybackListener_impl(listener, handler);
+    }
+
+    /**
+     * Remove previously added {@link PlaybackListener}.
+     *
+     * @param listener the listener to be removed
+     * @throws IllegalArgumentException if the listener is {@code null}.
+     */
+    @Override
+    public void removePlaybackListener(PlaybackListener listener) {
+        mProvider.removePlaybackListener_impl(listener);
+    }
+}
diff --git a/media/java/android/media/MediaSessionService2.java b/media/java/android/media/MediaSessionService2.java
new file mode 100644
index 0000000..f1f5467
--- /dev/null
+++ b/media/java/android/media/MediaSessionService2.java
@@ -0,0 +1,264 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media;
+
+import android.annotation.CallSuper;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.app.Notification;
+import android.app.Service;
+import android.content.Intent;
+import android.media.MediaSession2.ControllerInfo;
+import android.media.session.PlaybackState;
+import android.media.update.ApiLoader;
+import android.media.update.MediaSessionService2Provider;
+import android.os.IBinder;
+
+/**
+ * Service version of the {@link MediaSession2}.
+ * <p>
+ * It's highly recommended for an app to use this instead of {@link MediaSession2} if it wants
+ * to keep media playback in the background.
+ * <p>
+ * Here's the benefits of using {@link MediaSessionService2} instead of
+ * {@link MediaSession2}.
+ * <ul>
+ * <li>Another app can know that your app supports {@link MediaSession2} even when your app
+ * isn't running.
+ * <li>Another app can start playback of your app even when your app isn't running.
+ * </ul>
+ * For example, user's voice command can start playback of your app even when it's not running.
+ * <p>
+ * To use this class, adding followings directly to your {@code AndroidManifest.xml}.
+ * <pre>
+ * &lt;service android:name="component_name_of_your_implementation" &gt;
+ *   &lt;intent-filter&gt;
+ *     &lt;action android:name="android.media.session.MediaSessionService2" /&gt;
+ *   &lt;/intent-filter&gt;
+ * &lt;/service&gt;</pre>
+ * <p>
+ * A {@link MediaSessionService2} is another form of {@link MediaSession2}. IDs shouldn't
+ * be shared between the {@link MediaSessionService2} and {@link MediaSession2}. By
+ * default, an empty string will be used for ID of the service. If you want to specify an ID,
+ * declare metadata in the manifest as follows.
+ * <pre>
+ * &lt;service android:name="component_name_of_your_implementation" &gt;
+ *   &lt;intent-filter&gt;
+ *     &lt;action android:name="android.media.session.MediaSessionService2" /&gt;
+ *   &lt;/intent-filter&gt;
+ *   &lt;meta-data android:name="android.media.session"
+ *       android:value="session_id"/&gt;
+ * &lt;/service&gt;</pre>
+ * <p>
+ * It's recommended for an app to have a single {@link MediaSessionService2} declared in the
+ * manifest. Otherwise, your app might be shown twice in the list of the Auto/Wearable, or another
+ * app fails to pick the right session service when it wants to start the playback this app.
+ * <p>
+ * If there's conflicts with the session ID among the services, services wouldn't be available for
+ * any controllers.
+ * <p>
+ * Topic covered here:
+ * <ol>
+ * <li><a href="#ServiceLifecycle">Service Lifecycle</a>
+ * <li><a href="#Permissions">Permissions</a>
+ * </ol>
+ * <div class="special reference">
+ * <a name="ServiceLifecycle"></a>
+ * <h3>Service Lifecycle</h3>
+ * <p>
+ * Session service is bounded service. When a {@link MediaController2} is created for the
+ * session service, the controller binds to the session service. {@link #onCreateSession(String)}
+ * may be called after the {@link #onCreate} if the service hasn't created yet.
+ * <p>
+ * After the binding, session's {@link MediaSession2.SessionCallback#onConnect(ControllerInfo)}
+ * will be called to accept or reject connection request from a controller. If the connection is
+ * rejected, the controller will unbind. If it's accepted, the controller will be available to use
+ * and keep binding.
+ * <p>
+ * When playback is started for this session service, {@link #onUpdateNotification(PlaybackState)}
+ * is called and service would become a foreground service. It's needed to keep playback after the
+ * controller is destroyed. The session service becomes background service when the playback is
+ * stopped.
+ * <a name="Permissions"></a>
+ * <h3>Permissions</h3>
+ * <p>
+ * Any app can bind to the session service with controller, but the controller can be used only if
+ * the session service accepted the connection request through
+ * {@link MediaSession2.SessionCallback#onConnect(ControllerInfo)}.
+ *
+ * @hide
+ */
+// TODO(jaewan): Unhide
+// TODO(jaewan): Can we clean up sessions in onDestroy() automatically instead?
+//               What about currently running SessionCallback when the onDestroy() is called?
+// TODO(jaewan): Protect this with system|privilleged permission - Q.
+// TODO(jaewan): Add permission check for the service to know incoming connection request.
+//               Follow-up questions: What about asking a XML for list of white/black packages for
+//                                    allowing enumeration?
+//                                    We can read the information even when the service is started,
+//                                    so SessionManager.getXXXXService() can only return apps
+//                                    TODO(jaewan): Will be the black/white listing persistent?
+//                                                  In other words, can we cache the rejection?
+public abstract class MediaSessionService2 extends Service {
+    private final MediaSessionService2Provider mProvider;
+
+    /**
+     * This is the interface name that a service implementing a session service should say that it
+     * support -- that is, this is the action it uses for its intent filter.
+     */
+    public static final String SERVICE_INTERFACE =
+            "android.media.session.MediaSessionService2";
+
+    /**
+     * Name under which a MediaSessionService2 component publishes information about itself.
+     * This meta-data must provide a string value for the ID.
+     */
+    public static final String SERVICE_META_DATA = "android.media.session";
+
+    /**
+     * Default notification channel ID used by {@link #onUpdateNotification(PlaybackState)}.
+     *
+     */
+    public static final String DEFAULT_MEDIA_NOTIFICATION_CHANNEL_ID = "media_session_service";
+
+    /**
+     * Default notification channel ID used by {@link #onUpdateNotification(PlaybackState)}.
+     *
+     */
+    public static final int DEFAULT_MEDIA_NOTIFICATION_ID = 1001;
+
+    public MediaSessionService2() {
+        super();
+        mProvider = ApiLoader.getProvider(this).createMediaSessionService2(this);
+    }
+
+    /**
+     * Default implementation for {@link MediaSessionService2} to initialize session service.
+     * <p>
+     * Override this method if you need your own initialization. Derived classes MUST call through
+     * to the super class's implementation of this method.
+     */
+    @CallSuper
+    @Override
+    public void onCreate() {
+        super.onCreate();
+        mProvider.onCreate_impl();
+    }
+
+    /**
+     * Called when another app requested to start this service to get {@link MediaSession2}.
+     * <p>
+     * Session service will accept or reject the connection with the
+     * {@link MediaSession2.SessionCallback} in the created session.
+     * <p>
+     * Service wouldn't run if {@code null} is returned or session's ID doesn't match with the
+     * expected ID that you've specified through the AndroidManifest.xml.
+     * <p>
+     * This method will be call on the main thread.
+     *
+     * @param sessionId session id written in the AndroidManifest.xml.
+     * @return a new session
+     * @see MediaSession2.Builder
+     * @see #getSession()
+     */
+    // TODO(jaewan): Replace this with onCreateSession(). Its sesssion callback will replace
+    //               this abstract method.
+    // TODO(jaewan): Should we also include/documents notification listener access?
+    // TODO(jaewan): Is term accepted/rejected correct? For permission, granted is more common.
+    // TODO(jaewan): Return ConnectResult() that encapsulate supported action and player.
+    public @NonNull abstract MediaSession2 onCreateSession(String sessionId);
+
+    /**
+     * Called when the playback state of this session is changed, and notification needs update.
+     * <p>
+     * Default media style notification will be shown if you don't override this or return
+     * {@code null}. Override this method to show your own notification UI.
+     * <p>
+     * With the notification returned here, the service become foreground service when the playback
+     * is started. It becomes background service after the playback is stopped.
+     *
+     * @param state playback state
+     * @return a {@link MediaNotification}. If it's {@code null}, default notification will be shown
+     *     instead.
+     */
+    // TODO(jaewan): Also add metadata
+    public MediaNotification onUpdateNotification(PlaybackState state) {
+        return mProvider.onUpdateNotification_impl(state);
+    }
+
+    /**
+     * Get instance of the {@link MediaSession2} that you've previously created with the
+     * {@link #onCreateSession} for this service.
+     *
+     * @return created session
+     */
+    public final MediaSession2 getSession() {
+        return mProvider.getSession_impl();
+    }
+
+    /**
+     * Default implementation for {@link MediaSessionService2} to handle incoming binding
+     * request. If the request is for getting the session, the intent will have action
+     * {@link #SERVICE_INTERFACE}.
+     * <p>
+     * Override this method if this service also needs to handle binder requests other than
+     * {@link #SERVICE_INTERFACE}. Derived classes MUST call through to the super class's
+     * implementation of this method.
+     *
+     * @param intent
+     * @return Binder
+     */
+    @CallSuper
+    @Nullable
+    @Override
+    public IBinder onBind(Intent intent) {
+        return mProvider.onBind_impl(intent);
+    }
+
+    /**
+     * Returned by {@link #onUpdateNotification(PlaybackState)} for making session service
+     * foreground service to keep playback running in the background. It's highly recommended to
+     * show media style notification here.
+     */
+    // TODO(jaewan): Should we also move this to updatable?
+    public static class MediaNotification {
+        public final int id;
+        public final Notification notification;
+
+        private MediaNotification(int id, @NonNull Notification notification) {
+            this.id = id;
+            this.notification = notification;
+        }
+
+        /**
+         * Create a {@link MediaNotification}.
+         *
+         * @param notificationId notification id to be used for
+         *      {@link android.app.NotificationManager#notify(int, Notification)}.
+         * @param notification a notification to make session service foreground service. Media
+         *      style notification is recommended here.
+         * @return
+         */
+        public static MediaNotification create(int notificationId,
+                @NonNull Notification notification) {
+            if (notification == null) {
+                throw new IllegalArgumentException("Notification cannot be null");
+            }
+            return new MediaNotification(notificationId, notification);
+        }
+    }
+}
diff --git a/media/java/android/media/SessionToken.java b/media/java/android/media/SessionToken.java
new file mode 100644
index 0000000..b470dea
--- /dev/null
+++ b/media/java/android/media/SessionToken.java
@@ -0,0 +1,223 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.media.session.MediaSessionManager;
+import android.os.Bundle;
+import android.os.IBinder;
+import android.text.TextUtils;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * Represents an ongoing {@link MediaSession2} or a {@link MediaSessionService2}.
+ * If it's representing a session service, it may not be ongoing.
+ * <p>
+ * This may be passed to apps by the session owner to allow them to create a
+ * {@link MediaController2} to communicate with the session.
+ * <p>
+ * It can be also obtained by {@link MediaSessionManager}.
+ * @hide
+ */
+// TODO(jaewan): Unhide. SessionToken2?
+// TODO(jaewan): Move Token to updatable!
+// TODO(jaewan): Find better name for this (SessionToken or Session2Token)
+public final class SessionToken {
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(value = {TYPE_SESSION, TYPE_SESSION_SERVICE})
+    public @interface TokenType {
+    }
+
+    public static final int TYPE_SESSION = 0;
+    public static final int TYPE_SESSION_SERVICE = 1;
+
+    private static final String KEY_TYPE = "android.media.token.type";
+    private static final String KEY_PACKAGE_NAME = "android.media.token.package_name";
+    private static final String KEY_SERVICE_NAME = "android.media.token.service_name";
+    private static final String KEY_ID = "android.media.token.id";
+    private static final String KEY_SESSION_BINDER = "android.media.token.session_binder";
+
+    private final @TokenType int mType;
+    private final String mPackageName;
+    private final String mServiceName;
+    private final String mId;
+    private final IMediaSession2 mSessionBinder;
+
+    /**
+     * Constructor for the token.
+     *
+     * @hide
+     * @param type type
+     * @param packageName package name
+     * @param id id
+     * @param serviceName name of service. Can be {@code null} if it's not an service.
+     * @param sessionBinder binder for this session. Can be {@code null} if it's service.
+     * @hide
+     */
+    // TODO(jaewan): UID is also needed.
+    public SessionToken(@TokenType int type, @NonNull String packageName, @NonNull String id,
+            @Nullable String serviceName, @Nullable IMediaSession2 sessionBinder) {
+        // TODO(jaewan): Add sanity check.
+        mType = type;
+        mPackageName = packageName;
+        mId = id;
+        mServiceName = serviceName;
+        mSessionBinder = sessionBinder;
+    }
+
+    public int hashCode() {
+        final int prime = 31;
+        return mType
+                + prime * (mPackageName.hashCode()
+                + prime * (mId.hashCode()
+                + prime * ((mServiceName != null ? mServiceName.hashCode() : 0)
+                + prime * (mSessionBinder != null ? mSessionBinder.asBinder().hashCode() : 0))));
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj)
+            return true;
+        if (obj == null)
+            return false;
+        if (getClass() != obj.getClass())
+            return false;
+        SessionToken other = (SessionToken) obj;
+        if (!mPackageName.equals(other.getPackageName())
+                || !mServiceName.equals(other.getServiceName())
+                || !mId.equals(other.getId())
+                || mType != other.getType()) {
+            return false;
+        }
+        if (mSessionBinder == other.getSessionBinder()) {
+            return true;
+        } else if (mSessionBinder == null || other.getSessionBinder() == null) {
+            return false;
+        }
+        return mSessionBinder.asBinder().equals(other.getSessionBinder().asBinder());
+    }
+
+    @Override
+    public String toString() {
+        return "SessionToken {pkg=" + mPackageName + " id=" + mId + " type=" + mType
+                + " service=" + mServiceName + " binder=" + mSessionBinder + "}";
+    }
+
+    /**
+     * @return package name
+     */
+    public String getPackageName() {
+        return mPackageName;
+    }
+
+    /**
+     * @return id
+     */
+    public String getId() {
+        return mId;
+    }
+
+    /**
+     * @return type of the token
+     * @see #TYPE_SESSION
+     * @see #TYPE_SESSION_SERVICE
+     */
+    public @TokenType int getType() {
+        return mType;
+    }
+
+    /**
+     * @return session binder.
+     * @hide
+     */
+    public @Nullable IMediaSession2 getSessionBinder() {
+        return mSessionBinder;
+    }
+
+    /**
+     * @return service name if it's session service.
+     * @hide
+     */
+    public @Nullable String getServiceName() {
+        return mServiceName;
+    }
+
+    /**
+     * Create a token from the bundle, exported by {@link #toBundle()}.
+     *
+     * @param bundle
+     * @return
+     */
+    public static SessionToken fromBundle(@NonNull Bundle bundle) {
+        if (bundle == null) {
+            return null;
+        }
+        final @TokenType int type = bundle.getInt(KEY_TYPE, -1);
+        final String packageName = bundle.getString(KEY_PACKAGE_NAME);
+        final String serviceName = bundle.getString(KEY_SERVICE_NAME);
+        final String id = bundle.getString(KEY_ID);
+        final IBinder sessionBinder = bundle.getBinder(KEY_SESSION_BINDER);
+
+        // Sanity check.
+        switch (type) {
+            case TYPE_SESSION:
+                if (!(sessionBinder instanceof IMediaSession2)) {
+                    throw new IllegalArgumentException("Session needs sessionBinder");
+                }
+                break;
+            case TYPE_SESSION_SERVICE:
+                if (TextUtils.isEmpty(serviceName)) {
+                    throw new IllegalArgumentException("Session service needs service name");
+                }
+                if (sessionBinder != null && !(sessionBinder instanceof IMediaSession2)) {
+                    throw new IllegalArgumentException("Invalid session binder");
+                }
+                break;
+            default:
+                throw new IllegalArgumentException("Invalid type");
+        }
+        if (TextUtils.isEmpty(packageName) || id == null) {
+            throw new IllegalArgumentException("Package name nor ID cannot be null.");
+        }
+        // TODO(jaewan): Revisit here when we add connection callback to the session for individual
+        //               controller's permission check. With it, sessionBinder should be available
+        //               if and only if for session, not session service.
+        return new SessionToken(type, packageName, id, serviceName,
+                sessionBinder != null ? IMediaSession2.Stub.asInterface(sessionBinder) : null);
+    }
+
+    /**
+     * Create a {@link Bundle} from this token to share it across processes.
+     *
+     * @return Bundle
+     * @hide
+     */
+    public Bundle toBundle() {
+        Bundle bundle = new Bundle();
+        bundle.putString(KEY_PACKAGE_NAME, mPackageName);
+        bundle.putString(KEY_SERVICE_NAME, mServiceName);
+        bundle.putString(KEY_ID, mId);
+        bundle.putInt(KEY_TYPE, mType);
+        bundle.putBinder(KEY_SESSION_BINDER,
+                mSessionBinder != null ? mSessionBinder.asBinder() : null);
+        return bundle;
+    }
+}
diff --git a/media/java/android/media/session/ISessionManager.aidl b/media/java/android/media/session/ISessionManager.aidl
index 5fcb430..b8463dd 100644
--- a/media/java/android/media/session/ISessionManager.aidl
+++ b/media/java/android/media/session/ISessionManager.aidl
@@ -17,6 +17,7 @@
 
 import android.content.ComponentName;
 import android.media.IRemoteVolumeController;
+import android.media.IMediaSession2;
 import android.media.session.IActiveSessionsListener;
 import android.media.session.ICallback;
 import android.media.session.IOnMediaKeyListener;
@@ -49,4 +50,8 @@
     void setCallback(in ICallback callback);
     void setOnVolumeKeyLongPressListener(in IOnVolumeKeyLongPressListener listener);
     void setOnMediaKeyListener(in IOnMediaKeyListener listener);
+
+    // MediaSession2
+    Bundle createSessionToken(String callingPackage, String id, IMediaSession2 binder);
+    List<Bundle> getSessionTokens(boolean activeSessionOnly, boolean sessionServiceOnly);
 }
diff --git a/media/java/android/media/session/MediaSessionManager.java b/media/java/android/media/session/MediaSessionManager.java
index b215825..6a9f04a 100644
--- a/media/java/android/media/session/MediaSessionManager.java
+++ b/media/java/android/media/session/MediaSessionManager.java
@@ -24,8 +24,13 @@
 import android.content.ComponentName;
 import android.content.Context;
 import android.media.AudioManager;
+import android.media.IMediaSession2;
 import android.media.IRemoteVolumeController;
+import android.media.MediaSession2;
+import android.media.MediaSessionService2;
+import android.media.SessionToken;
 import android.media.session.ISessionManager;
+import android.os.Bundle;
 import android.os.Handler;
 import android.os.IBinder;
 import android.os.RemoteException;
@@ -38,6 +43,7 @@
 import android.view.KeyEvent;
 
 import java.util.ArrayList;
+import java.util.Collections;
 import java.util.List;
 
 /**
@@ -331,6 +337,101 @@
     }
 
     /**
+     * Called when a {@link MediaSession2} is created.
+     *
+     * @hide
+     */
+    // TODO(jaewan): System API
+    public SessionToken createSessionToken(@NonNull String callingPackage, @NonNull String id,
+            @NonNull IMediaSession2 binder) {
+        try {
+            Bundle bundle = mService.createSessionToken(callingPackage, id, binder);
+            return SessionToken.fromBundle(bundle);
+        } catch (RemoteException e) {
+            Log.wtf(TAG, "Cannot communicate with the service.", e);
+        }
+        return null;
+    }
+
+    /**
+     * Get {@link List} of {@link SessionToken} whose sessions are active now. This list represents
+     * active sessions regardless of whether they're {@link MediaSession2} or
+     * {@link MediaSessionService2}.
+     *
+     * @return list of Tokens
+     * @hide
+     */
+    // TODO(jaewan): Unhide
+    // TODO(jaewan): Protect this with permission.
+    // TODO(jaewna): Add listener for change in lists.
+    public List<SessionToken> getActiveSessionTokens() {
+        try {
+            List<Bundle> bundles = mService.getSessionTokens(
+                    /* activeSessionOnly */ true, /* sessionServiceOnly */ false);
+            return toTokenList(bundles);
+        } catch (RemoteException e) {
+            Log.wtf(TAG, "Cannot communicate with the service.", e);
+            return Collections.emptyList();
+        }
+    }
+
+    /**
+     * Get {@link List} of {@link SessionToken} for {@link MediaSessionService2} regardless of their
+     * activeness. This list represents media apps that support background playback.
+     *
+     * @return list of Tokens
+     * @hide
+     */
+    // TODO(jaewan): Unhide
+    // TODO(jaewna): Add listener for change in lists.
+    public List<SessionToken> getSessionServiceTokens() {
+        try {
+            List<Bundle> bundles = mService.getSessionTokens(
+                    /* activeSessionOnly */ false, /* sessionServiceOnly */ true);
+            return toTokenList(bundles);
+        } catch (RemoteException e) {
+            Log.wtf(TAG, "Cannot communicate with the service.", e);
+            return Collections.emptyList();
+        }
+    }
+
+    /**
+     * Get all {@link SessionToken}s. This is the combined list of {@link #getActiveSessionTokens()}
+     * and {@link #getSessionServiceTokens}.
+     *
+     * @return list of Tokens
+     * @see #getActiveSessionTokens
+     * @see #getSessionServiceTokens
+     * @hide
+     */
+    // TODO(jaewan): Unhide
+    // TODO(jaewan): Protect this with permission.
+    // TODO(jaewna): Add listener for change in lists.
+    public List<SessionToken> getAllSessionTokens() {
+        try {
+            List<Bundle> bundles = mService.getSessionTokens(
+                    /* activeSessionOnly */ false, /* sessionServiceOnly */ false);
+            return toTokenList(bundles);
+        } catch (RemoteException e) {
+            Log.wtf(TAG, "Cannot communicate with the service.", e);
+            return Collections.emptyList();
+        }
+    }
+
+    private static List<SessionToken> toTokenList(List<Bundle> bundles) {
+        List<SessionToken> tokens = new ArrayList<>();
+        if (bundles != null) {
+            for (int i = 0; i < bundles.size(); i++) {
+                SessionToken token = SessionToken.fromBundle(bundles.get(i));
+                if (token != null) {
+                    tokens.add(token);
+                }
+            }
+        }
+        return tokens;
+    }
+
+    /**
      * Check if the global priority session is currently active. This can be
      * used to decide if media keys should be sent to the session or to the app.
      *
diff --git a/media/java/android/media/update/MediaController2Provider.java b/media/java/android/media/update/MediaController2Provider.java
new file mode 100644
index 0000000..b15d6db
--- /dev/null
+++ b/media/java/android/media/update/MediaController2Provider.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.update;
+
+import android.media.SessionToken;
+
+/**
+ * @hide
+ */
+public interface MediaController2Provider extends MediaPlayerBaseProvider {
+    void release_impl();
+    SessionToken getSessionToken_impl();
+    boolean isConnected_impl();
+}
diff --git a/media/java/android/media/update/MediaPlayerBaseProvider.java b/media/java/android/media/update/MediaPlayerBaseProvider.java
new file mode 100644
index 0000000..5b13e74
--- /dev/null
+++ b/media/java/android/media/update/MediaPlayerBaseProvider.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.update;
+
+import android.media.MediaPlayerBase;
+import android.media.session.PlaybackState;
+import android.os.Handler;
+
+/**
+ * @hide
+ */
+public interface MediaPlayerBaseProvider {
+    void play_impl();
+    void pause_impl();
+    void stop_impl();
+    void skipToPrevious_impl();
+    void skipToNext_impl();
+
+    PlaybackState getPlaybackState_impl();
+    void addPlaybackListener_impl(MediaPlayerBase.PlaybackListener listener, Handler handler);
+    void removePlaybackListener_impl(MediaPlayerBase.PlaybackListener listener);
+}
diff --git a/media/java/android/media/update/MediaSession2Provider.java b/media/java/android/media/update/MediaSession2Provider.java
new file mode 100644
index 0000000..36fd182
--- /dev/null
+++ b/media/java/android/media/update/MediaSession2Provider.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.update;
+
+import android.media.MediaPlayerBase;
+import android.media.MediaSession2.ControllerInfo;
+import android.media.SessionToken;
+
+import java.util.List;
+
+/**
+ * @hide
+ */
+public interface MediaSession2Provider extends MediaPlayerBaseProvider {
+    void setPlayer_impl(MediaPlayerBase player) throws IllegalArgumentException;
+    MediaPlayerBase getPlayer_impl();
+    SessionToken getToken_impl();
+    List<ControllerInfo> getConnectedControllers_impl();
+
+    /**
+     * @hide
+     */
+    interface ControllerInfoProvider {
+        String getPackageName_impl();
+        int getUid_impl();
+        boolean isTrusted_impl();
+        int hashCode_impl();
+        boolean equals_impl(ControllerInfoProvider obj);
+    }
+}
diff --git a/media/java/android/media/update/MediaSessionService2Provider.java b/media/java/android/media/update/MediaSessionService2Provider.java
new file mode 100644
index 0000000..1174915
--- /dev/null
+++ b/media/java/android/media/update/MediaSessionService2Provider.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.update;
+
+import android.content.Intent;
+import android.media.MediaSession2;
+import android.media.MediaSessionService2.MediaNotification;
+import android.media.session.PlaybackState;
+import android.os.Handler;
+import android.os.IBinder;
+
+/**
+ * @hide
+ */
+public interface MediaSessionService2Provider {
+    MediaSession2 getSession_impl();
+    MediaNotification onUpdateNotification_impl(PlaybackState state);
+
+    // Service
+    void onCreate_impl();
+    IBinder onBind_impl(Intent intent);
+}
diff --git a/media/java/android/media/update/StaticProvider.java b/media/java/android/media/update/StaticProvider.java
index 1a0df52..91c9c66 100644
--- a/media/java/android/media/update/StaticProvider.java
+++ b/media/java/android/media/update/StaticProvider.java
@@ -17,11 +17,19 @@
 package android.media.update;
 
 import android.annotation.Nullable;
-import android.annotation.SystemApi;
+import android.content.Context;
+import android.media.IMediaSession2Callback;
+import android.media.MediaController2;
+import android.media.MediaPlayerBase;
+import android.media.MediaSession2;
+import android.media.MediaSessionService2;
+import android.media.SessionToken;
 import android.util.AttributeSet;
 import android.widget.MediaControlView2;
 import android.widget.VideoView2;
 
+import java.util.concurrent.Executor;
+
 /**
  * Interface for connecting the public API to an updatable implementation.
  *
@@ -37,4 +45,15 @@
     VideoView2Provider createVideoView2(
             VideoView2 instance, ViewProvider superProvider,
             @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes);
+
+    MediaSession2Provider createMediaSession2(MediaSession2 mediaSession2, Context context,
+            MediaPlayerBase player, String id, MediaSession2.SessionCallback callback);
+    MediaSession2Provider.ControllerInfoProvider createMediaSession2ControllerInfoProvider(
+            MediaSession2.ControllerInfo instance, Context context, int uid, int pid,
+            String packageName, IMediaSession2Callback callback);
+    MediaController2Provider createMediaController2(
+            MediaController2 instance, Context context, SessionToken token,
+            MediaController2.ControllerCallback callback, Executor executor);
+    MediaSessionService2Provider createMediaSessionService2(
+            MediaSessionService2 instance);
 }
diff --git a/media/java/android/media/update/ViewProvider.java b/media/java/android/media/update/ViewProvider.java
index e542404..78c5b36 100644
--- a/media/java/android/media/update/ViewProvider.java
+++ b/media/java/android/media/update/ViewProvider.java
@@ -37,6 +37,8 @@
 // TODO @SystemApi
 public interface ViewProvider {
     // TODO Add more (all?) methods from View
+    void onAttachedToWindow_impl();
+    void onDetachedFromWindow_impl();
     CharSequence getAccessibilityClassName_impl();
     boolean onTouchEvent_impl(MotionEvent ev);
     boolean onTrackballEvent_impl(MotionEvent ev);
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraDeviceBinderTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraDeviceBinderTest.java
index fadb76d..4c96d89 100644
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraDeviceBinderTest.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraDeviceBinderTest.java
@@ -197,7 +197,7 @@
         assertFalse(metadata.isEmpty());
 
         CaptureRequest.Builder request = new CaptureRequest.Builder(metadata, /*reprocess*/false,
-                CameraCaptureSession.SESSION_ID_NONE);
+                CameraCaptureSession.SESSION_ID_NONE, mCameraId, /*physicalCameraIdSet*/null);
         assertFalse(request.isEmpty());
         assertFalse(metadata.isEmpty());
         if (needStream) {
diff --git a/packages/ExtServices/AndroidManifest.xml b/packages/ExtServices/AndroidManifest.xml
index 63d3623..45e557c 100644
--- a/packages/ExtServices/AndroidManifest.xml
+++ b/packages/ExtServices/AndroidManifest.xml
@@ -56,6 +56,12 @@
             <intent-filter>
                 <action android:name="android.service.autofill.AutofillFieldClassificationService" />
             </intent-filter>
+            <meta-data
+                android:name="android.autofill.field_classification.default_algorithm"
+                android:resource="@string/autofill_field_classification_default_algorithm" />
+            <meta-data
+                android:name="android.autofill.field_classification.available_algorithms"
+                android:resource="@array/autofill_field_classification_available_algorithms" />
         </service>
 
         <library android:name="android.ext.services"/>
diff --git a/packages/ExtServices/res/values/strings.xml b/packages/ExtServices/res/values/strings.xml
index a2e65bc..72647ab 100644
--- a/packages/ExtServices/res/values/strings.xml
+++ b/packages/ExtServices/res/values/strings.xml
@@ -19,4 +19,9 @@
 
     <string name="notification_assistant">Notification Assistant</string>
     <string name="prompt_block_reason">Too many dismissals:views</string>
+
+    <string name="autofill_field_classification_default_algorithm">EDIT_DISTANCE</string>
+    <string-array name="autofill_field_classification_available_algorithms">
+        <item>EDIT_DISTANCE</item>
+    </string-array>
 </resources>
diff --git a/packages/ExtServices/src/android/ext/services/autofill/AutofillFieldClassificationServiceImpl.java b/packages/ExtServices/src/android/ext/services/autofill/AutofillFieldClassificationServiceImpl.java
index ea516a1..4709d35 100644
--- a/packages/ExtServices/src/android/ext/services/autofill/AutofillFieldClassificationServiceImpl.java
+++ b/packages/ExtServices/src/android/ext/services/autofill/AutofillFieldClassificationServiceImpl.java
@@ -24,28 +24,17 @@
 
 import com.android.internal.util.ArrayUtils;
 
-import java.util.Arrays;
 import java.util.List;
 
 public class AutofillFieldClassificationServiceImpl extends AutofillFieldClassificationService {
 
     private static final String TAG = "AutofillFieldClassificationServiceImpl";
-    private static final boolean DEBUG = false;
-    private static final List<String> sAvailableAlgorithms = Arrays.asList(EditDistanceScorer.NAME);
-
-    @Override
-    public List<String> onGetAvailableAlgorithms() {
-        return sAvailableAlgorithms;
-    }
-
-    @Override
-    public String onGetDefaultAlgorithm() {
-        return EditDistanceScorer.NAME;
-    }
+    // TODO(b/70291841): set to false before launching
+    private static final boolean DEBUG = true;
 
     @Nullable
     @Override
-    public Scores onGetScores(@Nullable String algorithmName,
+    public float[][] onGetScores(@Nullable String algorithmName,
             @Nullable Bundle algorithmArgs, @NonNull List<AutofillValue> actualValues,
             @NonNull List<String> userDataValues) {
         if (ArrayUtils.isEmpty(actualValues) || ArrayUtils.isEmpty(userDataValues)) {
@@ -66,14 +55,13 @@
             Log.d(TAG, "getScores() will return a " + actualValuesSize + "x"
                     + userDataValuesSize + " matrix for " + actualAlgorithmName);
         }
-        final Scores scores = new Scores(actualAlgorithmName, actualValuesSize, userDataValuesSize);
-        final float[][] scoresMatrix = scores.getScores();
+        final float[][] scores = new float[actualValuesSize][userDataValuesSize];
 
         final EditDistanceScorer algorithm = EditDistanceScorer.getInstance();
         for (int i = 0; i < actualValuesSize; i++) {
             for (int j = 0; j < userDataValuesSize; j++) {
                 final float score = algorithm.getScore(actualValues.get(i), userDataValues.get(j));
-                scoresMatrix[i][j] = score;
+                scores[i][j] = score;
             }
         }
         return scores;
diff --git a/packages/SettingsLib/res/values/strings.xml b/packages/SettingsLib/res/values/strings.xml
index b13de2e..042767d 100644
--- a/packages/SettingsLib/res/values/strings.xml
+++ b/packages/SettingsLib/res/values/strings.xml
@@ -498,6 +498,8 @@
     <string name="wifi_display_certification">Wireless display certification</string>
     <!-- Setting Checkbox title whether to enable WiFi Verbose Logging. [CHAR LIMIT=40] -->
     <string name="wifi_verbose_logging">Enable Wi\u2011Fi Verbose Logging</string>
+    <!-- Setting Checkbox title whether to enable connected MAC randomization -->
+    <string name="wifi_connected_mac_randomization">Connected MAC Randomization</string>
     <!-- Setting Checkbox title whether to always keep mobile data active. [CHAR LIMIT=80] -->
     <string name="mobile_data_always_on">Mobile data always active</string>
     <!-- Setting Checkbox title whether to enable hardware acceleration for tethering. [CHAR LIMIT=80] -->
@@ -552,6 +554,8 @@
     <string name="wifi_display_certification_summary">Show options for wireless display certification</string>
     <!-- Setting Checkbox summary whether to enable Wifi verbose Logging [CHAR LIMIT=80] -->
     <string name="wifi_verbose_logging_summary">Increase Wi\u2011Fi logging level, show per SSID RSSI in Wi\u2011Fi Picker</string>
+    <!-- Setting Checkbox title whether to enable connected MAC randomization -->
+    <string name="wifi_connected_mac_randomization_summary">Randomize MAC address when connecting to Wi\u2011Fi networks</string>
     <!-- UI debug setting: limit size of Android logger buffers -->
     <string name="select_logd_size_title">Logger buffer sizes</string>
     <!-- UI debug setting: limit size of Android logger buffers [CHAR LIMIT=59] -->
@@ -1015,4 +1019,10 @@
     <!-- About phone, status item value if the actual value is not available. -->
     <string name="status_unavailable">Unavailable</string>
 
+    <!-- Summary to show how many devices are connected in wifi hotspot [CHAR LIMIT=NONE] -->
+    <plurals name="wifi_tether_connected_summary">
+        <item quantity="one">%1$d device connected</item>
+        <item quantity="other">%1$d devices connected</item>
+    </plurals>
+
 </resources>
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpProfile.java
index 764c5922..9b69304 100755
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpProfile.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpProfile.java
@@ -128,14 +128,18 @@
 
     public boolean connect(BluetoothDevice device) {
         if (mService == null) return false;
-        List<BluetoothDevice> sinks = getConnectedDevices();
-        if (sinks != null) {
-            for (BluetoothDevice sink : sinks) {
-                if (sink.equals(device)) {
-                    Log.w(TAG, "Connecting to device " + device + " : disconnect skipped");
-                    continue;
+        int max_connected_devices = mLocalAdapter.getMaxConnectedAudioDevices();
+        if (max_connected_devices == 1) {
+            // Original behavior: disconnect currently connected device
+            List<BluetoothDevice> sinks = getConnectedDevices();
+            if (sinks != null) {
+                for (BluetoothDevice sink : sinks) {
+                    if (sink.equals(device)) {
+                        Log.w(TAG, "Connecting to device " + device + " : disconnect skipped");
+                        continue;
+                    }
+                    mService.disconnect(sink);
                 }
-                mService.disconnect(sink);
             }
         }
         return mService.connect(device);
@@ -157,6 +161,16 @@
         return mService.getConnectionState(device);
     }
 
+    public boolean setActiveDevice(BluetoothDevice device) {
+        if (mService == null) return false;
+        return mService.setActiveDevice(device);
+    }
+
+    public BluetoothDevice getActiveDevice() {
+        if (mService == null) return null;
+        return mService.getActiveDevice();
+    }
+
     public boolean isPreferred(BluetoothDevice device) {
         if (mService == null) return false;
         return mService.getPriority(device) > BluetoothProfile.PRIORITY_OFF;
@@ -180,8 +194,8 @@
     boolean isA2dpPlaying() {
         if (mService == null) return false;
         List<BluetoothDevice> sinks = mService.getConnectedDevices();
-        if (!sinks.isEmpty()) {
-            if (mService.isA2dpPlaying(sinks.get(0))) {
+        for (BluetoothDevice device : sinks) {
+            if (mService.isA2dpPlaying(device)) {
                 return true;
             }
         }
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothCallback.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothCallback.java
index 4c41b49..ac3599c 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothCallback.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothCallback.java
@@ -28,4 +28,5 @@
     void onDeviceDeleted(CachedBluetoothDevice cachedDevice);
     void onDeviceBondStateChanged(CachedBluetoothDevice cachedDevice, int bondState);
     void onConnectionStateChanged(CachedBluetoothDevice cachedDevice, int state);
+    void onActiveDeviceChanged(CachedBluetoothDevice activeDevice, int bluetoothProfile);
 }
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothEventManager.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothEventManager.java
index f57d02b..3cda9c9 100755
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothEventManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothEventManager.java
@@ -16,9 +16,12 @@
 
 package com.android.settingslib.bluetooth;
 
+import android.bluetooth.BluetoothA2dp;
 import android.bluetooth.BluetoothAdapter;
 import android.bluetooth.BluetoothClass;
 import android.bluetooth.BluetoothDevice;
+import android.bluetooth.BluetoothHeadset;
+import android.bluetooth.BluetoothProfile;
 import android.content.BroadcastReceiver;
 import android.content.Context;
 import android.content.Intent;
@@ -31,6 +34,7 @@
 import java.util.Collection;
 import java.util.HashMap;
 import java.util.Map;
+import java.util.Objects;
 import java.util.Set;
 
 /**
@@ -106,6 +110,12 @@
         // Dock event broadcasts
         addHandler(Intent.ACTION_DOCK_EVENT, new DockEventHandler());
 
+        // Active device broadcasts
+        addHandler(BluetoothA2dp.ACTION_ACTIVE_DEVICE_CHANGED,
+                   new ActiveDeviceChangedHandler());
+        addHandler(BluetoothHeadset.ACTION_ACTIVE_DEVICE_CHANGED,
+                   new ActiveDeviceChangedHandler());
+
         mContext.registerReceiver(mBroadcastReceiver, mAdapterIntentFilter, null, mReceiverHandler);
         mContext.registerReceiver(mProfileBroadcastReceiver, mProfileIntentFilter, null, mReceiverHandler);
     }
@@ -409,4 +419,35 @@
 
         return deviceAdded;
     }
+
+    private class ActiveDeviceChangedHandler implements Handler {
+        @Override
+        public void onReceive(Context context, Intent intent, BluetoothDevice device) {
+            String action = intent.getAction();
+            if (action == null) {
+                Log.w(TAG, "ActiveDeviceChangedHandler: action is null");
+                return;
+            }
+            CachedBluetoothDevice activeDevice = mDeviceManager.findDevice(device);
+            int bluetoothProfile = 0;
+            if (Objects.equals(action, BluetoothA2dp.ACTION_ACTIVE_DEVICE_CHANGED)) {
+                bluetoothProfile = BluetoothProfile.A2DP;
+            } else if (Objects.equals(action, BluetoothHeadset.ACTION_ACTIVE_DEVICE_CHANGED)) {
+                bluetoothProfile = BluetoothProfile.HEADSET;
+            } else {
+                Log.w(TAG, "ActiveDeviceChangedHandler: unknown action " + action);
+                return;
+            }
+            dispatchActiveDeviceChanged(activeDevice, bluetoothProfile);
+        }
+    }
+
+    private void dispatchActiveDeviceChanged(CachedBluetoothDevice activeDevice,
+                                             int bluetoothProfile) {
+        synchronized (mCallbacks) {
+            for (BluetoothCallback callback : mCallbacks) {
+                callback.onActiveDeviceChanged(activeDevice, bluetoothProfile);
+            }
+        }
+    }
 }
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
index 9caff10..fb0f75b 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
@@ -105,6 +105,10 @@
     private static final long MAX_UUID_DELAY_FOR_AUTO_CONNECT = 5000;
     private static final long MAX_HOGP_DELAY_FOR_AUTO_CONNECT = 30000;
 
+    // Active device state
+    private boolean mIsActiveDeviceA2dp = false;
+    private boolean mIsActiveDeviceHeadset = false;
+
     /**
      * Describes the current device and profile for logging.
      *
@@ -156,6 +160,7 @@
             mRemovedProfiles.add(profile);
             mLocalNapRoleConnected = false;
         }
+        fetchActiveDevices();
     }
 
     CachedBluetoothDevice(Context context,
@@ -359,6 +364,7 @@
         fetchName();
         fetchBtClass();
         updateProfiles();
+        fetchActiveDevices();
         migratePhonebookPermissionChoice();
         migrateMessagePermissionChoice();
         fetchMessageRejectionCount();
@@ -454,6 +460,33 @@
         return mDevice.getBondState();
     }
 
+    /**
+     * Set the device status as active or non-active per Bluetooth profile.
+     *
+     * @param isActive true if the device is active
+     * @param bluetoothProfile the Bluetooth profile
+     */
+    public void setActiveDevice(boolean isActive, int bluetoothProfile) {
+        boolean changed = false;
+        switch (bluetoothProfile) {
+        case BluetoothProfile.A2DP:
+            changed = (mIsActiveDeviceA2dp != isActive);
+            mIsActiveDeviceA2dp = isActive;
+            break;
+        case BluetoothProfile.HEADSET:
+            changed = (mIsActiveDeviceHeadset != isActive);
+            mIsActiveDeviceHeadset = isActive;
+            break;
+        default:
+            Log.w(TAG, "setActiveDevice: unknown profile " + bluetoothProfile +
+                    " isActive " + isActive);
+            break;
+        }
+        if (changed) {
+            dispatchAttributesChanged();
+        }
+    }
+
     void setRssi(short rssi) {
         if (mRssi != rssi) {
             mRssi = rssi;
@@ -529,6 +562,17 @@
         return true;
     }
 
+    private void fetchActiveDevices() {
+        A2dpProfile a2dpProfile = mProfileManager.getA2dpProfile();
+        if (a2dpProfile != null) {
+            mIsActiveDeviceA2dp = mDevice.equals(a2dpProfile.getActiveDevice());
+        }
+        HeadsetProfile headsetProfile = mProfileManager.getHeadsetProfile();
+        if (headsetProfile != null) {
+            mIsActiveDeviceHeadset = mDevice.equals(headsetProfile.getActiveDevice());
+        }
+    }
+
     /**
      * Refreshes the UI for the BT class, including fetching the latest value
      * for the class.
@@ -896,37 +940,60 @@
                     com.android.settingslib.Utils.formatPercentage(batteryLevel);
         }
 
+        // TODO: A temporary workaround solution using string description the device is active.
+        // Issue tracked by b/72317067 .
+        // An alternative solution would be visual indication.
+        // Intentionally not adding the strings to strings.xml for now:
+        //  1) If this is just a short-term solution, no need to waste translation effort
+        //  2) The number of strings with all possible combinations becomes enormously large.
+        // If string description becomes part of the final solution, we MUST NOT
+        // concatenate the strings here: this does not translate well.
+        String activeString = null;
+        if (mIsActiveDeviceA2dp && mIsActiveDeviceHeadset) {
+            activeString = ", active";
+        } else {
+            if (mIsActiveDeviceA2dp) {
+                activeString = ", active(media)";
+            }
+            if (mIsActiveDeviceHeadset) {
+                activeString = ", active(phone)";
+            }
+        }
+        if (activeString == null) activeString = "";
+
         if (profileConnected) {
             if (a2dpNotConnected && hfpNotConnected) {
                 if (batteryLevelPercentageString != null) {
                     return mContext.getString(
                             R.string.bluetooth_connected_no_headset_no_a2dp_battery_level,
-                            batteryLevelPercentageString);
+                            batteryLevelPercentageString) + activeString;
                 } else {
-                    return mContext.getString(R.string.bluetooth_connected_no_headset_no_a2dp);
+                    return mContext.getString(R.string.bluetooth_connected_no_headset_no_a2dp) +
+                        activeString;
                 }
 
             } else if (a2dpNotConnected) {
                 if (batteryLevelPercentageString != null) {
                     return mContext.getString(R.string.bluetooth_connected_no_a2dp_battery_level,
-                            batteryLevelPercentageString);
+                            batteryLevelPercentageString) + activeString;
                 } else {
-                    return mContext.getString(R.string.bluetooth_connected_no_a2dp);
+                    return mContext.getString(R.string.bluetooth_connected_no_a2dp) + activeString;
                 }
 
             } else if (hfpNotConnected) {
                 if (batteryLevelPercentageString != null) {
                     return mContext.getString(R.string.bluetooth_connected_no_headset_battery_level,
-                            batteryLevelPercentageString);
+                            batteryLevelPercentageString) + activeString;
                 } else {
-                    return mContext.getString(R.string.bluetooth_connected_no_headset);
+                    return mContext.getString(R.string.bluetooth_connected_no_headset)
+                          + activeString;
                 }
             } else {
                 if (batteryLevelPercentageString != null) {
                     return mContext.getString(R.string.bluetooth_connected_battery_level,
-                            batteryLevelPercentageString);
+                            batteryLevelPercentageString) + activeString;
                 } else {
-                    return mContext.getString(R.string.bluetooth_connected);
+                    return mContext.getString(R.string.bluetooth_connected) + activeString;
                 }
             }
         }
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HeadsetProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HeadsetProfile.java
index d45fe1a..ee12191 100755
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HeadsetProfile.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HeadsetProfile.java
@@ -153,6 +153,16 @@
         return BluetoothProfile.STATE_DISCONNECTED;
     }
 
+    public boolean setActiveDevice(BluetoothDevice device) {
+        if (mService == null) return false;
+        return mService.setActiveDevice(device);
+    }
+
+    public BluetoothDevice getActiveDevice() {
+        if (mService == null) return null;
+        return mService.getActiveDevice();
+    }
+
     public boolean isPreferred(BluetoothDevice device) {
         if (mService == null) return false;
         return mService.getPriority(device) > BluetoothProfile.PRIORITY_OFF;
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothAdapter.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothAdapter.java
index 22674cb..cda4e45 100755
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothAdapter.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothAdapter.java
@@ -239,4 +239,8 @@
     public BluetoothDevice getRemoteDevice(String address) {
         return mAdapter.getRemoteDevice(address);
     }
+
+    public int getMaxConnectedAudioDevices() {
+        return mAdapter.getMaxConnectedAudioDevices();
+    }
 }
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
index 87ed7eb..5a75681 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
@@ -1128,6 +1128,9 @@
         dumpSetting(s, p,
                 Settings.Global.SHOW_FIRST_CRASH_DIALOG,
                 GlobalSettingsProto.SHOW_FIRST_CRASH_DIALOG);
+        dumpSetting(s, p,
+                    Settings.Global.WIFI_CONNECTED_MAC_RANDOMIZATION_ENABLED,
+                    GlobalSettingsProto.WIFI_CONNECTED_MAC_RANDOMIZATION_ENABLED);
     }
 
     /** Dump a single {@link SettingsState.Setting} to a proto buf */
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
index 9ee205f..2a697b8 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
@@ -302,6 +302,7 @@
         // fail to boot if there're any backed up settings that don't have a non-null validator
         ensureAllBackedUpSystemSettingsHaveValidators();
         ensureAllBackedUpGlobalSettingsHaveValidators();
+        ensureAllBackedUpSecureSettingsHaveValidators();
 
         synchronized (mLock) {
             mUserManager = UserManager.get(getContext());
@@ -321,19 +322,26 @@
     }
 
     private void ensureAllBackedUpSystemSettingsHaveValidators() {
-        String offenders = getOffenders(Settings.System.SETTINGS_TO_BACKUP,
-                Settings.System.VALIDATORS);
+        String offenders = getOffenders(concat(Settings.System.SETTINGS_TO_BACKUP,
+                Settings.System.LEGACY_RESTORE_SETTINGS), Settings.System.VALIDATORS);
 
         failToBootIfOffendersPresent(offenders, "Settings.System");
     }
 
     private void ensureAllBackedUpGlobalSettingsHaveValidators() {
-        String offenders = getOffenders(Settings.Global.SETTINGS_TO_BACKUP,
-                Settings.Global.VALIDATORS);
+        String offenders = getOffenders(concat(Settings.Global.SETTINGS_TO_BACKUP,
+                Settings.Global.LEGACY_RESTORE_SETTINGS), Settings.Global.VALIDATORS);
 
         failToBootIfOffendersPresent(offenders, "Settings.Global");
     }
 
+    private void ensureAllBackedUpSecureSettingsHaveValidators() {
+        String offenders = getOffenders(concat(Settings.Secure.SETTINGS_TO_BACKUP,
+                Settings.Secure.LEGACY_RESTORE_SETTINGS), Settings.Secure.VALIDATORS);
+
+        failToBootIfOffendersPresent(offenders, "Settings.Secure");
+    }
+
     private void failToBootIfOffendersPresent(String offenders, String settingsType) {
         if (offenders.length() > 0) {
             throw new RuntimeException("All " + settingsType + " settings that are backed up"
@@ -352,6 +360,18 @@
         return offenders.toString();
     }
 
+    private final String[] concat(String[] first, String[] second) {
+        if (second == null || second.length == 0) {
+            return first;
+        }
+        final int firstLen = first.length;
+        final int secondLen = second.length;
+        String[] both = new String[firstLen + secondLen];
+        System.arraycopy(first, 0, both, 0, firstLen);
+        System.arraycopy(second, 0, both, firstLen, secondLen);
+        return both;
+    }
+
     @Override
     public Bundle call(String method, String name, Bundle args) {
         final int requestingUserId = getRequestingUserId(args);
diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml
index 0f43db0..64b2ae6 100644
--- a/packages/Shell/AndroidManifest.xml
+++ b/packages/Shell/AndroidManifest.xml
@@ -135,6 +135,9 @@
     <uses-permission android:name="android.permission.RESTRICTED_VR_ACCESS" />
     <uses-permission android:name="android.permission.MANAGE_BIND_INSTANT_SERVICE" />
     <uses-permission android:name="android.permission.SET_HARMFUL_APP_WARNINGS" />
+    <uses-permission android:name="android.permission.MANAGE_SENSORS" />
+    <uses-permission android:name="android.permission.MANAGE_AUDIO_POLICY" />
+    <uses-permission android:name="android.permission.MANAGE_CAMERA" />
 
     <application android:label="@string/app_label"
                  android:defaultToDeviceProtectedStorage="true"
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index aa2cdbb..80ac825 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -121,7 +121,7 @@
     <uses-permission android:name="android.permission.TRUST_LISTENER" />
     <uses-permission android:name="android.permission.USE_FINGERPRINT" />
     <uses-permission android:name="android.permission.RESET_FINGERPRINT_LOCKOUT" />
-    <uses-permission android:name="android.permission.BIND_SLICE" />
+    <uses-permission android:name="android.permission.MANAGE_SLICE_PERMISSIONS" />
 
     <!-- Needed for WallpaperManager.clear in ImageWallpaper.updateWallpaperLocked -->
     <uses-permission android:name="android.permission.SET_WALLPAPER"/>
@@ -435,6 +435,16 @@
             android:launchMode="singleTop"
             androidprv:alwaysFocusable="true" />
 
+        <!-- started from SliceProvider -->
+        <activity android:name=".SlicePermissionActivity"
+            android:theme="@style/Theme.SystemUI.Dialog.Alert"
+            android:finishOnCloseSystemDialogs="true"
+            android:excludeFromRecents="true">
+            <intent-filter>
+                <action android:name="android.intent.action.REQUEST_SLICE_PERMISSION" />
+            </intent-filter>
+        </activity>
+
         <!-- platform logo easter egg activity -->
         <activity
             android:name=".DessertCase"
@@ -572,6 +582,7 @@
 
         <provider android:name=".keyguard.KeyguardSliceProvider"
                   android:authorities="com.android.systemui.keyguard"
+                  android:grantUriPermissions="true"
                   android:exported="true">
         </provider>
 
diff --git a/packages/SystemUI/res-keyguard/layout/keyguard_status_area.xml b/packages/SystemUI/res-keyguard/layout/keyguard_status_area.xml
index 5e09e75..d0389eb 100644
--- a/packages/SystemUI/res-keyguard/layout/keyguard_status_area.xml
+++ b/packages/SystemUI/res-keyguard/layout/keyguard_status_area.xml
@@ -31,7 +31,7 @@
     <TextView android:id="@+id/title"
               android:layout_width="match_parent"
               android:layout_height="wrap_content"
-              android:layout_marginBottom="@dimen/widget_vertical_padding"
+              android:layout_marginBottom="7dp"
               android:paddingStart="64dp"
               android:paddingEnd="64dp"
               android:theme="@style/TextAppearance.Keyguard"
diff --git a/packages/SystemUI/res-keyguard/layout/keyguard_status_view.xml b/packages/SystemUI/res-keyguard/layout/keyguard_status_view.xml
index 9adb550..eda8c69 100644
--- a/packages/SystemUI/res-keyguard/layout/keyguard_status_view.xml
+++ b/packages/SystemUI/res-keyguard/layout/keyguard_status_view.xml
@@ -44,6 +44,7 @@
                 android:layout_gravity="center_horizontal"
                 android:layout_centerHorizontal="true"
                 android:layout_alignParentTop="true"
+                android:letterSpacing="0.04"
                 android:textColor="?attr/wallpaperTextColor"
                 android:singleLine="true"
                 style="@style/widget_big_thin"
@@ -52,16 +53,16 @@
                 android:layout_marginBottom="@dimen/bottom_text_spacing_digital" />
             <View
                 android:id="@+id/clock_separator"
-                android:layout_width="16dp"
-                android:layout_height="1dp"
-                android:layout_marginTop="10dp"
+                android:layout_width="@dimen/widget_separator_width"
+                android:layout_height="@dimen/widget_separator_thickness"
+                android:layout_marginTop="22dp"
                 android:layout_below="@id/clock_view"
                 android:background="#f00"
                 android:layout_centerHorizontal="true" />
 
             <include layout="@layout/keyguard_status_area"
                 android:id="@+id/keyguard_status_area"
-                android:layout_marginTop="10dp"
+                android:layout_marginTop="20dp"
                 android:layout_marginBottom="@dimen/widget_vertical_padding"
                 android:layout_width="match_parent"
                 android:layout_height="wrap_content"
diff --git a/packages/SystemUI/res-keyguard/values/dimens.xml b/packages/SystemUI/res-keyguard/values/dimens.xml
index 04cf6b0..5aca7f9 100644
--- a/packages/SystemUI/res-keyguard/values/dimens.xml
+++ b/packages/SystemUI/res-keyguard/values/dimens.xml
@@ -42,7 +42,7 @@
     <dimen name="eca_overlap">-10dip</dimen>
 
     <!-- Default clock parameters -->
-    <dimen name="bottom_text_spacing_digital">-10dp</dimen>
+    <dimen name="bottom_text_spacing_digital">-24dp</dimen>
     <!-- Slice header -->
     <dimen name="widget_title_font_size">28sp</dimen>
     <!-- Slice subtitle  -->
@@ -52,12 +52,16 @@
     <!-- Clock with header -->
     <dimen name="widget_small_font_size">22dp</dimen>
     <!-- Dash between clock and header -->
-    <dimen name="widget_vertical_padding">16dp</dimen>
+    <dimen name="widget_separator_width">16dp</dimen>
+    <dimen name="widget_separator_thickness">1dp</dimen>
+    <dimen name="widget_vertical_padding">26dp</dimen>
+    <dimen name="widget_icon_bottom_padding">14dp</dimen>
     <!-- Subtitle paddings -->
-    <dimen name="widget_separator_thickness">2dp</dimen>
     <dimen name="widget_horizontal_padding">8dp</dimen>
     <dimen name="widget_icon_size">16dp</dimen>
     <dimen name="widget_icon_padding">8dp</dimen>
+    <!-- Dash between notification shelf and date/alarm -->
+    <dimen name="widget_bottom_separator_padding">29dp</dimen>
 
     <!-- The y translation to apply at the start in appear animations. -->
     <dimen name="appear_y_translation_start">32dp</dimen>
diff --git a/packages/SystemUI/res/color/qs_background_dark.xml b/packages/SystemUI/res/color/qs_background_dark.xml
index c19fa08..24afebd 100644
--- a/packages/SystemUI/res/color/qs_background_dark.xml
+++ b/packages/SystemUI/res/color/qs_background_dark.xml
@@ -15,6 +15,6 @@
 -->
 
 <selector xmlns:android="http://schemas.android.com/apk/res/android">
-    <item android:alpha="0.87"
+    <item android:alpha="1"
           android:color="?android:attr/colorBackgroundFloating"/>
 </selector>
diff --git a/packages/SystemUI/res/drawable/ic_screenshot_edit.xml b/packages/SystemUI/res/drawable/ic_screenshot_edit.xml
new file mode 100644
index 0000000..d901292
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_screenshot_edit.xml
@@ -0,0 +1,27 @@
+<!--
+Copyright (C) 2014 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24.0dp"
+        android:height="24.0dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M3.0,17.25L3.0,21.0l3.75,0.0L17.81,9.94l-3.75,-3.75L3.0,17.25zM20.71,7.04c0.39,-0.3 0.39,-1.02 0.0,-1.41l-2.34,-2.34c-0.39,-0.39 -1.02,-0.39 -1.41,0.0l-1.83,1.83 3.75,3.75 1.83,-1.83z"/>
+    <path
+        android:pathData="M0 0h24v24H0z"
+        android:fillColor="#00000000"/>
+</vector>
diff --git a/packages/SystemUI/res/drawable/qs_bg_gradient.xml b/packages/SystemUI/res/drawable/qs_bg_gradient.xml
new file mode 100644
index 0000000..a1ad528
--- /dev/null
+++ b/packages/SystemUI/res/drawable/qs_bg_gradient.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2017 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.
+-->
+<shape
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:shape="rectangle">
+    <gradient
+        android:angle="270"
+        android:startColor="#ff000000"
+        android:endColor="#00000000"
+        android:type="linear" />
+</shape>
diff --git a/packages/SystemUI/res/layout/battery_percentage_view.xml b/packages/SystemUI/res/layout/battery_percentage_view.xml
index 59c0957..e52aa14 100644
--- a/packages/SystemUI/res/layout/battery_percentage_view.xml
+++ b/packages/SystemUI/res/layout/battery_percentage_view.xml
@@ -25,6 +25,6 @@
         android:textAppearance="@style/TextAppearance.StatusBar.Expanded.Clock"
         android:textColor="?android:attr/textColorPrimary"
         android:gravity="center_vertical|start"
-        android:paddingEnd="@dimen/battery_level_padding_start"
+        android:paddingStart="@dimen/battery_level_padding_start"
         android:importantForAccessibility="no"
         />
diff --git a/packages/SystemUI/res/layout/output_chooser.xml b/packages/SystemUI/res/layout/output_chooser.xml
index 3d0ab35..b96f447 100644
--- a/packages/SystemUI/res/layout/output_chooser.xml
+++ b/packages/SystemUI/res/layout/output_chooser.xml
@@ -14,7 +14,7 @@
      See the License for the specific language governing permissions and
      limitations under the License.
 -->
-<!-- extends FrameLayout -->
+<!-- extends LinearLayout -->
 <com.android.systemui.volume.OutputChooserLayout
     xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:sysui="http://schemas.android.com/apk/res-auto"
@@ -23,8 +23,17 @@
     android:minHeight="320dp"
     android:layout_width="match_parent"
     android:layout_height="match_parent"
+    android:orientation="vertical"
     android:padding="20dp" >
 
+    <TextView
+        android:id="@+id/title"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:textDirection="locale"
+        android:textAppearance="@style/TextAppearance.QS.DetailHeader"
+        android:layout_marginBottom="20dp" />
+
     <com.android.systemui.qs.AutoSizingList
         android:id="@android:id/list"
         android:layout_width="match_parent"
@@ -42,9 +51,10 @@
         android:orientation="vertical">
 
         <TextView
-            android:id="@android:id/title"
+            android:id="@+id/empty_text"
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
+            android:textDirection="locale"
             android:layout_marginTop="20dp"
             android:textAppearance="@style/TextAppearance.QS.DetailEmpty"/>
     </LinearLayout>
diff --git a/packages/SystemUI/res/layout/output_chooser_item.xml b/packages/SystemUI/res/layout/output_chooser_item.xml
index ed7df4b..c3ddbbe 100644
--- a/packages/SystemUI/res/layout/output_chooser_item.xml
+++ b/packages/SystemUI/res/layout/output_chooser_item.xml
@@ -30,6 +30,7 @@
         android:layout_height="@dimen/qs_detail_item_icon_size"
         android:layout_marginStart="@dimen/qs_detail_item_icon_marginStart"
         android:layout_marginEnd="@dimen/qs_detail_item_icon_marginEnd"
+        android:background="?android:selectableItemBackgroundBorderless"
         android:tint="?android:attr/textColorPrimary"/>
 
     <LinearLayout
diff --git a/packages/SystemUI/res/layout/qs_customize_panel.xml b/packages/SystemUI/res/layout/qs_customize_panel.xml
index 9ab8ac63..b3b6a0c 100644
--- a/packages/SystemUI/res/layout/qs_customize_panel.xml
+++ b/packages/SystemUI/res/layout/qs_customize_panel.xml
@@ -20,6 +20,7 @@
     xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_width="match_parent"
     android:layout_height="0dp"
+    android:elevation="4dp"
     android:orientation="vertical"
     android:background="@drawable/qs_customizer_background"
     android:gravity="center_horizontal">
diff --git a/packages/SystemUI/res/layout/qs_detail.xml b/packages/SystemUI/res/layout/qs_detail.xml
index 1fd239b..0b9a7b2 100644
--- a/packages/SystemUI/res/layout/qs_detail.xml
+++ b/packages/SystemUI/res/layout/qs_detail.xml
@@ -23,7 +23,8 @@
     android:clickable="true"
     android:orientation="vertical"
     android:paddingBottom="8dp"
-    android:visibility="invisible">
+    android:visibility="invisible"
+    android:elevation="4dp" >
 
     <include
         android:id="@+id/qs_detail_header"
diff --git a/packages/SystemUI/res/layout/qs_footer_impl.xml b/packages/SystemUI/res/layout/qs_footer_impl.xml
index 3d09b74..9f6a946 100644
--- a/packages/SystemUI/res/layout/qs_footer_impl.xml
+++ b/packages/SystemUI/res/layout/qs_footer_impl.xml
@@ -21,6 +21,7 @@
     android:id="@+id/qs_footer"
     android:layout_width="match_parent"
     android:layout_height="48dp"
+    android:elevation="4dp"
     android:baselineAligned="false"
     android:clickable="false"
     android:clipChildren="false"
@@ -105,17 +106,6 @@
                 android:visibility="invisible"/>
 
         </com.android.systemui.statusbar.AlphaOptimizedFrameLayout>
-
-        <com.android.systemui.statusbar.phone.ExpandableIndicator
-            android:id="@+id/expand_indicator"
-            android:layout_width="48dp"
-            android:layout_height="48dp"
-            android:clipToPadding="false"
-            android:clickable="true"
-            android:focusable="true"
-            android:background="?android:attr/selectableItemBackgroundBorderless"
-            android:contentDescription="@string/accessibility_quick_settings_expand"
-            android:padding="14dp" />
     </LinearLayout>
 
 </com.android.systemui.qs.QSFooterImpl>
diff --git a/packages/SystemUI/res/layout/qs_panel.xml b/packages/SystemUI/res/layout/qs_panel.xml
index 5541f3d..5bcb7fd 100644
--- a/packages/SystemUI/res/layout/qs_panel.xml
+++ b/packages/SystemUI/res/layout/qs_panel.xml
@@ -18,17 +18,46 @@
     android:id="@+id/quick_settings_container"
     android:layout_width="match_parent"
     android:layout_height="wrap_content"
-    android:background="@drawable/qs_background_primary"
-    android:elevation="4dp"
     android:clipToPadding="false"
-    android:clipChildren="false">
+    android:clipChildren="false" >
+
+    <!-- Main QS background -->
+    <View
+        android:id="@+id/quick_settings_background"
+        android:layout_width="match_parent"
+        android:layout_height="0dp"
+        android:elevation="4dp"
+        android:background="@drawable/qs_background_primary" />
+
+    <!-- Black part behind the status bar -->
+    <View
+        android:id="@+id/quick_settings_status_bar_background"
+        android:layout_width="match_parent"
+        android:layout_height="@dimen/qs_header_system_icons_area_height"
+        android:clipToPadding="false"
+        android:clipChildren="false"
+        android:background="#ff000000" />
+
+    <!-- Gradient view behind QS -->
+    <View
+        android:id="@+id/quick_settings_gradient_view"
+        android:layout_width="match_parent"
+        android:layout_height="126dp"
+        android:layout_marginTop="@dimen/qs_header_system_icons_area_height"
+        android:clipToPadding="false"
+        android:clipChildren="false"
+        android:background="@drawable/qs_bg_gradient" />
+
 
     <com.android.systemui.qs.QSPanel
         android:id="@+id/quick_settings_panel"
-        android:layout_marginTop="28dp"
+        android:layout_marginTop="@dimen/qs_header_system_icons_area_height"
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
-        android:layout_marginBottom="48dp" />
+        android:layout_marginBottom="48dp"
+        android:elevation="4dp"
+    />
+
 
     <include layout="@layout/quick_status_bar_expanded_header" />
 
diff --git a/packages/SystemUI/res/layout/quick_qs_status_icons.xml b/packages/SystemUI/res/layout/quick_qs_status_icons.xml
new file mode 100644
index 0000000..e0f0ed9
--- /dev/null
+++ b/packages/SystemUI/res/layout/quick_qs_status_icons.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2017 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.
+-->
+<View
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/quick_qs_status_icons"
+    android:layout_width="match_parent"
+    android:layout_height="20dp"
+    android:layout_marginBottom="22dp"
+    android:layout_below="@id/quick_status_bar_system_icons"
+    >
+
+</View>
diff --git a/packages/SystemUI/res/layout/quick_status_bar_expanded_header.xml b/packages/SystemUI/res/layout/quick_status_bar_expanded_header.xml
index e8b418c..dacc3f9 100644
--- a/packages/SystemUI/res/layout/quick_status_bar_expanded_header.xml
+++ b/packages/SystemUI/res/layout/quick_status_bar_expanded_header.xml
@@ -28,23 +28,21 @@
     android:clipToPadding="false"
     android:paddingTop="0dp"
     android:paddingEnd="0dp"
-    android:paddingStart="0dp">
+    android:paddingStart="0dp"
+    android:elevation="4dp" >
 
     <include layout="@layout/quick_status_bar_header_system_icons" />
+    <include layout="@layout/quick_qs_status_icons" />
 
     <com.android.systemui.qs.QuickQSPanel
         android:id="@+id/quick_qs_panel"
         android:layout_width="match_parent"
         android:layout_height="48dp"
-        android:layout_alignParentEnd="true"
-        android:layout_marginTop="31dp"
-        android:layout_alignParentTop="true"
+        android:layout_below="@id/quick_qs_status_icons"
         android:accessibilityTraversalAfter="@+id/date_time_group"
         android:accessibilityTraversalBefore="@id/expand_indicator"
         android:clipChildren="false"
         android:clipToPadding="false"
-        android:layout_marginStart="8dp"
-        android:layout_marginEnd="8dp"
         android:focusable="true"
         android:importantForAccessibility="yes" />
 
diff --git a/packages/SystemUI/res/layout/quick_status_bar_header_system_icons.xml b/packages/SystemUI/res/layout/quick_status_bar_header_system_icons.xml
index 739a255..2c69501 100644
--- a/packages/SystemUI/res/layout/quick_status_bar_header_system_icons.xml
+++ b/packages/SystemUI/res/layout/quick_status_bar_header_system_icons.xml
@@ -17,6 +17,7 @@
 <LinearLayout
     xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:systemui="http://schemas.android.com/apk/res-auto"
+    android:id="@+id/quick_status_bar_system_icons"
     android:layout_width="match_parent"
     android:layout_height="@dimen/qs_header_system_icons_area_height"
     android:layout_alignParentEnd="true"
@@ -40,6 +41,17 @@
         systemui:showDark="false"
     />
 
+    <com.android.systemui.statusbar.policy.DateView
+        android:id="@+id/date"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:padding="4dp"
+        android:singleLine="true"
+        android:textAppearance="@style/TextAppearance.StatusBar.Expanded.Date"
+        android:textSize="@dimen/qs_time_collapsed_size"
+        android:gravity="center_vertical"
+        systemui:datePattern="@string/abbrev_wday_month_day_no_year_alarm" />
+
     <android.widget.Space
         android:id="@+id/space"
         android:layout_width="0dp"
diff --git a/packages/SystemUI/res/layout/rotate_suggestion.xml b/packages/SystemUI/res/layout/rotate_suggestion.xml
index 7762950..5074682 100644
--- a/packages/SystemUI/res/layout/rotate_suggestion.xml
+++ b/packages/SystemUI/res/layout/rotate_suggestion.xml
@@ -14,19 +14,13 @@
      limitations under the License.
 -->
 
-<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:systemui="http://schemas.android.com/apk/res-auto"
-    android:layout_width="@dimen/navigation_side_padding"
+<com.android.systemui.statusbar.policy.KeyButtonView
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/rotate_suggestion"
+    android:layout_width="@dimen/navigation_extra_key_width"
     android:layout_height="match_parent"
-    android:layout_weight="0"
-    >
-    <com.android.systemui.statusbar.policy.KeyButtonView
-        android:id="@+id/rotate_suggestion"
-        android:layout_width="@dimen/navigation_extra_key_width"
-        android:layout_height="match_parent"
-        android:layout_marginEnd="2dp"
-        android:visibility="invisible"
-        android:scaleType="centerInside"
-    />
-    <!-- TODO android:contentDescription -->
-</FrameLayout>
+    android:layout_marginEnd="2dp"
+    android:visibility="invisible"
+    android:scaleType="centerInside"
+    android:contentDescription="@string/accessibility_rotate_button"
+/>
diff --git a/packages/SystemUI/res/layout/slice_permission_request.xml b/packages/SystemUI/res/layout/slice_permission_request.xml
new file mode 100644
index 0000000..cdb2a91
--- /dev/null
+++ b/packages/SystemUI/res/layout/slice_permission_request.xml
@@ -0,0 +1,50 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     Copyright (C) 2014 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.
+-->
+<!-- Extends LinearLayout -->
+<LinearLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:paddingStart="16dp"
+    android:paddingEnd="16dp"
+    android:orientation="vertical">
+
+    <TextView
+        android:id="@+id/text2"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:paddingTop="8dp"
+        android:paddingStart="8dp"
+        android:textAppearance="?android:attr/textAppearanceMedium"
+        android:text="@string/slice_permission_text_1" />
+
+    <TextView
+        android:id="@+id/text1"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:paddingStart="8dp"
+        android:textAppearance="?android:attr/textAppearanceMedium"
+        android:paddingBottom="16dp"
+        android:text="@string/slice_permission_text_2" />
+
+    <CheckBox
+        android:id="@+id/slice_permission_checkbox"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:text="@string/slice_permission_checkbox" />
+
+</LinearLayout>
diff --git a/packages/SystemUI/res/layout/status_bar.xml b/packages/SystemUI/res/layout/status_bar.xml
index 17b38cb..8c0b9ba 100644
--- a/packages/SystemUI/res/layout/status_bar.xml
+++ b/packages/SystemUI/res/layout/status_bar.xml
@@ -54,30 +54,47 @@
             android:layout_height="match_parent"
             android:layout="@layout/operator_name" />
 
-        <com.android.systemui.statusbar.policy.Clock
-            android:id="@+id/clock"
-            android:textAppearance="@style/TextAppearance.StatusBar.Clock"
-            android:layout_width="wrap_content"
+        <LinearLayout
             android:layout_height="match_parent"
-            android:singleLine="true"
-            android:paddingStart="@dimen/status_bar_left_clock_starting_padding"
-            android:paddingEnd="@dimen/status_bar_left_clock_end_padding"
-            android:gravity="center_vertical|start"
+            android:layout_width="0dp"
+            android:layout_weight="1"
+        >
+            <com.android.systemui.statusbar.policy.Clock
+                android:id="@+id/clock"
+                android:layout_width="wrap_content"
+                android:layout_height="match_parent"
+                android:textAppearance="@style/TextAppearance.StatusBar.Clock"
+                android:singleLine="true"
+                android:paddingStart="@dimen/status_bar_left_clock_starting_padding"
+                android:paddingEnd="@dimen/status_bar_left_clock_end_padding"
+                android:gravity="center_vertical|start"
+            />
+
+            <!-- The alpha of this area is controlled from both PhoneStatusBarTransitions and
+                 PhoneStatusBar (DISABLE_NOTIFICATION_ICONS). -->
+            <com.android.systemui.statusbar.AlphaOptimizedFrameLayout
+                android:id="@+id/notification_icon_area"
+                android:layout_width="0dp"
+                android:layout_height="match_parent"
+                android:layout_weight="1"
+                android:orientation="horizontal" />
+
+        </LinearLayout>
+
+        <!-- Space should cover the notch (if it exists) and let other views lay out around it -->
+        <android.widget.Space
+            android:id="@+id/cutout_space_view"
+            android:layout_width="0dp"
+            android:layout_height="match_parent"
+            android:gravity="center_horizontal|center_vertical"
         />
 
-        <!-- The alpha of this area is controlled from both PhoneStatusBarTransitions and
-             PhoneStatusBar (DISABLE_NOTIFICATION_ICONS). -->
-        <com.android.systemui.statusbar.AlphaOptimizedFrameLayout
-            android:id="@+id/notification_icon_area"
-            android:layout_width="0dip"
+        <com.android.keyguard.AlphaOptimizedLinearLayout android:id="@+id/system_icon_area"
+            android:layout_width="0dp"
             android:layout_height="match_parent"
             android:layout_weight="1"
-            android:orientation="horizontal" />
-
-        <com.android.keyguard.AlphaOptimizedLinearLayout android:id="@+id/system_icon_area"
-            android:layout_width="wrap_content"
-            android:layout_height="match_parent"
             android:orientation="horizontal"
+            android:gravity="center_vertical|end"
             >
 
             <include layout="@layout/system_icons" />
diff --git a/packages/SystemUI/res/layout/status_bar_expanded.xml b/packages/SystemUI/res/layout/status_bar_expanded.xml
index c5e5ee1..bef0830 100644
--- a/packages/SystemUI/res/layout/status_bar_expanded.xml
+++ b/packages/SystemUI/res/layout/status_bar_expanded.xml
@@ -43,8 +43,6 @@
             android:layout_width="@dimen/qs_panel_width"
             android:layout_height="match_parent"
             android:layout_gravity="@integer/notification_panel_layout_gravity"
-            android:layout_marginStart="@dimen/notification_side_paddings"
-            android:layout_marginEnd="@dimen/notification_side_paddings"
             android:clipToPadding="false"
             android:clipChildren="false"
             systemui:viewType="com.android.systemui.plugins.qs.QS" />
diff --git a/packages/SystemUI/res/layout/system_icons.xml b/packages/SystemUI/res/layout/system_icons.xml
index bfa92ad..1fafb2f 100644
--- a/packages/SystemUI/res/layout/system_icons.xml
+++ b/packages/SystemUI/res/layout/system_icons.xml
@@ -16,12 +16,13 @@
 
 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
     android:id="@+id/system_icons"
-    android:layout_width="wrap_content"
+    android:layout_width="match_parent"
     android:layout_height="match_parent"
     android:gravity="center_vertical">
 
-    <com.android.keyguard.AlphaOptimizedLinearLayout android:id="@+id/statusIcons"
-        android:layout_width="wrap_content"
+    <com.android.systemui.statusbar.phone.StatusIconContainer android:id="@+id/statusIcons"
+        android:layout_width="0dp"
+        android:layout_weight="1"
         android:layout_height="match_parent"
         android:gravity="center_vertical"
         android:orientation="horizontal"/>
diff --git a/packages/SystemUI/res/values-h650dp/dimens.xml b/packages/SystemUI/res/values-h650dp/dimens.xml
index fbfca46..3811f67 100644
--- a/packages/SystemUI/res/values-h650dp/dimens.xml
+++ b/packages/SystemUI/res/values-h650dp/dimens.xml
@@ -15,8 +15,7 @@
   -->
 
 <resources>
-    <dimen name="keyguard_clock_notifications_margin_min">32dp</dimen>
-    <dimen name="keyguard_clock_notifications_margin_max">36dp</dimen>
+    <dimen name="keyguard_clock_notifications_margin">32dp</dimen>
 
     <fraction name="keyguard_clock_y_fraction_max">32.5%</fraction>
     <fraction name="keyguard_clock_y_fraction_min">18.5%</fraction>
diff --git a/packages/SystemUI/res/values-sw600dp-land/dimens.xml b/packages/SystemUI/res/values-sw600dp-land/dimens.xml
index 49a7a29..a5e37d5 100644
--- a/packages/SystemUI/res/values-sw600dp-land/dimens.xml
+++ b/packages/SystemUI/res/values-sw600dp-land/dimens.xml
@@ -19,8 +19,7 @@
     <fraction name="keyguard_clock_y_fraction_max">37%</fraction>
     <fraction name="keyguard_clock_y_fraction_min">20%</fraction>
 
-    <dimen name="keyguard_clock_notifications_margin_min">36dp</dimen>
-    <dimen name="keyguard_clock_notifications_margin_max">36dp</dimen>
+    <dimen name="keyguard_clock_notifications_margin">36dp</dimen>
 
     <dimen name="keyguard_indication_margin_bottom">80dp</dimen>
 
diff --git a/packages/SystemUI/res/values-sw600dp/dimens.xml b/packages/SystemUI/res/values-sw600dp/dimens.xml
index c8ffe8f..268fdec 100644
--- a/packages/SystemUI/res/values-sw600dp/dimens.xml
+++ b/packages/SystemUI/res/values-sw600dp/dimens.xml
@@ -46,8 +46,7 @@
 
     <!-- The margin between the clock and the notifications on Keyguard. See
          keyguard_clock_height_fraction_* for the difference between min and max.-->
-    <dimen name="keyguard_clock_notifications_margin_min">44dp</dimen>
-    <dimen name="keyguard_clock_notifications_margin_max">44dp</dimen>
+    <dimen name="keyguard_clock_notifications_margin">44dp</dimen>
 
     <!-- Height of the status bar header bar when on Keyguard -->
     <dimen name="status_bar_header_height_keyguard">60dp</dimen>
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 0134086..887d3cb 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -215,7 +215,7 @@
     <dimen name="close_handle_underlap">32dp</dimen>
 
     <!-- Height of the status bar header bar -->
-    <dimen name="status_bar_header_height">124dp</dimen>
+    <dimen name="status_bar_header_height">178dp</dimen>
 
     <!-- Height of the status bar header bar in the car setting. -->
     <dimen name="car_status_bar_header_height">128dp</dimen>
@@ -223,8 +223,9 @@
     <!-- The bottom padding of the status bar header. -->
     <dimen name="status_bar_header_padding_bottom">48dp</dimen>
 
-    <!-- The height of the container that holds the system icons in the quick settings header. -->
-    <dimen name="qs_header_system_icons_area_height">40dp</dimen>
+    <!-- The height of the container that holds the battery and time in the quick settings header.
+         -->
+    <dimen name="qs_header_system_icons_area_height">48dp</dimen>
 
     <!-- The height of the container that holds the system icons in the quick settings header in the
          car setting. -->
@@ -418,13 +419,12 @@
     <!-- The fraction of the screen height where the clock on the Keyguard has its center. The
          max value is used when no notifications are displaying, and the min value is when the
          highest possible number of notifications are showing. -->
-    <fraction name="keyguard_clock_y_fraction_max">32.5%</fraction>
+    <fraction name="keyguard_clock_y_fraction_max">45%</fraction>
     <fraction name="keyguard_clock_y_fraction_min">19.8%</fraction>
 
     <!-- The margin between the clock and the notifications on Keyguard. See
          keyguard_clock_height_fraction_* for the difference between min and max.-->
-    <dimen name="keyguard_clock_notifications_margin_min">30dp</dimen>
-    <dimen name="keyguard_clock_notifications_margin_max">42dp</dimen>
+    <dimen name="keyguard_clock_notifications_margin">30dp</dimen>
     <dimen name="heads_up_scrim_height">250dp</dimen>
 
     <item name="scrim_behind_alpha" format="float" type="dimen">0.62</item>
@@ -863,7 +863,7 @@
     <dimen name="burn_in_prevention_offset_y">50dp</dimen>
 
     <!-- padding between the notification stack and the keyguard status view when dozing -->
-    <dimen name="dozing_stack_padding">10dp</dimen>
+    <dimen name="dozing_stack_padding">30dp</dimen>
 
     <dimen name="corner_size">16dp</dimen>
     <dimen name="top_padding">0dp</dimen>
diff --git a/packages/SystemUI/res/values/ids.xml b/packages/SystemUI/res/values/ids.xml
index fed97c5..edda613 100644
--- a/packages/SystemUI/res/values/ids.xml
+++ b/packages/SystemUI/res/values/ids.xml
@@ -93,5 +93,8 @@
     <item type="id" name="action_snooze_long"/>
     <item type="id" name="action_snooze_longer"/>
     <item type="id" name="action_snooze_assistant_suggestion_1"/>
+
+    <!-- For StatusBarIconContainer to tag its icon views -->
+    <item type="id" name="status_bar_view_state_tag" />
 </resources>
 
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 6437903..ab83bcf 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -228,6 +228,8 @@
     <string name="accessibility_menu">Menu</string>
     <!-- Content description of the accessibility button in the navigation bar (not shown on the screen). [CHAR LIMIT=NONE] -->
     <string name="accessibility_accessibility_button">Accessibility</string>
+    <!-- Content description of the rotate button in the navigation bar (not shown on the screen). [CHAR LIMIT=NONE] -->
+    <string name="accessibility_rotate_button">Rotate screen</string>
     <!-- Content description of the recents button for accessibility (not shown on the screen). [CHAR LIMIT=NONE] -->
     <string name="accessibility_recent">Overview</string>
     <!-- Content description of the search button for accessibility. [CHAR LIMIT=NONE] -->
@@ -1702,12 +1704,14 @@
         <item>Clipboard</item>
         <item>Keycode</item>
         <item>Keyboard switcher</item>
+        <item>Rotation suggestion</item>
         <item>None</item>
     </string-array>
     <string-array name="nav_bar_button_values" translatable="false">
         <item>clipboard</item>
         <item>key</item>
         <item>menu_ime</item>
+        <item>rotate</item>
         <item>space</item>
     </string-array>
 
@@ -2065,5 +2069,21 @@
     <string name="touch_filtered_warning">Because an app is obscuring a permission request, Settings
         can’t verify your response.</string>
 
+    <!-- Title of prompt requesting access to display slices [CHAR LIMIT=NONE] -->
+    <string name="slice_permission_title">Allow <xliff:g id="app" example="Example App">%1$s</xliff:g> to show <xliff:g id="app_2" example="Other Example App">%2$s</xliff:g> slices?</string>
+
+    <!-- Description of what kind of access is given to a slice host [CHAR LIMIT=NONE] -->
+    <string name="slice_permission_text_1"> - It can read information from <xliff:g id="app" example="Example App">%1$s</xliff:g></string>
+    <!-- Description of what kind of access is given to a slice host [CHAR LIMIT=NONE] -->
+    <string name="slice_permission_text_2"> - It can take actions inside <xliff:g id="app" example="Example App">%1$s</xliff:g></string>
+
+    <!-- Text on checkbox allowing the app to show slices from all apps [CHAR LIMIT=NONE] -->
+    <string name="slice_permission_checkbox">Allow <xliff:g id="app" example="Example App">%1$s</xliff:g> to show slices from any app</string>
+
+    <!-- Option to grant the slice permission request on the screen [CHAR LIMIT=15] -->
+    <string name="slice_permission_allow">Allow</string>
+
+    <!-- Option to grant the slice permission request on the screen [CHAR LIMIT=15] -->
+    <string name="slice_permission_deny">Deny</string>
 
 </resources>
diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml
index 4837fef..bcce6d1 100644
--- a/packages/SystemUI/res/values/styles.xml
+++ b/packages/SystemUI/res/values/styles.xml
@@ -173,7 +173,7 @@
     <style name="TextAppearance.StatusBar.Expanded.Date">
         <item name="android:textSize">@dimen/qs_time_expanded_size</item>
         <item name="android:textStyle">normal</item>
-        <item name="android:textColor">?android:attr/textColorPrimary</item>
+        <item name="android:textColor">#ffffffff</item>
         <item name="android:fontFamily">sans-serif</item>
     </style>
 
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/utilities/Utilities.java b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/utilities/Utilities.java
index 13f30b2..7d159b7 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/utilities/Utilities.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/utilities/Utilities.java
@@ -20,6 +20,7 @@
 import android.animation.AnimatorSet;
 import android.animation.RectEvaluator;
 import android.annotation.FloatRange;
+import android.annotation.Nullable;
 import android.app.Activity;
 import android.content.Context;
 import android.content.res.Configuration;
@@ -39,6 +40,7 @@
 import android.view.View;
 import android.view.ViewGroup;
 import android.view.ViewParent;
+import android.view.ViewRootImpl;
 import android.view.ViewStub;
 
 import java.util.ArrayList;
@@ -294,17 +296,25 @@
     }
 
     /**
-     * @return The next frame name for the specified surface.
+     * @return The next frame name for the specified surface or -1 if the surface is no longer
+     *         valid.
      */
     public static long getNextFrameNumber(Surface s) {
-        return s.getNextFrameNumber();
+        return s != null && s.isValid()
+                ? s.getNextFrameNumber()
+                : -1;
+
     }
 
     /**
      * @return The surface for the specified view.
      */
-    public static Surface getSurface(View v) {
-        return v.getViewRootImpl().mSurface;
+    public static @Nullable Surface getSurface(View v) {
+        ViewRootImpl viewRoot = v.getViewRootImpl();
+        if (viewRoot == null) {
+            return null;
+        }
+        return viewRoot.mSurface;
     }
 
     /**
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
index e58ad05..19afcf5 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
@@ -991,6 +991,12 @@
                     maxChargingWattage > fastThreshold ? CHARGING_FAST :
                     CHARGING_REGULAR;
         }
+
+        @Override
+        public String toString() {
+            return "BatteryStatus{status=" + status + ",level=" + level + ",plugged=" + plugged
+                    + ",health=" + health + ",maxChargingWattage=" + maxChargingWattage + "}";
+        }
     }
 
     public class StrongAuthTracker extends LockPatternUtils.StrongAuthTracker {
@@ -1624,18 +1630,8 @@
             return true;
         }
 
-        // change in battery level while plugged in
-        if (nowPluggedIn && old.level != current.level) {
-            return true;
-        }
-
-        // change in battery level while keyguard visible
-        if (mKeyguardIsVisible && old.level != current.level) {
-            return true;
-        }
-
-        // change where battery needs charging
-        if (!nowPluggedIn && current.isBatteryLow() && current.level != old.level) {
+        // change in battery level
+        if (old.level != current.level) {
             return true;
         }
 
diff --git a/packages/SystemUI/src/com/android/systemui/BatteryMeterView.java b/packages/SystemUI/src/com/android/systemui/BatteryMeterView.java
index 2fe66a1..8666b0c 100644
--- a/packages/SystemUI/src/com/android/systemui/BatteryMeterView.java
+++ b/packages/SystemUI/src/com/android/systemui/BatteryMeterView.java
@@ -224,7 +224,6 @@
                 if (mTextColor != 0) mBatteryPercentView.setTextColor(mTextColor);
                 updatePercentText();
                 addView(mBatteryPercentView,
-                        0,
                         new ViewGroup.LayoutParams(
                                 LayoutParams.WRAP_CONTENT,
                                 LayoutParams.MATCH_PARENT));
diff --git a/packages/SystemUI/src/com/android/systemui/RecentsComponent.java b/packages/SystemUI/src/com/android/systemui/RecentsComponent.java
index 880ae70..f9dbf4a 100644
--- a/packages/SystemUI/src/com/android/systemui/RecentsComponent.java
+++ b/packages/SystemUI/src/com/android/systemui/RecentsComponent.java
@@ -21,7 +21,7 @@
 import android.view.View;
 
 public interface RecentsComponent {
-    void showRecentApps(boolean triggeredFromAltTab, boolean fromHome);
+    void showRecentApps(boolean triggeredFromAltTab);
     void showNextAffiliatedTask();
     void showPrevAffiliatedTask();
 
diff --git a/packages/SystemUI/src/com/android/systemui/SlicePermissionActivity.java b/packages/SystemUI/src/com/android/systemui/SlicePermissionActivity.java
new file mode 100644
index 0000000..302face
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/SlicePermissionActivity.java
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the
+ * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the specific language governing
+ * permissions and limitations under the License.
+ */
+
+package com.android.systemui;
+
+import android.app.Activity;
+import android.app.AlertDialog;
+import android.app.slice.SliceManager;
+import android.app.slice.SliceProvider;
+import android.content.DialogInterface;
+import android.content.DialogInterface.OnClickListener;
+import android.content.DialogInterface.OnDismissListener;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.net.Uri;
+import android.os.Bundle;
+import android.util.Log;
+import android.widget.CheckBox;
+import android.widget.TextView;
+
+public class SlicePermissionActivity extends Activity implements OnClickListener,
+        OnDismissListener {
+
+    private static final String TAG = "SlicePermissionActivity";
+
+    private CheckBox mAllCheckbox;
+
+    private Uri mUri;
+    private String mCallingPkg;
+    private String mProviderPkg;
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+
+        mUri = getIntent().getParcelableExtra(SliceProvider.EXTRA_BIND_URI);
+        mCallingPkg = getIntent().getStringExtra(SliceProvider.EXTRA_PKG);
+        mProviderPkg = getIntent().getStringExtra(SliceProvider.EXTRA_PROVIDER_PKG);
+
+        try {
+            PackageManager pm = getPackageManager();
+            CharSequence app1 = pm.getApplicationInfo(mCallingPkg, 0).loadLabel(pm);
+            CharSequence app2 = pm.getApplicationInfo(mProviderPkg, 0).loadLabel(pm);
+            AlertDialog dialog = new AlertDialog.Builder(this)
+                    .setTitle(getString(R.string.slice_permission_title, app1, app2))
+                    .setView(R.layout.slice_permission_request)
+                    .setNegativeButton(R.string.slice_permission_deny, this)
+                    .setPositiveButton(R.string.slice_permission_allow, this)
+                    .setOnDismissListener(this)
+                    .show();
+            TextView t1 = dialog.getWindow().getDecorView().findViewById(R.id.text1);
+            t1.setText(getString(R.string.slice_permission_text_1, app2));
+            TextView t2 = dialog.getWindow().getDecorView().findViewById(R.id.text2);
+            t2.setText(getString(R.string.slice_permission_text_2, app2));
+            mAllCheckbox = dialog.getWindow().getDecorView().findViewById(
+                    R.id.slice_permission_checkbox);
+            mAllCheckbox.setText(getString(R.string.slice_permission_checkbox, app1));
+        } catch (NameNotFoundException e) {
+            Log.e(TAG, "Couldn't find package", e);
+            finish();
+        }
+    }
+
+    @Override
+    public void onClick(DialogInterface dialog, int which) {
+        if (which == DialogInterface.BUTTON_POSITIVE) {
+            getSystemService(SliceManager.class).grantPermissionFromUser(mUri, mCallingPkg,
+                    mAllCheckbox.isChecked());
+        }
+        finish();
+    }
+
+    @Override
+    public void onDismiss(DialogInterface dialog) {
+        finish();
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/SwipeHelper.java b/packages/SystemUI/src/com/android/systemui/SwipeHelper.java
index 592dda0..a64ce29 100644
--- a/packages/SystemUI/src/com/android/systemui/SwipeHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/SwipeHelper.java
@@ -21,6 +21,7 @@
 import android.animation.ObjectAnimator;
 import android.animation.ValueAnimator;
 import android.animation.ValueAnimator.AnimatorUpdateListener;
+import android.annotation.NonNull;
 import android.content.Context;
 import android.content.res.Resources;
 import android.graphics.RectF;
@@ -316,10 +317,12 @@
                     float deltaPerpendicular = perpendicularPos - mPerpendicularInitialTouchPos;
                     if (Math.abs(delta) > mPagingTouchSlop
                             && Math.abs(delta) > Math.abs(deltaPerpendicular)) {
-                        mCallback.onBeginDrag(mCurrView);
-                        mDragging = true;
-                        mInitialTouchPos = getPos(ev);
-                        mTranslation = getTranslation(mCurrView);
+                        if (mCallback.canChildBeDragged(mCurrView)) {
+                            mCallback.onBeginDrag(mCurrView);
+                            mDragging = true;
+                            mInitialTouchPos = getPos(ev);
+                            mTranslation = getTranslation(mCurrView);
+                        }
                         cancelLongPress();
                     }
                 }
@@ -722,5 +725,10 @@
          * @return The factor the falsing threshold should be multiplied with
          */
         float getFalsingThresholdFactor();
+
+        /**
+         * @return If true, the given view is draggable.
+         */
+        default boolean canChildBeDragged(@NonNull View animView) { return true; }
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/analytics/DataCollector.java b/packages/SystemUI/src/com/android/systemui/analytics/DataCollector.java
index 931a994..69e347c9 100644
--- a/packages/SystemUI/src/com/android/systemui/analytics/DataCollector.java
+++ b/packages/SystemUI/src/com/android/systemui/analytics/DataCollector.java
@@ -463,4 +463,8 @@
     public boolean isReportingEnabled() {
         return mAllowReportRejectedTouch;
     }
+
+    public void onFalsingSessionStarted() {
+        sessionEntrypoint();
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/FalsingManager.java b/packages/SystemUI/src/com/android/systemui/classifier/FalsingManager.java
index e4b405f..ed659e2 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/FalsingManager.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/FalsingManager.java
@@ -167,6 +167,9 @@
         if (mDataCollector.isEnabledFull()) {
             registerSensors(COLLECTOR_SENSORS);
         }
+        if (mDataCollector.isEnabled()) {
+            mDataCollector.onFalsingSessionStarted();
+        }
     }
 
     private void registerSensors(int [] sensors) {
diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/KeyboardUI.java b/packages/SystemUI/src/com/android/systemui/keyboard/KeyboardUI.java
index 4b775a5..b8411e2 100644
--- a/packages/SystemUI/src/com/android/systemui/keyboard/KeyboardUI.java
+++ b/packages/SystemUI/src/com/android/systemui/keyboard/KeyboardUI.java
@@ -608,6 +608,9 @@
         public void onScanningStateChanged(boolean started) { }
         @Override
         public void onConnectionStateChanged(CachedBluetoothDevice cachedDevice, int state) { }
+        @Override
+        public void onActiveDeviceChanged(CachedBluetoothDevice activeDevice,
+                                          int bluetoothProfile) { }
     }
 
     private final class BluetoothErrorListener implements Utils.ErrorListener {
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java
index 2b48e0f..c0fed34 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java
@@ -42,9 +42,8 @@
 import android.view.accessibility.AccessibilityManager;
 import android.view.accessibility.AccessibilityNodeInfo;
 import android.view.accessibility.AccessibilityWindowInfo;
-
-import com.android.internal.logging.MetricsLogger;
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
+import com.android.internal.os.logging.MetricsLoggerWrapper;
 import com.android.internal.policy.PipSnapAlgorithm;
 import com.android.systemui.R;
 import com.android.systemui.statusbar.FlingAnimationUtils;
@@ -63,10 +62,6 @@
     // Allow the PIP to be flung from anywhere on the screen to the bottom to be dismissed.
     private static final boolean ENABLE_FLING_DISMISS = false;
 
-    // These values are used for metrics and should never change
-    private static final int METRIC_VALUE_DISMISSED_BY_TAP = 0;
-    private static final int METRIC_VALUE_DISMISSED_BY_DRAG = 1;
-
     private static final int SHOW_DISMISS_AFFORDANCE_DELAY = 225;
 
     // Allow dragging the PIP to a location to close it
@@ -163,8 +158,7 @@
         @Override
         public void onPipDismiss() {
             mMotionHelper.dismissPip();
-            MetricsLogger.action(mContext, MetricsEvent.ACTION_PICTURE_IN_PICTURE_DISMISSED,
-                    METRIC_VALUE_DISMISSED_BY_TAP);
+            MetricsLoggerWrapper.logPictureInPictureDismissByTap(mContext);
         }
 
         @Override
@@ -463,8 +457,7 @@
             return;
         }
         if (mIsMinimized != isMinimized) {
-            MetricsLogger.action(mContext, MetricsEvent.ACTION_PICTURE_IN_PICTURE_MINIMIZED,
-                    isMinimized);
+            MetricsLoggerWrapper.logPictureInPictureMinimize(mContext, isMinimized);
         }
         mIsMinimized = isMinimized;
         mSnapAlgorithm.setMinimized(isMinimized);
@@ -537,8 +530,7 @@
         mMenuState = menuState;
         updateMovementBounds(menuState);
         if (menuState != MENU_STATE_CLOSE) {
-            MetricsLogger.visibility(mContext, MetricsEvent.ACTION_PICTURE_IN_PICTURE_MENU,
-                    menuState == MENU_STATE_FULL);
+            MetricsLoggerWrapper.logPictureInPictureMenuVisible(mContext, menuState == MENU_STATE_FULL);
         }
     }
 
@@ -670,9 +662,7 @@
                 if (mMotionHelper.shouldDismissPip() || isFlingToBot) {
                     mMotionHelper.animateDismiss(mMotionHelper.getBounds(), vel.x,
                         vel.y, mUpdateScrimListener);
-                    MetricsLogger.action(mContext,
-                            MetricsEvent.ACTION_PICTURE_IN_PICTURE_DISMISSED,
-                            METRIC_VALUE_DISMISSED_BY_DRAG);
+                    MetricsLoggerWrapper.logPictureInPictureDismissByDrag(mContext);
                     return true;
                 }
             }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java b/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java
index 7f0acc2..7320b86 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java
@@ -45,8 +45,9 @@
     protected float mQsExpansion;
     private QSCustomizer mQSCustomizer;
     private View mQSFooter;
-    private float mFullElevation;
+    private View mBackground;
     private float mRadius;
+    private int mSideMargins;
 
     public QSContainerImpl(Context context, AttributeSet attrs) {
         super(context, attrs);
@@ -60,12 +61,14 @@
         mHeader = findViewById(R.id.header);
         mQSCustomizer = findViewById(R.id.qs_customize);
         mQSFooter = findViewById(R.id.qs_footer);
-        mFullElevation = mQSPanel.getElevation();
+        mBackground = findViewById(R.id.quick_settings_background);
         mRadius = getResources().getDimensionPixelSize(
                 Utils.getThemeAttr(mContext, android.R.attr.dialogCornerRadius));
+        mSideMargins = getResources().getDimensionPixelSize(R.dimen.notification_side_paddings);
 
         setClickable(true);
         setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_NO);
+        setMargins();
     }
 
     @Override
@@ -131,6 +134,8 @@
         mQSDetail.setBottom(getTop() + height);
         // Pin QS Footer to the bottom of the panel.
         mQSFooter.setTranslationY(height - mQSFooter.getHeight());
+        mBackground.setTop(mQSPanel.getTop());
+        mBackground.setBottom(height);
 
         ExpandableOutlineView.getRoundedRectPath(0, 0, getWidth(), height, mRadius,
                 mRadius,
@@ -148,4 +153,19 @@
         mQsExpansion = expansion;
         updateExpansion();
     }
+
+    private void setMargins() {
+        setMargins(mQSDetail);
+        setMargins(mBackground);
+        setMargins(mQSFooter);
+        setMargins(mQSPanel);
+        setMargins(mHeader);
+        setMargins(mQSCustomizer);
+    }
+
+    private void setMargins(View view) {
+        FrameLayout.LayoutParams lp = (LayoutParams) view.getLayoutParams();
+        lp.rightMargin = mSideMargins;
+        lp.leftMargin = mSideMargins;
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSFooterImpl.java b/packages/SystemUI/src/com/android/systemui/qs/QSFooterImpl.java
index 927a49c..92475da 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSFooterImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSFooterImpl.java
@@ -61,18 +61,16 @@
 public class QSFooterImpl extends FrameLayout implements QSFooter,
         OnClickListener, OnUserInfoChangedListener, EmergencyListener,
         SignalCallback, CommandQueue.Callbacks {
-    private static final float EXPAND_INDICATOR_THRESHOLD = .93f;
-
     private ActivityStarter mActivityStarter;
     private UserInfoController mUserInfoController;
     private SettingsButton mSettingsButton;
     protected View mSettingsContainer;
+    private View mCarrierText;
 
     private boolean mQsDisabled;
     private QSPanel mQsPanel;
 
     private boolean mExpanded;
-    protected ExpandableIndicator mExpandIndicator;
 
     private boolean mListening;
 
@@ -100,18 +98,18 @@
                 Dependency.get(ActivityStarter.class).postQSRunnableDismissingKeyguard(() ->
                         mQsPanel.showEdit(view)));
 
-        mExpandIndicator = findViewById(R.id.expand_indicator);
         mSettingsButton = findViewById(R.id.settings_button);
         mSettingsContainer = findViewById(R.id.settings_button_container);
         mSettingsButton.setOnClickListener(this);
 
+        mCarrierText = findViewById(R.id.qs_carrier_text);
+
         mMultiUserSwitch = findViewById(R.id.multi_user_switch);
         mMultiUserAvatar = mMultiUserSwitch.findViewById(R.id.multi_user_avatar);
 
         // RenderThread is doing more harm than good when touching the header (to expand quick
         // settings), so disable it for this view
         ((RippleDrawable) mSettingsButton.getBackground()).setForceSoftware(true);
-        ((RippleDrawable) mExpandIndicator.getBackground()).setForceSoftware(true);
 
         updateResources();
 
@@ -162,6 +160,8 @@
         return new TouchAnimator.Builder()
                 .addFloat(mEdit, "alpha", 0, 1)
                 .addFloat(mMultiUserSwitch, "alpha", 0, 1)
+                .addFloat(mCarrierText, "alpha", 0, 1)
+                .addFloat(mSettingsButton, "alpha", 0, 1)
                 .build();
     }
 
@@ -185,8 +185,6 @@
         if (mSettingsAlpha != null) {
             mSettingsAlpha.setPosition(headerExpansionFraction);
         }
-
-        mExpandIndicator.setExpanded(headerExpansionFraction > EXPAND_INDICATOR_THRESHOLD);
     }
 
     @Override
@@ -237,8 +235,6 @@
         mSettingsContainer.findViewById(R.id.tuner_icon).setVisibility(
                 TunerService.isTunerEnabled(mContext) ? View.VISIBLE : View.INVISIBLE);
 
-        mExpandIndicator.setVisibility(mQsDisabled ? View.GONE : View.VISIBLE);
-
         final boolean isDemo = UserManager.isDeviceInDemoMode(mContext);
 
 
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
index 398592a..a97b35c 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
@@ -18,10 +18,12 @@
 import static android.app.StatusBarManager.DISABLE_NONE;
 
 import android.content.Context;
+import android.content.Intent;
 import android.content.res.Configuration;
 import android.content.res.Resources;
 import android.graphics.Color;
 import android.graphics.Rect;
+import android.provider.AlarmClock;
 import android.support.annotation.VisibleForTesting;
 import android.util.AttributeSet;
 import android.view.View;
@@ -38,9 +40,11 @@
 import com.android.systemui.qs.QSDetail.Callback;
 import com.android.systemui.statusbar.CommandQueue;
 import com.android.systemui.statusbar.SignalClusterView;
+import com.android.systemui.statusbar.policy.DarkIconDispatcher;
 import com.android.systemui.statusbar.policy.DarkIconDispatcher.DarkReceiver;
 
-public class QuickStatusBarHeader extends RelativeLayout implements CommandQueue.Callbacks {
+public class QuickStatusBarHeader extends RelativeLayout
+        implements CommandQueue.Callbacks, View.OnClickListener {
 
     private ActivityStarter mActivityStarter;
 
@@ -53,6 +57,8 @@
     protected QuickQSPanel mHeaderQsPanel;
     protected QSTileHost mHost;
 
+    private View mDate;
+
     public QuickStatusBarHeader(Context context, AttributeSet attrs) {
         super(context, attrs);
     }
@@ -63,21 +69,21 @@
         Resources res = getResources();
 
         mHeaderQsPanel = findViewById(R.id.quick_qs_panel);
+        mDate = findViewById(R.id.date);
+        mDate.setOnClickListener(this);
 
         // RenderThread is doing more harm than good when touching the header (to expand quick
         // settings), so disable it for this view
 
         updateResources();
 
-        // Set the light/dark theming on the header status UI to match the current theme.
+        // Set light text on the header icons because they will always be on a black background
         int colorForeground = Utils.getColorAttr(getContext(), android.R.attr.colorForeground);
-        float intensity = colorForeground == Color.WHITE ? 0 : 1;
         Rect tintArea = new Rect(0, 0, 0, 0);
-
-        applyDarkness(R.id.battery, tintArea, intensity, colorForeground);
-        applyDarkness(R.id.clock, tintArea, intensity, colorForeground);
+        applyDarkness(R.id.clock, tintArea, 0, DarkIconDispatcher.DEFAULT_ICON_TINT);
 
         BatteryMeterView battery = findViewById(R.id.battery);
+        battery.setFillColor(Color.WHITE);
         battery.setForceShowPercent(true);
 
         mActivityStarter = Dependency.get(ActivityStarter.class);
@@ -146,6 +152,14 @@
         super.onDetachedFromWindow();
     }
 
+    @Override
+    public void onClick(View v) {
+        if (v == mDate) {
+            Dependency.get(ActivityStarter.class).postStartActivityDismissingKeyguard(new Intent(
+                    AlarmClock.ACTION_SHOW_ALARMS), 0);
+        }
+    }
+
     public void setListening(boolean listening) {
         if (listening == mListening) {
             return;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java
index 9e265e22..4ceace3 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java
@@ -33,7 +33,6 @@
 import android.service.notification.ZenModeConfig;
 import android.service.notification.ZenModeConfig.ZenRule;
 import android.service.quicksettings.Tile;
-import android.util.Log;
 import android.util.Slog;
 import android.view.LayoutInflater;
 import android.view.View;
@@ -55,7 +54,6 @@
 import com.android.systemui.qs.QSHost;
 import com.android.systemui.qs.tileimpl.QSTileImpl;
 import com.android.systemui.statusbar.policy.ZenModeController;
-import com.android.systemui.statusbar.policy.ZenModeController.Callback;
 import com.android.systemui.volume.ZenModePanel;
 
 /** Quick settings tile: Do not disturb **/
@@ -134,8 +132,7 @@
         if (mState.value) {
             mController.setZen(ZEN_MODE_OFF, null, TAG);
         } else {
-            int zen = Prefs.getInt(mContext, Prefs.Key.DND_FAVORITE_ZEN, Global.ZEN_MODE_ALARMS);
-            mController.setZen(zen, null, TAG);
+            mController.setZen(Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS, null, TAG);
         }
     }
 
@@ -159,9 +156,7 @@
                     showDetail(true);
                 }
             });
-            int zen = Prefs.getInt(mContext, Prefs.Key.DND_FAVORITE_ZEN,
-                    Global.ZEN_MODE_ALARMS);
-            mController.setZen(zen, null, TAG);
+            mController.setZen(Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS, null, TAG);
         } else {
             showDetail(true);
         }
@@ -313,9 +308,7 @@
                 mController.setZen(ZEN_MODE_OFF, null, TAG);
                 mAuto = false;
             } else {
-                int zen = Prefs.getInt(mContext, Prefs.Key.DND_FAVORITE_ZEN,
-                        ZEN_MODE_ALARMS);
-                mController.setZen(zen, null, TAG);
+                mController.setZen(Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS, null, TAG);
             }
         }
 
diff --git a/packages/SystemUI/src/com/android/systemui/recents/IRecentsNonSystemUserCallbacks.aidl b/packages/SystemUI/src/com/android/systemui/recents/IRecentsNonSystemUserCallbacks.aidl
index 5ae7f22c..fc1831d 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/IRecentsNonSystemUserCallbacks.aidl
+++ b/packages/SystemUI/src/com/android/systemui/recents/IRecentsNonSystemUserCallbacks.aidl
@@ -27,7 +27,7 @@
     void preloadRecents();
     void cancelPreloadingRecents();
     void showRecents(boolean triggeredFromAltTab, boolean draggingInRecents, boolean animate,
-            boolean reloadTasks, boolean fromHome, int recentsGrowTarget);
+            int recentsGrowTarget);
     void hideRecents(boolean triggeredFromAltTab, boolean triggeredFromHomeKey);
     void toggleRecents(int recentsGrowTarget);
     void onConfigurationChanged();
diff --git a/packages/SystemUI/src/com/android/systemui/recents/Recents.java b/packages/SystemUI/src/com/android/systemui/recents/Recents.java
index 5b62c7d..1da4deb 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/Recents.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/Recents.java
@@ -240,7 +240,7 @@
      * Shows the Recents.
      */
     @Override
-    public void showRecentApps(boolean triggeredFromAltTab, boolean fromHome) {
+    public void showRecentApps(boolean triggeredFromAltTab) {
         // Ensure the device has been provisioned before allowing the user to interact with
         // recents
         if (!isUserSetup()) {
@@ -252,7 +252,7 @@
         int currentUser = sSystemServicesProxy.getCurrentUser();
         if (sSystemServicesProxy.isSystemUser(currentUser)) {
             mImpl.showRecents(triggeredFromAltTab, false /* draggingInRecents */,
-                    true /* animate */, false /* reloadTasks */, fromHome, recentsGrowTarget);
+                    true /* animate */, recentsGrowTarget);
         } else {
             if (mSystemToUserCallbacks != null) {
                 IRecentsNonSystemUserCallbacks callbacks =
@@ -260,8 +260,7 @@
                 if (callbacks != null) {
                     try {
                         callbacks.showRecents(triggeredFromAltTab, false /* draggingInRecents */,
-                                true /* animate */, false /* reloadTasks */, fromHome,
-                                recentsGrowTarget);
+                                true /* animate */, recentsGrowTarget);
                     } catch (RemoteException e) {
                         Log.e(TAG, "Callback failed", e);
                     }
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
index 06dfd18..b0a2fad 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
@@ -356,15 +356,15 @@
         registerReceiver(mSystemBroadcastReceiver, filter);
 
         getWindow().addPrivateFlags(LayoutParams.PRIVATE_FLAG_NO_MOVE_ANIMATION);
-
-        // Reload the stack view
-        reloadStackView();
     }
 
     @Override
     protected void onStart() {
         super.onStart();
 
+        // Reload the stack view whenever we are made visible again
+        reloadStackView();
+
         // Notify that recents is now visible
         EventBus.getDefault().send(new RecentsVisibilityChangedEvent(this, true));
         MetricsLogger.visible(this, MetricsEvent.OVERVIEW_ACTIVITY);
@@ -411,14 +411,6 @@
         }
     }
 
-    @Override
-    protected void onNewIntent(Intent intent) {
-        super.onNewIntent(intent);
-
-        // Reload the stack view
-        reloadStackView();
-    }
-
     /**
      * Reloads the stack views upon launching Recents.
      */
@@ -530,7 +522,11 @@
         // Set the window background
         mRecentsView.updateBackgroundScrim(getWindow(), isInMultiWindowMode);
 
-        reloadTaskStack(isInMultiWindowMode, true /* sendConfigChangedEvent */);
+        // Reload the task stack view if we are still visible to pick up the change in tasks that
+        // result from entering/exiting multi-window
+        if (mIsVisible) {
+            reloadTaskStack(isInMultiWindowMode, true /* sendConfigChangedEvent */);
+        }
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java
index 8359690..ee1b091 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java
@@ -255,7 +255,6 @@
             // When this fires, then the user has not released alt-tab for at least
             // FAST_ALT_TAB_DELAY_MS milliseconds
             showRecents(mTriggeredFromAltTab, false /* draggingInRecents */, true /* animate */,
-                    false /* reloadTasks */, false /* fromHome */,
                     DividerView.INVALID_RECENTS_GROW_TARGET);
         }
     });
@@ -322,8 +321,15 @@
     }
 
     public void showRecents(boolean triggeredFromAltTab, boolean draggingInRecents,
-            boolean animate, boolean launchedWhileDockingTask, boolean fromHome,
-            int growTarget) {
+            boolean animate, int growTarget) {
+        final SystemServicesProxy ssp = Recents.getSystemServices();
+        final MutableBoolean isHomeStackVisible = new MutableBoolean(true);
+        final boolean isRecentsVisible = Recents.getSystemServices().isRecentsActivityVisible(
+                isHomeStackVisible);
+        final boolean fromHome = isHomeStackVisible.value;
+        final boolean launchedWhileDockingTask =
+                Recents.getSystemServices().getSplitScreenPrimaryStack() != null;
+
         mTriggeredFromAltTab = triggeredFromAltTab;
         mDraggingInRecents = draggingInRecents;
         mLaunchedWhileDocking = launchedWhileDockingTask;
@@ -349,10 +355,8 @@
 
         try {
             // Check if the top task is in the home stack, and start the recents activity
-            SystemServicesProxy ssp = Recents.getSystemServices();
-            boolean forceVisible = launchedWhileDockingTask || draggingInRecents;
-            MutableBoolean isHomeStackVisible = new MutableBoolean(forceVisible);
-            if (forceVisible || !ssp.isRecentsActivityVisible(isHomeStackVisible)) {
+            final boolean forceVisible = launchedWhileDockingTask || draggingInRecents;
+            if (forceVisible || !isRecentsVisible) {
                 ActivityManager.RunningTaskInfo runningTask =
                         ActivityManagerWrapper.getInstance().getRunningTask();
                 startRecentsActivityAndDismissKeyguardIfNeeded(runningTask,
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsImplProxy.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsImplProxy.java
index 9493c78..beec4b3 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsImplProxy.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsImplProxy.java
@@ -58,15 +58,12 @@
 
     @Override
     public void showRecents(boolean triggeredFromAltTab, boolean draggingInRecents, boolean animate,
-            boolean reloadTasks, boolean fromHome, int growTarget)
-            throws RemoteException {
+            int growTarget) throws RemoteException {
         SomeArgs args = SomeArgs.obtain();
         args.argi1 = triggeredFromAltTab ? 1 : 0;
         args.argi2 = draggingInRecents ? 1 : 0;
         args.argi3 = animate ? 1 : 0;
-        args.argi4 = reloadTasks ? 1 : 0;
-        args.argi5 = fromHome ? 1 : 0;
-        args.argi6 = growTarget;
+        args.argi4 = growTarget;
         mHandler.sendMessage(mHandler.obtainMessage(MSG_SHOW_RECENTS, args));
     }
 
@@ -130,7 +127,7 @@
                 case MSG_SHOW_RECENTS:
                     args = (SomeArgs) msg.obj;
                     mImpl.showRecents(args.argi1 != 0, args.argi2 != 0, args.argi3 != 0,
-                            args.argi4 != 0, args.argi5 != 0, args.argi6);
+                            args.argi4);
                     break;
                 case MSG_HIDE_RECENTS:
                     mImpl.hideRecents(msg.arg1 != 0, msg.arg2 != 0);
diff --git a/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java b/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java
index 130a5e3..613d9fb 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java
@@ -274,20 +274,21 @@
         return false;
     }
 
+    public ActivityManager.StackInfo getSplitScreenPrimaryStack() {
+        try {
+            return mIam.getStackInfo(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, ACTIVITY_TYPE_UNDEFINED);
+        } catch (RemoteException e) {
+            return null;
+        }
+    }
+
     /**
      * @return whether there are any docked tasks for the current user.
      */
     public boolean hasDockedTask() {
         if (mIam == null) return false;
 
-        ActivityManager.StackInfo stackInfo = null;
-        try {
-            stackInfo =
-                    mIam.getStackInfo(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, ACTIVITY_TYPE_UNDEFINED);
-        } catch (RemoteException e) {
-            e.printStackTrace();
-        }
-
+        ActivityManager.StackInfo stackInfo = getSplitScreenPrimaryStack();
         if (stackInfo != null) {
             int userId = getCurrentUser();
             boolean hasUserTask = false;
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
index 36c9095..5be2900 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
@@ -210,7 +210,7 @@
     private boolean mStackActionButtonVisible;
 
     // Percentage of last ScrollP from the min to max scrollP that lives after configuration changes
-    private float mLastScrollPPercent;
+    private float mLastScrollPPercent = -1;
 
     // We keep track of the task view focused by user interaction and draw a frame around it in the
     // grid layout.
@@ -647,14 +647,12 @@
      * an animation provided in {@param animationOverrides}, that will be used instead.
      */
     private void relayoutTaskViews(AnimationProps animation,
-            ArrayMap<Task, AnimationProps> animationOverrides,
-            boolean ignoreTaskOverrides) {
+            ArrayMap<Task, AnimationProps> animationOverrides, boolean ignoreTaskOverrides) {
         // If we had a deferred animation, cancel that
         cancelDeferredTaskViewLayoutAnimation();
 
         // Synchronize the current set of TaskViews
-        bindVisibleTaskViews(mStackScroller.getStackScroll(),
-                ignoreTaskOverrides /* ignoreTaskOverrides */);
+        bindVisibleTaskViews(mStackScroller.getStackScroll(), ignoreTaskOverrides);
 
         // Animate them to their final transforms with the given animation
         List<TaskView> taskViews = getTaskViews();
@@ -2067,8 +2065,11 @@
         // Update the Clear All button in case we're switching in or out of grid layout.
         updateStackActionButtonVisibility();
 
-        // Trigger a new layout and update to the initial state if necessary
-        if (event.fromMultiWindow) {
+        // Trigger a new layout and update to the initial state if necessary. When entering split
+        // screen, the multi-window configuration change event can happen after the stack is already
+        // reloaded (but pending measure/layout), in this case, do not override the intiial state
+        // and just wait for the upcoming measure/layout pass.
+        if (event.fromMultiWindow && mInitialState == INITIAL_STATE_UPDATE_NONE) {
             mInitialState = INITIAL_STATE_UPDATE_LAYOUT_ONLY;
             requestLayout();
         } else if (event.fromDeviceOrientationChange) {
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java b/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java
index 6db46b5..675aa8f 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java
@@ -293,12 +293,13 @@
             sharingIntent.putExtra(Intent.EXTRA_STREAM, uri);
             sharingIntent.putExtra(Intent.EXTRA_SUBJECT, subject);
 
-            // Create a share action for the notification. Note, we proxy the call to ShareReceiver
-            // because RemoteViews currently forces an activity options on the PendingIntent being
-            // launched, and since we don't want to trigger the share sheet in this case, we will
-            // start the chooser activitiy directly in ShareReceiver.
+            // Create a share action for the notification. Note, we proxy the call to
+            // ScreenshotActionReceiver because RemoteViews currently forces an activity options
+            // on the PendingIntent being launched, and since we don't want to trigger the share
+            // sheet in this case, we start the chooser activity directly in
+            // ScreenshotActionReceiver.
             PendingIntent shareAction = PendingIntent.getBroadcast(context, 0,
-                    new Intent(context, GlobalScreenshot.ShareReceiver.class)
+                    new Intent(context, GlobalScreenshot.ScreenshotActionReceiver.class)
                             .putExtra(SHARING_INTENT, sharingIntent),
                     PendingIntent.FLAG_CANCEL_CURRENT);
             Notification.Action.Builder shareActionBuilder = new Notification.Action.Builder(
@@ -306,15 +307,19 @@
                     r.getString(com.android.internal.R.string.share), shareAction);
             mNotificationBuilder.addAction(shareActionBuilder.build());
 
-            // Create a delete action for the notification
-            PendingIntent deleteAction = PendingIntent.getBroadcast(context, 0,
-                    new Intent(context, GlobalScreenshot.DeleteScreenshotReceiver.class)
-                            .putExtra(GlobalScreenshot.SCREENSHOT_URI_ID, uri.toString()),
-                    PendingIntent.FLAG_CANCEL_CURRENT | PendingIntent.FLAG_ONE_SHOT);
-            Notification.Action.Builder deleteActionBuilder = new Notification.Action.Builder(
-                    R.drawable.ic_screenshot_delete,
-                    r.getString(com.android.internal.R.string.delete), deleteAction);
-            mNotificationBuilder.addAction(deleteActionBuilder.build());
+            Intent editIntent = new Intent(Intent.ACTION_EDIT);
+            editIntent.setType("image/png");
+            editIntent.putExtra(Intent.EXTRA_STREAM, uri);
+
+            // Create a edit action for the notification the same way.
+            PendingIntent editAction = PendingIntent.getBroadcast(context, 1,
+                    new Intent(context, GlobalScreenshot.ScreenshotActionReceiver.class)
+                            .putExtra(SHARING_INTENT, editIntent),
+                    PendingIntent.FLAG_CANCEL_CURRENT);
+            Notification.Action.Builder editActionBuilder = new Notification.Action.Builder(
+                    R.drawable.ic_screenshot_edit,
+                    r.getString(com.android.internal.R.string.screenshot_edit), editAction);
+            mNotificationBuilder.addAction(editActionBuilder.build());
 
             mParams.imageUri = uri;
             mParams.image = null;
@@ -879,9 +884,9 @@
     }
 
     /**
-     * Receiver to proxy the share intent.
+     * Receiver to proxy the share or edit intent.
      */
-    public static class ShareReceiver extends BroadcastReceiver {
+    public static class ScreenshotActionReceiver extends BroadcastReceiver {
         @Override
         public void onReceive(Context context, Intent intent) {
             try {
@@ -903,7 +908,7 @@
     }
 
     /**
-     * Removes the notification for a screenshot after a share target is chosen.
+     * Removes the notification for a screenshot after a share or edit target is chosen.
      */
     public static class TargetChosenReceiver extends BroadcastReceiver {
         @Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
index 8e1b104..c6abcf2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
@@ -116,7 +116,7 @@
         default void topAppWindowChanged(boolean visible) { }
         default void setImeWindowStatus(IBinder token, int vis, int backDisposition,
                 boolean showImeSwitcher) { }
-        default void showRecentApps(boolean triggeredFromAltTab, boolean fromHome) { }
+        default void showRecentApps(boolean triggeredFromAltTab) { }
         default void hideRecentApps(boolean triggeredFromAltTab, boolean triggeredFromHomeKey) { }
         default void toggleRecentApps() { }
         default void toggleSplitScreen() { }
@@ -144,7 +144,7 @@
         default void handleShowGlobalActionsMenu() { }
         default void handleShowShutdownUi(boolean isReboot, String reason) { }
 
-        default void onRotationProposal(int rotation) { }
+        default void onRotationProposal(int rotation, boolean isValid) { }
     }
 
     @VisibleForTesting
@@ -268,11 +268,11 @@
         }
     }
 
-    public void showRecentApps(boolean triggeredFromAltTab, boolean fromHome) {
+    public void showRecentApps(boolean triggeredFromAltTab) {
         synchronized (mLock) {
             mHandler.removeMessages(MSG_SHOW_RECENT_APPS);
-            mHandler.obtainMessage(MSG_SHOW_RECENT_APPS,
-                    triggeredFromAltTab ? 1 : 0, fromHome ? 1 : 0, null).sendToTarget();
+            mHandler.obtainMessage(MSG_SHOW_RECENT_APPS, triggeredFromAltTab ? 1 : 0, 0,
+                    null).sendToTarget();
         }
     }
 
@@ -462,10 +462,10 @@
     }
 
     @Override
-    public void onProposedRotationChanged(int rotation) {
+    public void onProposedRotationChanged(int rotation, boolean isValid) {
         synchronized (mLock) {
             mHandler.removeMessages(MSG_ROTATION_PROPOSAL);
-            mHandler.obtainMessage(MSG_ROTATION_PROPOSAL, rotation, 0,
+            mHandler.obtainMessage(MSG_ROTATION_PROPOSAL, rotation, isValid ? 1 : 0,
                     null).sendToTarget();
         }
     }
@@ -541,7 +541,7 @@
                     break;
                 case MSG_SHOW_RECENT_APPS:
                     for (int i = 0; i < mCallbacks.size(); i++) {
-                        mCallbacks.get(i).showRecentApps(msg.arg1 != 0, msg.arg2 != 0);
+                        mCallbacks.get(i).showRecentApps(msg.arg1 != 0);
                     }
                     break;
                 case MSG_HIDE_RECENT_APPS:
@@ -668,7 +668,7 @@
                     break;
                 case MSG_ROTATION_PROPOSAL:
                     for (int i = 0; i < mCallbacks.size(); i++) {
-                        mCallbacks.get(i).onRotationProposal(msg.arg1);
+                        mCallbacks.get(i).onRotationProposal(msg.arg1, msg.arg2 != 0);
                     }
                     break;
             }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
index 43047ed6..0a12be4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
@@ -52,6 +52,8 @@
 import com.android.systemui.util.wakelock.SettableWakeLock;
 import com.android.systemui.util.wakelock.WakeLock;
 
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
 import java.text.NumberFormat;
 
 /**
@@ -116,11 +118,9 @@
                 WakeLock wakeLock) {
         mContext = context;
         mIndicationArea = indicationArea;
-        mTextView = (KeyguardIndicationTextView) indicationArea.findViewById(
-                R.id.keyguard_indication_text);
+        mTextView = indicationArea.findViewById(R.id.keyguard_indication_text);
         mInitialTextColor = mTextView != null ? mTextView.getCurrentTextColor() : Color.WHITE;
-        mDisclosure = (KeyguardIndicationTextView) indicationArea.findViewById(
-                R.id.keyguard_indication_enterprise_disclosure);
+        mDisclosure = indicationArea.findViewById(R.id.keyguard_indication_enterprise_disclosure);
         mLockIcon = lockIcon;
         mWakeLock = new SettableWakeLock(wakeLock);
 
@@ -416,6 +416,21 @@
         updateDisclosure();
     }
 
+    public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+        pw.println("KeyguardIndicationController:");
+        pw.println("  mTransientTextColor: " + Integer.toHexString(mTransientTextColor));
+        pw.println("  mInitialTextColor: " + Integer.toHexString(mInitialTextColor));
+        pw.println("  mPowerPluggedIn: " + mPowerPluggedIn);
+        pw.println("  mPowerCharged: " + mPowerCharged);
+        pw.println("  mChargingSpeed: " + mChargingSpeed);
+        pw.println("  mChargingWattage: " + mChargingWattage);
+        pw.println("  mMessageToShowOnScreenOn: " + mMessageToShowOnScreenOn);
+        pw.println("  mDozing: " + mDozing);
+        pw.println("  mBatteryLevel: " + mBatteryLevel);
+        pw.println("  mTextView.getText(): " + (mTextView == null ? null : mTextView.getText()));
+        pw.println("  computePowerIndication(): " + computePowerIndication());
+    }
+
     protected class BaseKeyguardCallback extends KeyguardUpdateMonitorCallback {
         public static final int HIDE_DELAY_MS = 5000;
         private int mLastSuccessiveErrorMessage = -1;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationEntryManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationEntryManager.java
index 5b06874..7360486 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationEntryManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationEntryManager.java
@@ -616,8 +616,7 @@
                     && entry.row.getGuts() == mGutsManager.getExposedGuts();
             entry.row.onDensityOrFontScaleChanged();
             if (exposedGuts) {
-                mGutsManager.setExposedGuts(entry.row.getGuts());
-                mGutsManager.bindGuts(entry.row);
+                mGutsManager.onDensityOrFontScaleChanged(entry.row);
             }
         }
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationGuts.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationGuts.java
index c4024a5..52776d7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationGuts.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationGuts.java
@@ -23,6 +23,7 @@
 import android.graphics.Canvas;
 import android.graphics.drawable.Drawable;
 import android.os.Handler;
+import android.support.annotation.Nullable;
 import android.util.AttributeSet;
 import android.view.View;
 import android.view.ViewAnimationUtils;
@@ -187,6 +188,12 @@
         }
     }
 
+    public void openControls(
+            int x, int y, boolean needsFalsingProtection, @Nullable Runnable onAnimationEnd) {
+        animateOpen(x, y, onAnimationEnd);
+        setExposed(true /* exposed */, needsFalsingProtection);
+    }
+
     public void closeControls(boolean leavebehinds, boolean controls, int x, int y, boolean force) {
         if (mGutsContent != null) {
             if (mGutsContent.isLeavebehind() && leavebehinds) {
@@ -214,6 +221,27 @@
         }
     }
 
+    private void animateOpen(int x, int y, @Nullable Runnable onAnimationEnd) {
+        final double horz = Math.max(getWidth() - x, x);
+        final double vert = Math.max(getHeight() - y, y);
+        final float r = (float) Math.hypot(horz, vert);
+
+        final Animator a
+                = ViewAnimationUtils.createCircularReveal(this, x, y, 0, r);
+        a.setDuration(StackStateAnimator.ANIMATION_DURATION_STANDARD);
+        a.setInterpolator(Interpolators.LINEAR_OUT_SLOW_IN);
+        a.addListener(new AnimatorListenerAdapter() {
+            @Override
+            public void onAnimationEnd(Animator animation) {
+                super.onAnimationEnd(animation);
+                if (onAnimationEnd != null) {
+                    onAnimationEnd.run();
+                }
+            }
+        });
+        a.start();
+    }
+
     private void animateClose(int x, int y) {
         if (x == -1 || y == -1) {
             x = (getLeft() + getRight()) / 2;
@@ -279,7 +307,7 @@
         }
     }
 
-    public void setExposed(boolean exposed, boolean needsFalsingProtection) {
+    private void setExposed(boolean exposed, boolean needsFalsingProtection) {
         final boolean wasExposed = mExposed;
         mExposed = exposed;
         mNeedsFalsingProtection = needsFalsingProtection;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationGutsManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationGutsManager.java
index 441c184..9d8892d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationGutsManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationGutsManager.java
@@ -15,8 +15,6 @@
  */
 package com.android.systemui.statusbar;
 
-import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
 import android.app.INotificationManager;
 import android.app.NotificationChannel;
 import android.content.Context;
@@ -32,17 +30,14 @@
 import android.util.Log;
 import android.view.HapticFeedbackConstants;
 import android.view.View;
-import android.view.ViewAnimationUtils;
 import android.view.accessibility.AccessibilityManager;
 
 import com.android.internal.logging.MetricsLogger;
 import com.android.internal.logging.nano.MetricsProto;
 import com.android.systemui.Dependency;
 import com.android.systemui.Dumpable;
-import com.android.systemui.Interpolators;
 import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin;
 import com.android.systemui.statusbar.phone.StatusBar;
-import com.android.systemui.statusbar.stack.StackStateAnimator;
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
@@ -112,6 +107,11 @@
         mKeyToRemoveOnGutsClosed = keyToRemoveOnGutsClosed;
     }
 
+    public void onDensityOrFontScaleChanged(ExpandableNotificationRow row) {
+        setExposedGuts(row.getGuts());
+        bindGuts(row);
+    }
+
     private void saveAndCloseNotificationMenu(
             ExpandableNotificationRow row, NotificationGuts guts, View done) {
         guts.resetFalsingCheck();
@@ -270,7 +270,7 @@
     }
 
     /**
-     *  Opens guts on the given ExpandableNotificationRow |v|.
+     * Opens guts on the given ExpandableNotificationRow |v|.
      *
      * @param v ExpandableNotificationRow to open guts on
      * @param x x coordinate of origin of circular reveal
@@ -326,26 +326,15 @@
                         true /* removeControls */, -1 /* x */, -1 /* y */,
                         false /* resetMenu */);
                 guts.setVisibility(View.VISIBLE);
-                final double horz = Math.max(guts.getWidth() - x, x);
-                final double vert = Math.max(guts.getHeight() - y, y);
-                final float r = (float) Math.hypot(horz, vert);
-                final Animator a
-                        = ViewAnimationUtils.createCircularReveal(guts, x, y, 0, r);
-                a.setDuration(StackStateAnimator.ANIMATION_DURATION_STANDARD);
-                a.setInterpolator(Interpolators.LINEAR_OUT_SLOW_IN);
-                a.addListener(new AnimatorListenerAdapter() {
-                    @Override
-                    public void onAnimationEnd(Animator animation) {
-                        super.onAnimationEnd(animation);
-                        // Move the notification view back over the menu
-                        row.resetTranslation();
-                    }
-                });
-                a.start();
+
                 final boolean needsFalsingProtection =
                         (mPresenter.isPresenterLocked() &&
                                 !mAccessibilityManager.isTouchExplorationEnabled());
-                guts.setExposed(true /* exposed */, needsFalsingProtection);
+                guts.openControls(x, y, needsFalsingProtection, () -> {
+                    // Move the notification view back over the menu
+                    row.resetTranslation();
+                });
+
                 row.closeRemoteInput();
                 mListContainer.onHeightChanged(row, true /* needsAnimation */);
                 mNotificationGutsExposed = guts;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ButtonDispatcher.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ButtonDispatcher.java
index 9b123cb..7284ee8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ButtonDispatcher.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ButtonDispatcher.java
@@ -34,6 +34,7 @@
     private View.OnClickListener mClickListener;
     private View.OnTouchListener mTouchListener;
     private View.OnLongClickListener mLongClickListener;
+    private View.OnHoverListener mOnHoverListener;
     private Boolean mLongClickable;
     private Integer mAlpha;
     private Float mDarkIntensity;
@@ -56,6 +57,7 @@
         view.setOnClickListener(mClickListener);
         view.setOnTouchListener(mTouchListener);
         view.setOnLongClickListener(mLongClickListener);
+        view.setOnHoverListener(mOnHoverListener);
         if (mLongClickable != null) {
             view.setLongClickable(mLongClickable);
         }
@@ -174,6 +176,14 @@
         }
     }
 
+    public void setOnHoverListener(View.OnHoverListener hoverListener) {
+        mOnHoverListener = hoverListener;
+        final int N = mViews.size();
+        for (int i = 0; i < N; i++) {
+            mViews.get(i).setOnHoverListener(mOnHoverListener);
+        }
+    }
+
     public void setClickable(boolean clickable) {
         abortCurrentGesture();
         final int N = mViews.size();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java
index f7aa818..389be1a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java
@@ -20,6 +20,7 @@
 
 import android.content.res.Resources;
 import android.graphics.Path;
+import android.util.MathUtils;
 import android.view.animation.AccelerateInterpolator;
 import android.view.animation.PathInterpolator;
 
@@ -45,8 +46,7 @@
     private static final float BURN_IN_PREVENTION_PERIOD_Y = 521;
     private static final float BURN_IN_PREVENTION_PERIOD_X = 83;
 
-    private int mClockNotificationsMarginMin;
-    private int mClockNotificationsMarginMax;
+    private int mClockNotificationsMargin;
     private float mClockYFractionMin;
     private float mClockYFractionMax;
     private int mMaxKeyguardNotifications;
@@ -84,10 +84,8 @@
      * Refreshes the dimension values.
      */
     public void loadDimens(Resources res) {
-        mClockNotificationsMarginMin = res.getDimensionPixelSize(
-                R.dimen.keyguard_clock_notifications_margin_min);
-        mClockNotificationsMarginMax = res.getDimensionPixelSize(
-                R.dimen.keyguard_clock_notifications_margin_max);
+        mClockNotificationsMargin = res.getDimensionPixelSize(
+                R.dimen.keyguard_clock_notifications_margin);
         mClockYFractionMin = res.getFraction(R.fraction.keyguard_clock_y_fraction_min, 1, 1);
         mClockYFractionMax = res.getFraction(R.fraction.keyguard_clock_y_fraction_max, 1, 1);
         mMoreCardNotificationAmount =
@@ -117,7 +115,7 @@
 
     public float getMinStackScrollerPadding(int height, int keyguardStatusHeight) {
         return mClockYFractionMin * height + keyguardStatusHeight / 2
-                + mClockNotificationsMarginMin;
+                + mClockNotificationsMargin;
     }
 
     public void run(Result result) {
@@ -125,21 +123,15 @@
         float clockAdjustment = getClockYExpansionAdjustment();
         float topPaddingAdjMultiplier = getTopPaddingAdjMultiplier();
         result.stackScrollerPaddingAdjustment = (int) (clockAdjustment*topPaddingAdjMultiplier);
-        int clockNotificationsPadding = getClockNotificationsPadding()
+        result.clockY = y;
+        int clockNotificationsPadding = mClockNotificationsMargin
                 + result.stackScrollerPaddingAdjustment;
         int padding = y + clockNotificationsPadding;
-        result.clockY = y;
-        result.stackScrollerPadding = mKeyguardStatusHeight + padding;
-        result.clockScale = getClockScale(result.stackScrollerPadding,
-                result.clockY,
-                y + getClockNotificationsPadding() + mKeyguardStatusHeight);
+        result.clockScale = getClockScale(mKeyguardStatusHeight + padding,
+                y, y + mClockNotificationsMargin + mKeyguardStatusHeight);
         result.clockAlpha = getClockAlpha(result.clockScale);
 
-        result.stackScrollerPadding = (int) interpolate(
-                result.stackScrollerPadding,
-                mClockBottom + y + mDozingStackPadding,
-                mDarkAmount);
-
+        result.stackScrollerPadding = mClockBottom + y + mDozingStackPadding;
         result.clockX = (int) interpolate(0, burnInPreventionOffsetX(), mDarkAmount);
     }
 
@@ -154,22 +146,16 @@
         return interpolate(progress, 1, mDarkAmount);
     }
 
-    private int getClockNotificationsPadding() {
-        float t = getNotificationAmountT();
-        t = Math.min(t, 1.0f);
-        return (int) (t * mClockNotificationsMarginMin + (1 - t) * mClockNotificationsMarginMax);
-    }
-
     private float getClockYFraction() {
         float t = getNotificationAmountT();
         t = Math.min(t, 1.0f);
-        return (1 - t) * mClockYFractionMax + t * mClockYFractionMin;
+        return MathUtils.lerp(mClockYFractionMax, mClockYFractionMin, t);
     }
 
     private int getClockY() {
-        // Dark: Align the bottom edge of the clock at one third:
-        // clockBottomEdge = result - mKeyguardStatusHeight / 2 + mClockBottom
-        float clockYDark = (0.33f * mHeight + (float) mKeyguardStatusHeight / 2 - mClockBottom)
+        // Dark: Align the bottom edge of the clock at about half of the screen:
+        float clockYDark = (mClockYFractionMax * mHeight +
+                (float) mKeyguardStatusHeight / 2 - mClockBottom)
                 + burnInPreventionOffsetY();
         float clockYRegular = getClockYFraction() * mHeight;
         return (int) interpolate(clockYRegular, clockYDark, mDarkAmount);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java
index 70ec45e..368b36b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java
@@ -26,7 +26,6 @@
 import android.accessibilityservice.AccessibilityServiceInfo;
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
-import android.animation.AnimatorSet;
 import android.animation.ObjectAnimator;
 import android.annotation.Nullable;
 import android.app.ActivityManager;
@@ -111,7 +110,7 @@
     /** Allow some time inbetween the long press for back and recents. */
     private static final int LOCK_TO_APP_GESTURE_TOLERENCE = 200;
 
-    private static final int ROTATE_SUGGESTION_TIMEOUT_MS = 4000;
+    private static final int BUTTON_FADE_IN_OUT_DURATION_MS = 100;
 
     protected NavigationBarView mNavigationBarView = null;
     protected AssistManager mAssistManager;
@@ -120,6 +119,7 @@
 
     private int mNavigationIconHints = 0;
     private int mNavigationBarMode;
+    private boolean mAccessibilityFeedbackEnabled;
     private AccessibilityManager mAccessibilityManager;
     private MagnificationContentObserver mMagnificationObserver;
     private ContentResolver mContentResolver;
@@ -143,6 +143,7 @@
     public boolean mHomeBlockedThisTouch;
 
     private int mLastRotationSuggestion;
+    private boolean mHoveringRotationSuggestion;
     private RotationLockController mRotationLockController;
     private TaskStackListenerImpl mTaskStackListener;
 
@@ -335,43 +336,77 @@
     }
 
     @Override
-    public void onRotationProposal(final int rotation) {
-        // This method will only be called if rotation is valid but will include proposals for the
-        // current system rotation
-        Handler h = getView().getHandler();
+    public void onRotationProposal(final int rotation, boolean isValid) {
+        // This method will be called on rotation suggestion changes even if the proposed rotation
+        // is not valid for the top app. Use invalid rotation choices as a signal to remove the
+        // rotate button if shown.
+
+        if (!isValid) {
+            setRotateSuggestionButtonState(false);
+            return;
+        }
+
         if (rotation == mWindowManager.getDefaultDisplay().getRotation()) {
             // Use this as a signal to remove any current suggestions
-            h.removeCallbacks(mRemoveRotationProposal);
+            getView().getHandler().removeCallbacks(mRemoveRotationProposal);
             setRotateSuggestionButtonState(false);
         } else {
             mLastRotationSuggestion = rotation; // Remember rotation for click
             setRotateSuggestionButtonState(true);
-            h.removeCallbacks(mRemoveRotationProposal); // Stop any pending removal
-            h.postDelayed(mRemoveRotationProposal,
-                    ROTATE_SUGGESTION_TIMEOUT_MS); // Schedule timeout
+            rescheduleRotationTimeout(false);
         }
     }
 
+    private void rescheduleRotationTimeout(final boolean reasonHover) {
+        // May be called due to a new rotation proposal or a change in hover state
+        if (reasonHover) {
+            // Don't reschedule if a hide animator is running
+            if (mRotateHideAnimator != null && mRotateHideAnimator.isRunning()) {
+                return;
+            }
+            // Don't reschedule if not visible
+            if (mNavigationBarView.getRotateSuggestionButton().getVisibility() != View.VISIBLE) {
+                return;
+            }
+        }
+
+        Handler h = getView().getHandler();
+        h.removeCallbacks(mRemoveRotationProposal); // Stop any pending removal
+        h.postDelayed(mRemoveRotationProposal,
+                computeRotationProposalTimeout()); // Schedule timeout
+    }
+
+    private int computeRotationProposalTimeout() {
+        if (mAccessibilityFeedbackEnabled) return 20000;
+        if (mHoveringRotationSuggestion) return 16000;
+        return 6000;
+    }
+
     public void setRotateSuggestionButtonState(final boolean visible) {
         setRotateSuggestionButtonState(visible, false);
     }
 
     public void setRotateSuggestionButtonState(final boolean visible, final boolean skipAnim) {
         ButtonDispatcher rotBtn = mNavigationBarView.getRotateSuggestionButton();
-        boolean currentlyVisible = rotBtn.getVisibility() == View.VISIBLE;
+        final boolean currentlyVisible = rotBtn.getVisibility() == View.VISIBLE;
 
         // Rerun a show animation to indicate change but don't rerun a hide animation
         if (!visible && !currentlyVisible) return;
 
-        View currentView = mNavigationBarView.getRotateSuggestionButton().getCurrentView();
+        View currentView = rotBtn.getCurrentView();
         if (currentView == null) return;
 
-        KeyButtonDrawable kbd = mNavigationBarView.getRotateSuggestionButton().getImageDrawable();
+        KeyButtonDrawable kbd = rotBtn.getImageDrawable();
         if (kbd == null) return;
 
-        AnimatedVectorDrawable animIcon = (AnimatedVectorDrawable) kbd.getDrawable(0);
+        AnimatedVectorDrawable animIcon = null;
+        if (kbd.getDrawable(0) instanceof AnimatedVectorDrawable) {
+            animIcon = (AnimatedVectorDrawable) kbd.getDrawable(0);
+        }
+
         if (visible) { // Appear and change
             rotBtn.setVisibility(View.VISIBLE);
+            mNavigationBarView.notifyAccessibilitySubtreeChanged();
 
             if (skipAnim) {
                 currentView.setAlpha(1f);
@@ -384,18 +419,22 @@
 
             ObjectAnimator appearFade = ObjectAnimator.ofFloat(currentView, "alpha",
                     0f, 1f);
-            appearFade.setDuration(100);
+            appearFade.setDuration(BUTTON_FADE_IN_OUT_DURATION_MS);
             appearFade.setInterpolator(Interpolators.LINEAR);
             mRotateShowAnimator = appearFade;
             appearFade.start();
 
-            // Run the rotate icon's animation
-            animIcon.reset();
-            animIcon.start();
+            // Run the rotate icon's animation if it has one
+            if (animIcon != null) {
+                animIcon.reset();
+                animIcon.start();
+            }
+
         } else { // Hide
 
             if (skipAnim) {
                 rotBtn.setVisibility(View.INVISIBLE);
+                mNavigationBarView.notifyAccessibilitySubtreeChanged();
                 return;
             }
 
@@ -406,12 +445,13 @@
 
             ObjectAnimator fadeOut = ObjectAnimator.ofFloat(currentView, "alpha",
                     0f);
-            fadeOut.setDuration(100);
+            fadeOut.setDuration(BUTTON_FADE_IN_OUT_DURATION_MS);
             fadeOut.setInterpolator(Interpolators.LINEAR);
             fadeOut.addListener(new AnimatorListenerAdapter() {
                 @Override
                 public void onAnimationEnd(Animator animation) {
                     rotBtn.setVisibility(View.INVISIBLE);
+                    mNavigationBarView.notifyAccessibilitySubtreeChanged();
                 }
             });
 
@@ -525,6 +565,7 @@
 
         ButtonDispatcher rotateSuggestionButton = mNavigationBarView.getRotateSuggestionButton();
         rotateSuggestionButton.setOnClickListener(this::onRotateSuggestionClick);
+        rotateSuggestionButton.setOnHoverListener(this::onRotateSuggestionHover);
     }
 
     private boolean onHomeTouch(View v, MotionEvent event) {
@@ -700,6 +741,7 @@
         } catch (Settings.SettingNotFoundException e) {
         }
 
+        boolean feedbackEnabled = false;
         // AccessibilityManagerService resolves services for the current user since the local
         // AccessibilityManager is created from a Context with the INTERACT_ACROSS_USERS permission
         final List<AccessibilityServiceInfo> services =
@@ -710,8 +752,15 @@
             if ((info.flags & AccessibilityServiceInfo.FLAG_REQUEST_ACCESSIBILITY_BUTTON) != 0) {
                 requestingServices++;
             }
+
+            if (info.feedbackType != 0 && info.feedbackType !=
+                    AccessibilityServiceInfo.FEEDBACK_GENERIC) {
+                feedbackEnabled = true;
+            }
         }
 
+        mAccessibilityFeedbackEnabled = feedbackEnabled;
+
         final boolean showAccessibilityButton = requestingServices >= 1;
         final boolean targetSelection = requestingServices >= 2;
         mNavigationBarView.setAccessibilityButtonState(showAccessibilityButton, targetSelection);
@@ -721,6 +770,14 @@
         mRotationLockController.setRotationLockedAtAngle(true, mLastRotationSuggestion);
     }
 
+    private boolean onRotateSuggestionHover(View v, MotionEvent event) {
+        final int action = event.getActionMasked();
+        mHoveringRotationSuggestion = (action == MotionEvent.ACTION_HOVER_ENTER)
+                || (action == MotionEvent.ACTION_HOVER_MOVE);
+        rescheduleRotationTimeout(true);
+        return false; // Must return false so a11y hover events are dispatched correctly.
+    }
+
     // ----- Methods that StatusBar talks to (should be minimized) -----
 
     public void setLightBarController(LightBarController lightBarController) {
@@ -768,18 +825,18 @@
 
     private final Stub mRotationWatcher = new Stub() {
         @Override
-        public void onRotationChanged(int rotation) throws RemoteException {
-            // If the screen rotation changes while locked, update lock rotation to flow with
-            // new screen rotation and hide any showing suggestions.
-            if (mRotationLockController.isRotationLocked()) {
-                mRotationLockController.setRotationLockedAtAngle(true, rotation);
-                setRotateSuggestionButtonState(false, true);
-            }
-
+        public void onRotationChanged(final int rotation) throws RemoteException {
             // We need this to be scheduled as early as possible to beat the redrawing of
             // window in response to the orientation change.
             Handler h = getView().getHandler();
             Message msg = Message.obtain(h, () -> {
+                // If the screen rotation changes while locked, update lock rotation to flow with
+                // new screen rotation and hide any showing suggestions.
+                if (mRotationLockController.isRotationLocked()) {
+                    mRotationLockController.setRotationLockedAtAngle(true, rotation);
+                    setRotateSuggestionButtonState(false, true);
+                }
+
                 if (mNavigationBarView != null
                         && mNavigationBarView.needsReorient(rotation)) {
                     repositionNavigationBar();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarTransitions.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarTransitions.java
index b113675..e09d31c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarTransitions.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarTransitions.java
@@ -26,6 +26,7 @@
 import android.view.IWindowManager;
 import android.view.MotionEvent;
 import android.view.View;
+import android.view.View.OnLayoutChangeListener;
 
 import com.android.internal.statusbar.IStatusBarService;
 import com.android.systemui.Dependency;
@@ -41,6 +42,7 @@
 
     private boolean mLightsOut;
     private boolean mAutoDim;
+    private View mNavButtons;
 
     public NavigationBarTransitions(NavigationBarView view) {
         super(view, R.drawable.nav_background);
@@ -66,6 +68,18 @@
                 }, Display.DEFAULT_DISPLAY);
         } catch (RemoteException e) {
         }
+        mView.addOnLayoutChangeListener(
+                (v, left, top, right, bottom, oldLeft, oldTop, oldRight, oldBottom) -> {
+                    View currentView = mView.getCurrentView();
+                    if (currentView != null) {
+                        mNavButtons = currentView.findViewById(R.id.nav_buttons);
+                        applyLightsOut(false, true);
+                    }
+                });
+        View currentView = mView.getCurrentView();
+        if (currentView != null) {
+            mNavButtons = currentView.findViewById(R.id.nav_buttons);
+        }
     }
 
     public void init() {
@@ -105,21 +119,20 @@
         if (!force && lightsOut == mLightsOut) return;
 
         mLightsOut = lightsOut;
-
-        final View navButtons = mView.getCurrentView().findViewById(R.id.nav_buttons);
+        if (mNavButtons == null) return;
 
         // ok, everyone, stop it right there
-        navButtons.animate().cancel();
+        mNavButtons.animate().cancel();
 
         // Bump percentage by 10% if dark.
         float darkBump = mLightTransitionsController.getCurrentDarkIntensity() / 10;
         final float navButtonsAlpha = lightsOut ? 0.6f + darkBump : 1f;
 
         if (!animate) {
-            navButtons.setAlpha(navButtonsAlpha);
+            mNavButtons.setAlpha(navButtonsAlpha);
         } else {
             final int duration = lightsOut ? LIGHTS_OUT_DURATION : LIGHTS_IN_DURATION;
-            navButtons.animate()
+            mNavButtons.animate()
                 .alpha(navButtonsAlpha)
                 .setDuration(duration)
                 .start();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java
index 970d1de..b181212 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java
@@ -16,26 +16,34 @@
 
 package com.android.systemui.statusbar.phone;
 
+import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
+
+import android.annotation.Nullable;
 import android.content.Context;
+import android.content.res.Configuration;
+import android.graphics.Rect;
 import android.util.AttributeSet;
 import android.util.EventLog;
+import android.view.DisplayCutout;
 import android.view.MotionEvent;
 import android.view.View;
 import android.view.ViewGroup;
 import android.view.accessibility.AccessibilityEvent;
 
-import com.android.systemui.BatteryMeterView;
-import com.android.systemui.DejankUtils;
+import android.widget.FrameLayout;
+import android.widget.LinearLayout;
 import com.android.systemui.Dependency;
 import com.android.systemui.EventLogTags;
 import com.android.systemui.R;
 import com.android.systemui.statusbar.policy.DarkIconDispatcher;
 import com.android.systemui.statusbar.policy.DarkIconDispatcher.DarkReceiver;
+import com.android.systemui.util.leak.RotationUtils;
 
 public class PhoneStatusBarView extends PanelBar {
     private static final String TAG = "PhoneStatusBarView";
     private static final boolean DEBUG = StatusBar.DEBUG;
     private static final boolean DEBUG_GESTURES = false;
+    private static final int NO_VALUE = Integer.MIN_VALUE;
 
     StatusBar mBar;
 
@@ -53,6 +61,11 @@
         }
     };
     private DarkReceiver mBattery;
+    private int mLastOrientation;
+    @Nullable
+    private View mCutoutSpace;
+    @Nullable
+    private DisplayCutout mDisplayCutout;
 
     public PhoneStatusBarView(Context context, AttributeSet attrs) {
         super(context, attrs);
@@ -76,6 +89,7 @@
     public void onFinishInflate() {
         mBarTransitions.init();
         mBattery = findViewById(R.id.battery);
+        mCutoutSpace = findViewById(R.id.cutout_space_view);
     }
 
     @Override
@@ -83,12 +97,51 @@
         super.onAttachedToWindow();
         // Always have Battery meters in the status bar observe the dark/light modes.
         Dependency.get(DarkIconDispatcher.class).addDarkReceiver(mBattery);
+        if (updateOrientationAndCutout(getResources().getConfiguration().orientation)) {
+            postUpdateLayoutForCutout();
+        }
     }
 
     @Override
     protected void onDetachedFromWindow() {
         super.onDetachedFromWindow();
         Dependency.get(DarkIconDispatcher.class).removeDarkReceiver(mBattery);
+        mDisplayCutout = null;
+    }
+
+    @Override
+    protected void onConfigurationChanged(Configuration newConfig) {
+        super.onConfigurationChanged(newConfig);
+
+        // May trigger cutout space layout-ing
+        if (updateOrientationAndCutout(newConfig.orientation)) {
+            postUpdateLayoutForCutout();
+        }
+    }
+
+    /**
+     *
+     * @param newOrientation may pass NO_VALUE for no change
+     * @return boolean indicating if we need to update the cutout location / margins
+     */
+    private boolean updateOrientationAndCutout(int newOrientation) {
+        boolean changed = false;
+        if (newOrientation != NO_VALUE) {
+            if (mLastOrientation != newOrientation) {
+                changed = true;
+                mLastOrientation = newOrientation;
+            }
+        }
+
+        if (mDisplayCutout == null) {
+            DisplayCutout cutout = getRootWindowInsets().getDisplayCutout();
+            if (cutout != null) {
+                changed = true;
+                mDisplayCutout = cutout;
+            }
+        }
+
+        return changed;
     }
 
     @Override
@@ -214,4 +267,80 @@
                 R.dimen.status_bar_height);
         setLayoutParams(layoutParams);
     }
+
+    private void updateLayoutForCutout() {
+        updateCutoutLocation();
+        updateSafeInsets();
+    }
+
+    private void postUpdateLayoutForCutout() {
+        Runnable r = new Runnable() {
+            @Override
+            public void run() {
+                updateLayoutForCutout();
+            }
+        };
+        // Let the cutout emulation draw first
+        postDelayed(r, 0);
+    }
+
+    private void updateCutoutLocation() {
+        // Not all layouts have a cutout (e.g., Car)
+        if (mCutoutSpace == null) {
+            return;
+        }
+
+        if (mDisplayCutout == null || mDisplayCutout.isEmpty()
+                    || mLastOrientation != ORIENTATION_PORTRAIT) {
+            mCutoutSpace.setVisibility(View.GONE);
+            return;
+        }
+
+        mCutoutSpace.setVisibility(View.VISIBLE);
+        LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) mCutoutSpace.getLayoutParams();
+        lp.width = mDisplayCutout.getBoundingRect().width();
+        lp.height = mDisplayCutout.getBoundingRect().height();
+    }
+
+    private void updateSafeInsets() {
+        // Depending on our rotation, we may have to work around a cutout in the middle of the view,
+        // or letterboxing from the right or left sides.
+
+        FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) getLayoutParams();
+        if (mDisplayCutout == null || mDisplayCutout.isEmpty()) {
+            lp.leftMargin = 0;
+            lp.rightMargin = 0;
+            return;
+        }
+
+        int leftMargin = 0;
+        int rightMargin = 0;
+        switch (RotationUtils.getRotation(getContext())) {
+            /*
+             * Landscape: <-|
+             * Seascape:  |->
+             */
+            case RotationUtils.ROTATION_LANDSCAPE:
+                leftMargin = getDisplayCutoutHeight();
+                break;
+            case RotationUtils.ROTATION_SEASCAPE:
+                rightMargin = getDisplayCutoutHeight();
+                break;
+            default:
+                break;
+        }
+
+        lp.leftMargin = leftMargin;
+        lp.rightMargin = rightMargin;
+    }
+
+    //TODO: Find a better way
+    private int getDisplayCutoutHeight() {
+        if (mDisplayCutout == null || mDisplayCutout.isEmpty()) {
+            return 0;
+        }
+
+        Rect r = mDisplayCutout.getBoundingRect();
+        return r.bottom - r.top;
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
index 14329b5..3b394dd 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
@@ -866,13 +866,13 @@
 
     @Override
     public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
-        pw.println(" ScrimController:");
-        pw.print(" state:"); pw.println(mState);
-        pw.print("   frontScrim:"); pw.print(" viewAlpha="); pw.print(mScrimInFront.getViewAlpha());
+        pw.println(" ScrimController: ");
+        pw.print("  state: "); pw.println(mState);
+        pw.print("  frontScrim:"); pw.print(" viewAlpha="); pw.print(mScrimInFront.getViewAlpha());
         pw.print(" alpha="); pw.print(mCurrentInFrontAlpha);
         pw.print(" tint=0x"); pw.println(Integer.toHexString(mScrimInFront.getTint()));
 
-        pw.print("   backScrim:"); pw.print(" viewAlpha="); pw.print(mScrimBehind.getViewAlpha());
+        pw.print("  backScrim:"); pw.print(" viewAlpha="); pw.print(mScrimBehind.getViewAlpha());
         pw.print(" alpha="); pw.print(mCurrentBehindAlpha);
         pw.print(" tint=0x"); pw.println(Integer.toHexString(mScrimBehind.getTint()));
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/SettingsButton.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/SettingsButton.java
index 6220fcb..1130b6d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/SettingsButton.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/SettingsButton.java
@@ -32,6 +32,8 @@
 
 public class SettingsButton extends AlphaOptimizedImageButton {
 
+    private static final boolean TUNER_ENABLE_AVAILABLE = false;
+
     private static final long LONG_PRESS_LENGTH = 1000;
     private static final long ACCEL_LENGTH = 750;
     private static final long FULL_SPEED_LENGTH = 375;
@@ -59,7 +61,7 @@
     public boolean onTouchEvent(MotionEvent event) {
         switch (event.getActionMasked()) {
             case MotionEvent.ACTION_DOWN:
-                postDelayed(mLongPressCallback, LONG_PRESS_LENGTH);
+                if (TUNER_ENABLE_AVAILABLE) postDelayed(mLongPressCallback, LONG_PRESS_LENGTH);
                 break;
             case MotionEvent.ACTION_UP:
                 if (mUpToSpeed) {
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 5e08ec3..c30fb22 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -1704,7 +1704,7 @@
         if (mReportRejectedTouch == null) {
             return;
         }
-        mReportRejectedTouch.setVisibility(mState == StatusBarState.KEYGUARD
+        mReportRejectedTouch.setVisibility(mState == StatusBarState.KEYGUARD && !mDozing
                 && mFalsingManager.isReportingEnabled() ? View.VISIBLE : View.INVISIBLE);
     }
 
@@ -2663,6 +2663,10 @@
             mFingerprintUnlockController.dump(pw);
         }
 
+        if (mKeyguardIndicationController != null) {
+            mKeyguardIndicationController.dump(fd, pw, args);
+        }
+
         if (mScrimController != null) {
             mScrimController.dump(fd, pw, args);
         }
@@ -4506,6 +4510,7 @@
             ((DozeReceiver) mAmbientIndicationContainer).setDozing(mDozing);
         }
         updateDozingState();
+        updateReportRejectedTouchVisibility();
         Trace.endSection();
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java
index bcda60e..07610ce 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java
@@ -62,7 +62,7 @@
     }
 
     /**
-     * Version of ViewGroup that observers state from the DarkIconDispatcher.
+     * Version of ViewGroup that observes state from the DarkIconDispatcher.
      */
     public static class DarkIconManager extends IconManager {
         private final DarkIconDispatcher mDarkIconDispatcher;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusIconContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusIconContainer.java
new file mode 100644
index 0000000..1897171
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusIconContainer.java
@@ -0,0 +1,169 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+/**
+ * A container for Status bar system icons. Limits the number of system icons and handles overflow
+ * similar to NotificationIconController. Can be used to layout nested StatusIconContainers
+ *
+ * Children are expected to be of type StatusBarIconView.
+ */
+package com.android.systemui.statusbar.phone;
+
+import android.annotation.Nullable;
+import android.content.Context;
+import android.util.ArrayMap;
+import android.util.AttributeSet;
+
+import android.view.View;
+import com.android.keyguard.AlphaOptimizedLinearLayout;
+import com.android.systemui.R;
+import com.android.systemui.statusbar.StatusBarIconView;
+import com.android.systemui.statusbar.stack.ViewState;
+
+public class StatusIconContainer extends AlphaOptimizedLinearLayout {
+
+    private static final String TAG = "StatusIconContainer";
+    private static final int MAX_ICONS = 5;
+    private static final int MAX_DOTS = 3;
+
+    public StatusIconContainer(Context context, AttributeSet attrs) {
+        super(context, attrs);
+    }
+
+    @Override
+    protected void onLayout(boolean changed, int l, int t, int r, int b) {
+        float midY = getHeight() / 2.0f;
+
+        // Layout all child views so that we can move them around later
+        for (int i = 0; i < getChildCount(); i++) {
+            View child = getChildAt(i);
+            int width = child.getMeasuredWidth();
+            int height = child.getMeasuredHeight();
+            int top = (int) (midY - height / 2.0f);
+            child.layout(0, top, width, top + height);
+        }
+
+        resetViewStates();
+        calculateIconTranslations();
+        applyIconStates();
+    }
+
+    @Override
+    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+        final int count = getChildCount();
+        // Measure all children so that they report the correct width
+        for (int i = 0; i < count; i++) {
+            measureChild(getChildAt(i), widthMeasureSpec, heightMeasureSpec);
+        }
+    }
+
+    @Override
+    public void onViewAdded(View child) {
+        super.onViewAdded(child);
+        ViewState vs = new ViewState();
+        child.setTag(R.id.status_bar_view_state_tag, vs);
+    }
+
+    @Override
+    public void onViewRemoved(View child) {
+        super.onViewRemoved(child);
+        child.setTag(R.id.status_bar_view_state_tag, null);
+    }
+
+    /**
+     * Layout is happening from end -> start
+     */
+    private void calculateIconTranslations() {
+        float translationX = getWidth();
+        float contentStart = getPaddingStart();
+        int childCount = getChildCount();
+        // Underflow === don't show content until that index
+        int firstUnderflowIndex = -1;
+        android.util.Log.d(TAG, "calculateIconTransitions: start=" + translationX);
+
+        //TODO: Dots
+        for (int i = childCount - 1; i >= 0; i--) {
+            View child = getChildAt(i);
+            if (!(child instanceof StatusBarIconView)) {
+                continue;
+            }
+
+            ViewState childState = getViewStateFromChild(child);
+            if (childState == null ) {
+                continue;
+            }
+
+            // Rely on StatusBarIcon for truth about visibility
+            if (!((StatusBarIconView) child).getStatusBarIcon().visible) {
+                childState.hidden = true;
+                continue;
+            }
+
+            childState.xTranslation = translationX - child.getWidth();
+
+            if (childState.xTranslation < contentStart) {
+                if (firstUnderflowIndex == -1) {
+                    firstUnderflowIndex = i;
+                }
+            }
+
+            translationX -= child.getWidth();
+        }
+
+        if (firstUnderflowIndex != -1) {
+            for (int i = 0; i <= firstUnderflowIndex; i++) {
+                View child = getChildAt(i);
+                ViewState vs = getViewStateFromChild(child);
+                if (vs != null) {
+                    vs.hidden = true;
+                }
+            }
+        }
+    }
+
+    private void applyIconStates() {
+        for (int i = 0; i < getChildCount(); i++) {
+            View child = getChildAt(i);
+            ViewState vs = getViewStateFromChild(child);
+            if (vs != null) {
+                vs.applyToView(child);
+            }
+        }
+    }
+
+    private void resetViewStates() {
+        for (int i = 0; i < getChildCount(); i++) {
+            View child = getChildAt(i);
+            ViewState vs = getViewStateFromChild(child);
+            if (vs == null) {
+                continue;
+            }
+
+            vs.initFrom(child);
+            vs.alpha = 1.0f;
+            if (child instanceof StatusBarIconView) {
+                vs.hidden = !((StatusBarIconView)child).getStatusBarIcon().visible;
+            } else {
+                vs.hidden = false;
+            }
+        }
+    }
+
+    private static @Nullable ViewState getViewStateFromChild(View child) {
+        return (ViewState) child.getTag(R.id.status_bar_view_state_tag);
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BluetoothControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BluetoothControllerImpl.java
index 3b15c2b..fcf084b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BluetoothControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BluetoothControllerImpl.java
@@ -276,6 +276,9 @@
         mHandler.sendEmptyMessage(H.MSG_STATE_CHANGED);
     }
 
+    @Override
+    public void onActiveDeviceChanged(CachedBluetoothDevice activeDevice, int bluetoothProfile) {}
+
     private ActuallyCachedState getCachedState(CachedBluetoothDevice device) {
         ActuallyCachedState state = mCachedState.get(device);
         if (state == null) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationChildrenContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationChildrenContainer.java
index 5505099..4ca33cd 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationChildrenContainer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationChildrenContainer.java
@@ -394,7 +394,7 @@
             }
         } else if (mOverflowNumber != null) {
             removeView(mOverflowNumber);
-            if (isShown()) {
+            if (isShown() && isAttachedToWindow()) {
                 final View removedOverflowNumber = mOverflowNumber;
                 addTransientView(removedOverflowNumber, getTransientViewCount());
                 CrossFadeHelper.fadeOut(removedOverflowNumber, new Runnable() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
index 167508a..443e760 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
@@ -29,6 +29,7 @@
 import android.content.res.Configuration;
 import android.content.res.Resources;
 import android.graphics.Canvas;
+import android.graphics.Color;
 import android.graphics.Paint;
 import android.graphics.Path;
 import android.graphics.PointF;
@@ -43,6 +44,7 @@
 import android.util.AttributeSet;
 import android.util.FloatProperty;
 import android.util.Log;
+import android.util.MathUtils;
 import android.util.Pair;
 import android.util.Property;
 import android.view.ContextThemeWrapper;
@@ -123,7 +125,7 @@
     /**
      * Sentinel value for no current active pointer. Used by {@link #mActivePointerId}.
      */
-    private static final int INVALID_POINTER = -1;;
+    private static final int INVALID_POINTER = -1;
 
     private ExpandHelper mExpandHelper;
     private NotificationSwipeHelper mSwipeHelper;
@@ -158,7 +160,12 @@
     private int mCollapsedSize;
     private int mPaddingBetweenElements;
     private int mIncreasedPaddingBetweenElements;
+    private int mRegularTopPadding;
+    private int mDarkTopPadding;
+    // Current padding, will be either mRegularTopPadding or mDarkTopPadding
     private int mTopPadding;
+    // Distance between AOD separator and shelf
+    private int mDarkSeparatorPadding;
     private int mBottomMargin;
     private int mBottomInset = 0;
 
@@ -356,17 +363,17 @@
     private boolean mGroupExpandedForMeasure;
     private boolean mScrollable;
     private View mForcedScroll;
-    private float mBackgroundFadeAmount = 1.0f;
-    private static final Property<NotificationStackScrollLayout, Float> BACKGROUND_FADE =
-            new FloatProperty<NotificationStackScrollLayout>("backgroundFade") {
+    private float mDarkAmount = 1.0f;
+    private static final Property<NotificationStackScrollLayout, Float> DARK_AMOUNT =
+            new FloatProperty<NotificationStackScrollLayout>("darkAmount") {
                 @Override
                 public void setValue(NotificationStackScrollLayout object, float value) {
-                    object.setBackgroundFadeAmount(value);
+                    object.setDarkAmount(value);
                 }
 
                 @Override
                 public Float get(NotificationStackScrollLayout object) {
-                    return object.getBackgroundFadeAmount();
+                    return object.getDarkAmount();
                 }
             };
     private boolean mUsingLightTheme;
@@ -389,6 +396,9 @@
     private Runnable mAnimateScroll = this::animateScroll;
     private int mCornerRadius;
     private int mSidePaddings;
+    private final int mSeparatorWidth;
+    private final int mSeparatorThickness;
+    private final Rect mTmpRect = new Rect();
 
     public NotificationStackScrollLayout(Context context) {
         this(context, null);
@@ -423,9 +433,12 @@
                 res.getBoolean(R.bool.config_drawNotificationBackground);
         mFadeNotificationsOnDismiss =
                 res.getBoolean(R.bool.config_fadeNotificationsOnDismiss);
+        mSeparatorWidth = res.getDimensionPixelSize(R.dimen.widget_separator_width);
+        mSeparatorThickness = res.getDimensionPixelSize(R.dimen.widget_separator_thickness);
+        mDarkSeparatorPadding = res.getDimensionPixelSize(R.dimen.widget_bottom_separator_padding);
 
         updateWillNotDraw();
-        mBackgroundPaint.setAntiAlias(true);;
+        mBackgroundPaint.setAntiAlias(true);
         if (DEBUG) {
             mDebugPaint = new Paint();
             mDebugPaint.setColor(0xffff0000);
@@ -472,10 +485,9 @@
     }
 
     protected void onDraw(Canvas canvas) {
-        if (mShouldDrawNotificationBackground && !mAmbientState.isDark()
-                && mCurrentBounds.top < mCurrentBounds.bottom) {
-            canvas.drawRoundRect(mSidePaddings, mCurrentBounds.top, getWidth() - mSidePaddings,
-                    mCurrentBounds.bottom, mCornerRadius, mCornerRadius, mBackgroundPaint);
+        if (mShouldDrawNotificationBackground
+                && (mCurrentBounds.top < mCurrentBounds.bottom || mAmbientState.isDark())) {
+            drawBackground(canvas);
         }
 
         if (DEBUG) {
@@ -488,17 +500,55 @@
         }
     }
 
+    private void drawBackground(Canvas canvas) {
+        final int lockScreenLeft = mSidePaddings;
+        final int lockScreenRight = getWidth() - mSidePaddings;
+        final int lockScreenTop = mCurrentBounds.top;
+        final int lockScreenBottom = mCurrentBounds.bottom;
+        final int darkLeft = getWidth() / 2 - mSeparatorWidth / 2;
+        final int darkRight = darkLeft + mSeparatorWidth;
+        final int darkTop = (int) (mRegularTopPadding + mSeparatorThickness / 2f);
+        final int darkBottom = darkTop + mSeparatorThickness;
+
+        if (mAmbientState.isDark()) {
+            // Only draw divider on AOD if we actually have notifications
+            if (mFirstVisibleBackgroundChild != null) {
+                canvas.drawRect(darkLeft, darkTop, darkRight, darkBottom, mBackgroundPaint);
+            }
+            setClipBounds(null);
+        } else {
+            float animProgress = Interpolators.FAST_OUT_SLOW_IN
+                    .getInterpolation(mDarkAmount);
+            float sidePaddingsProgress = Interpolators.FAST_OUT_SLOW_IN
+                    .getInterpolation(mDarkAmount * 2);
+            mTmpRect.set((int) MathUtils.lerp(darkLeft, lockScreenLeft, sidePaddingsProgress),
+                    (int) MathUtils.lerp(darkTop, lockScreenTop, animProgress),
+                    (int) MathUtils.lerp(darkRight, lockScreenRight, sidePaddingsProgress),
+                    (int) MathUtils.lerp(darkBottom, lockScreenBottom, animProgress));
+            canvas.drawRoundRect(mTmpRect.left, mTmpRect.top, mTmpRect.right, mTmpRect.bottom,
+                    mCornerRadius, mCornerRadius, mBackgroundPaint);
+            setClipBounds(animProgress == 1 ? null : mTmpRect);
+        }
+    }
+
     private void updateBackgroundDimming() {
         // No need to update the background color if it's not being drawn.
         if (!mShouldDrawNotificationBackground) {
             return;
         }
 
-        float alpha = BACKGROUND_ALPHA_DIMMED + (1 - BACKGROUND_ALPHA_DIMMED) * (1.0f - mDimAmount);
-        alpha *= mBackgroundFadeAmount;
-        // We need to manually blend in the background color
-        int scrimColor = mScrimController.getBackgroundColor();
-        int color = ColorUtils.blendARGB(scrimColor, mBgColor, alpha);
+        final int color;
+        if (mAmbientState.isDark()) {
+            color = Color.WHITE;
+        } else {
+            float alpha =
+                    BACKGROUND_ALPHA_DIMMED + (1 - BACKGROUND_ALPHA_DIMMED) * (1.0f - mDimAmount);
+            alpha *= mDarkAmount;
+            // We need to manually blend in the background color
+            int scrimColor = mScrimController.getBackgroundColor();
+            color = ColorUtils.blendARGB(scrimColor, mBgColor, alpha);
+        }
+
         if (mCachedBackgroundColor != color) {
             mCachedBackgroundColor = color;
             mBackgroundPaint.setColor(color);
@@ -634,6 +684,7 @@
     }
 
     private void updateAlgorithmHeightAndPadding() {
+        mTopPadding = mAmbientState.isDark() ? mDarkTopPadding : mRegularTopPadding;
         mAmbientState.setLayoutHeight(getLayoutHeight());
         updateAlgorithmLayoutMinHeight();
         mAmbientState.setTopPadding(mTopPadding);
@@ -756,8 +807,9 @@
     }
 
     private void setTopPadding(int topPadding, boolean animate) {
-        if (mTopPadding != topPadding) {
-            mTopPadding = topPadding;
+        if (mRegularTopPadding != topPadding) {
+            mRegularTopPadding = topPadding;
+            mDarkTopPadding = topPadding + mDarkSeparatorPadding;
             updateAlgorithmHeightAndPadding();
             updateContentHeight();
             if (animate && mAnimationsEnabled && mIsExpanded) {
@@ -2262,7 +2314,7 @@
         }
 
         mScrimController.setExcludedBackgroundArea(
-                mFadingOut || mParentNotFullyVisible || mAmbientState.isDark() || mIsClipped ? null
+                mFadingOut || mParentNotFullyVisible || mDarkAmount != 1 || mIsClipped ? null
                         : mCurrentBounds);
         invalidate();
     }
@@ -3804,9 +3856,9 @@
             mDarkNeedsAnimation = true;
             mDarkAnimationOriginIndex = findDarkAnimationOriginIndex(touchWakeUpScreenLocation);
             mNeedsAnimation =  true;
-            setBackgroundFadeAmount(0.0f);
+            setDarkAmount(0.0f);
         } else if (!dark) {
-            setBackgroundFadeAmount(1.0f);
+            setDarkAmount(1.0f);
         }
         requestChildrenUpdate();
         if (dark) {
@@ -3826,21 +3878,21 @@
      * {@link #mAmbientState}'s dark mode is toggled.
      */
     private void updateWillNotDraw() {
-        boolean willDraw = !mAmbientState.isDark() && mShouldDrawNotificationBackground || DEBUG;
+        boolean willDraw = mShouldDrawNotificationBackground || DEBUG;
         setWillNotDraw(!willDraw);
     }
 
-    private void setBackgroundFadeAmount(float fadeAmount) {
-        mBackgroundFadeAmount = fadeAmount;
+    private void setDarkAmount(float darkAmount) {
+        mDarkAmount = darkAmount;
         updateBackgroundDimming();
     }
 
-    public float getBackgroundFadeAmount() {
-        return mBackgroundFadeAmount;
+    public float getDarkAmount() {
+        return mDarkAmount;
     }
 
     private void startBackgroundFadeIn() {
-        ObjectAnimator fadeAnimator = ObjectAnimator.ofFloat(this, BACKGROUND_FADE, 0f, 1f);
+        ObjectAnimator fadeAnimator = ObjectAnimator.ofFloat(this, DARK_AMOUNT, 0f, 1f);
         fadeAnimator.setDuration(StackStateAnimator.ANIMATION_DURATION_WAKEUP);
         fadeAnimator.setInterpolator(Interpolators.ALPHA_IN);
         fadeAnimator.start();
diff --git a/packages/SystemUI/src/com/android/systemui/tuner/TunerServiceImpl.java b/packages/SystemUI/src/com/android/systemui/tuner/TunerServiceImpl.java
index 8e584bc..5a4478f 100644
--- a/packages/SystemUI/src/com/android/systemui/tuner/TunerServiceImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/tuner/TunerServiceImpl.java
@@ -58,7 +58,7 @@
 
     private static final String TUNER_VERSION = "sysui_tuner_version";
 
-    private static final int CURRENT_TUNER_VERSION = 1;
+    private static final int CURRENT_TUNER_VERSION = 2;
 
     private final Observer mObserver = new Observer();
     // Map of Uris we listen on to their settings keys.
@@ -116,6 +116,9 @@
                         TextUtils.join(",", iconBlacklist), mCurrentUser);
             }
         }
+        if (oldVersion < 2) {
+            setTunerEnabled(mContext, false);
+        }
         setValue(TUNER_VERSION, newVersion);
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/volume/MediaRouterWrapper.java b/packages/SystemUI/src/com/android/systemui/volume/MediaRouterWrapper.java
new file mode 100644
index 0000000..3423452
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/volume/MediaRouterWrapper.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.volume;
+
+import android.support.v7.media.MediaRouteSelector;
+import android.support.v7.media.MediaRouter;
+
+import java.util.List;
+
+/**
+ * Wrapper for final class MediaRouter, for testing.
+ */
+public class MediaRouterWrapper {
+
+    private final MediaRouter mRouter;
+
+    public MediaRouterWrapper(MediaRouter router)
+    {
+        mRouter = router;
+    }
+
+    public void addCallback(MediaRouteSelector selector, MediaRouter.Callback callback, int flags) {
+        mRouter.addCallback(selector, callback, flags);
+    }
+
+    public void removeCallback(MediaRouter.Callback callback) {
+        mRouter.removeCallback(callback);
+    }
+
+    public void unselect(int reason) {
+        mRouter.unselect(reason);
+    }
+
+    public List<MediaRouter.RouteInfo> getRoutes() {
+        return mRouter.getRoutes();
+    }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/volume/OutputChooserDialog.java b/packages/SystemUI/src/com/android/systemui/volume/OutputChooserDialog.java
index f8843a9..e0af9ba 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/OutputChooserDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/OutputChooserDialog.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2015 The Android Open Source Project
+ * Copyright (C) 2017 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.
@@ -40,6 +40,7 @@
 import android.support.v7.media.MediaControlIntent;
 import android.support.v7.media.MediaRouteSelector;
 import android.support.v7.media.MediaRouter;
+import android.telecom.TelecomManager;
 import android.util.Log;
 import android.util.Pair;
 
@@ -54,7 +55,6 @@
 import java.io.InputStream;
 import java.util.ArrayList;
 import java.util.Collection;
-import java.util.Collections;
 import java.util.Comparator;
 import java.util.List;
 
@@ -65,15 +65,17 @@
     private static final int MAX_DEVICES = 10;
 
     private static final long UPDATE_DELAY_MS = 300L;
-    static final int MSG_UPDATE_ITEMS = 1;
+    private static final int MSG_UPDATE_ITEMS = 1;
 
     private final Context mContext;
-    private final BluetoothController mController;
-    private final WifiManager mWifiManager;
+    private final BluetoothController mBluetoothController;
+    private WifiManager mWifiManager;
     private OutputChooserLayout mView;
-    private final MediaRouter mRouter;
+    private final MediaRouterWrapper mRouter;
     private final MediaRouterCallback mRouterCallback;
     private long mLastUpdateTime;
+    private boolean mIsInCall;
+    protected boolean isAttached;
 
     private final MediaRouteSelector mRouteSelector;
     private Drawable mDefaultIcon;
@@ -81,12 +83,14 @@
     private Drawable mSpeakerIcon;
     private Drawable mSpeakerGroupIcon;
 
-    public OutputChooserDialog(Context context) {
+    public OutputChooserDialog(Context context, MediaRouterWrapper router) {
         super(context);
         mContext = context;
-        mController = Dependency.get(BluetoothController.class);
+        mBluetoothController = Dependency.get(BluetoothController.class);
         mWifiManager = (WifiManager) context.getSystemService(Context.WIFI_SERVICE);
-        mRouter = MediaRouter.getInstance(context);
+        TelecomManager tm = (TelecomManager) context.getSystemService(Context.TELECOM_SERVICE);
+        mIsInCall = tm.isInCall();
+        mRouter = router;
         mRouterCallback = new MediaRouterCallback();
         mRouteSelector = new MediaRouteSelector.Builder()
                 .addControlCategory(MediaControlIntent.CATEGORY_REMOTE_PLAYBACK)
@@ -96,27 +100,38 @@
         context.registerReceiver(mReceiver, filter);
     }
 
+    protected void setIsInCall(boolean inCall) {
+        mIsInCall = inCall;
+    }
+
     @Override
     protected void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
         setContentView(R.layout.output_chooser);
         setCanceledOnTouchOutside(true);
         setOnDismissListener(this::onDismiss);
-        setTitle(R.string.output_title);
 
         mView = findViewById(R.id.output_chooser);
         mView.setCallback(this);
 
+        if (mIsInCall) {
+            mView.setTitle(R.string.output_calls_title);
+        } else {
+            mView.setTitle(R.string.output_title);
+        }
+
         mDefaultIcon = mContext.getDrawable(R.drawable.ic_cast);
         mTvIcon = mContext.getDrawable(R.drawable.ic_tv);
         mSpeakerIcon = mContext.getDrawable(R.drawable.ic_speaker);
         mSpeakerGroupIcon = mContext.getDrawable(R.drawable.ic_speaker_group);
 
         final boolean wifiOff = !mWifiManager.isWifiEnabled();
-        final boolean btOff = !mController.isBluetoothEnabled();
-        if (wifiOff || btOff) {
+        final boolean btOff = !mBluetoothController.isBluetoothEnabled();
+        if (wifiOff && btOff) {
             mView.setEmptyState(getDisabledServicesMessage(wifiOff, btOff));
         }
+        // time out after 5 seconds
+        mView.postDelayed(() -> updateItems(true), 5000);
     }
 
     protected void cleanUp() {}
@@ -131,15 +146,19 @@
     public void onAttachedToWindow() {
         super.onAttachedToWindow();
 
-        mRouter.addCallback(mRouteSelector, mRouterCallback,
-                MediaRouter.CALLBACK_FLAG_PERFORM_ACTIVE_SCAN);
-        mController.addCallback(mCallback);
+        if (!mIsInCall) {
+            mRouter.addCallback(mRouteSelector, mRouterCallback,
+                    MediaRouter.CALLBACK_FLAG_PERFORM_ACTIVE_SCAN);
+        }
+        mBluetoothController.addCallback(mCallback);
+        isAttached = true;
     }
 
     @Override
     public void onDetachedFromWindow() {
+        isAttached = false;
         mRouter.removeCallback(mRouterCallback);
-        mController.removeCallback(mCallback);
+        mBluetoothController.removeCallback(mCallback);
         super.onDetachedFromWindow();
     }
 
@@ -154,9 +173,8 @@
         if (item == null || item.tag == null) return;
         if (item.deviceType == OutputChooserLayout.Item.DEVICE_TYPE_BT) {
             final CachedBluetoothDevice device = (CachedBluetoothDevice) item.tag;
-            if (device != null && device.getMaxConnectionState()
-                    == BluetoothProfile.STATE_DISCONNECTED) {
-                mController.connect(device);
+            if (device.getMaxConnectionState() == BluetoothProfile.STATE_DISCONNECTED) {
+                mBluetoothController.connect(device);
             }
         } else if (item.deviceType == OutputChooserLayout.Item.DEVICE_TYPE_MEDIA_ROUTER) {
             final MediaRouter.RouteInfo route = (MediaRouter.RouteInfo) item.tag;
@@ -171,18 +189,16 @@
         if (item == null || item.tag == null) return;
         if (item.deviceType == OutputChooserLayout.Item.DEVICE_TYPE_BT) {
             final CachedBluetoothDevice device = (CachedBluetoothDevice) item.tag;
-            if (device != null) {
-                mController.disconnect(device);
-            }
+            mBluetoothController.disconnect(device);
         } else if (item.deviceType == OutputChooserLayout.Item.DEVICE_TYPE_MEDIA_ROUTER) {
             mRouter.unselect(UNSELECT_REASON_DISCONNECTED);
         }
     }
 
-    private void updateItems() {
+    private void updateItems(boolean timeout) {
         if (SystemClock.uptimeMillis() - mLastUpdateTime < UPDATE_DELAY_MS) {
             mHandler.removeMessages(MSG_UPDATE_ITEMS);
-            mHandler.sendMessageAtTime(mHandler.obtainMessage(MSG_UPDATE_ITEMS),
+            mHandler.sendMessageAtTime(mHandler.obtainMessage(MSG_UPDATE_ITEMS, timeout),
                     mLastUpdateTime + UPDATE_DELAY_MS);
             return;
         }
@@ -194,14 +210,16 @@
         addBluetoothDevices(items);
 
         // Add remote displays
-        addRemoteDisplayRoutes(items);
+        if (!mIsInCall) {
+            addRemoteDisplayRoutes(items);
+        }
 
-        Collections.sort(items, ItemComparator.sInstance);
+        items.sort(ItemComparator.sInstance);
 
-        if (items.size() == 0) {
+        if (items.size() == 0 && timeout) {
             String emptyMessage = mContext.getString(R.string.output_none_found);
             final boolean wifiOff = !mWifiManager.isWifiEnabled();
-            final boolean btOff = !mController.isBluetoothEnabled();
+            final boolean btOff = !mBluetoothController.isBluetoothEnabled();
             if (wifiOff || btOff) {
                 emptyMessage = getDisabledServicesMessage(wifiOff, btOff);
             }
@@ -219,12 +237,12 @@
     }
 
     private void addBluetoothDevices(List<OutputChooserLayout.Item> items) {
-        final Collection<CachedBluetoothDevice> devices = mController.getDevices();
+        final Collection<CachedBluetoothDevice> devices = mBluetoothController.getDevices();
         if (devices != null) {
             int connectedDevices = 0;
             int count = 0;
             for (CachedBluetoothDevice device : devices) {
-                if (mController.getBondState(device) == BluetoothDevice.BOND_NONE) continue;
+                if (mBluetoothController.getBondState(device) == BluetoothDevice.BOND_NONE) continue;
                 final int majorClass = device.getBtClass().getMajorDeviceClass();
                 if (majorClass != BluetoothClass.Device.Major.AUDIO_VIDEO
                         && majorClass != BluetoothClass.Device.Major.UNCATEGORIZED) {
@@ -328,22 +346,22 @@
     private final class MediaRouterCallback extends MediaRouter.Callback {
         @Override
         public void onRouteAdded(MediaRouter router, MediaRouter.RouteInfo info) {
-            updateItems();
+            updateItems(false);
         }
 
         @Override
         public void onRouteRemoved(MediaRouter router, MediaRouter.RouteInfo info) {
-            updateItems();
+            updateItems(false);
         }
 
         @Override
         public void onRouteChanged(MediaRouter router, MediaRouter.RouteInfo info) {
-            updateItems();
+            updateItems(false);
         }
 
         @Override
         public void onRouteSelected(MediaRouter router, MediaRouter.RouteInfo route) {
-            dismiss();
+            updateItems(false);
         }
     }
 
@@ -361,12 +379,12 @@
     private final BluetoothController.Callback mCallback = new BluetoothController.Callback() {
         @Override
         public void onBluetoothStateChange(boolean enabled) {
-            updateItems();
+            updateItems(false);
         }
 
         @Override
         public void onBluetoothDevicesChanged() {
-            updateItems();
+            updateItems(false);
         }
     };
 
@@ -393,7 +411,7 @@
         public void handleMessage(Message message) {
             switch (message.what) {
                 case MSG_UPDATE_ITEMS:
-                    updateItems();
+                    updateItems((Boolean) message.obj);
                     break;
             }
         }
diff --git a/packages/SystemUI/src/com/android/systemui/volume/OutputChooserLayout.java b/packages/SystemUI/src/com/android/systemui/volume/OutputChooserLayout.java
index 22ced60..d4c6f89 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/OutputChooserLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/OutputChooserLayout.java
@@ -29,8 +29,8 @@
 import android.view.View;
 import android.view.ViewGroup;
 import android.widget.BaseAdapter;
-import android.widget.FrameLayout;
 import android.widget.ImageView;
+import android.widget.LinearLayout;
 import android.widget.TextView;
 
 import com.android.systemui.FontSizeUtils;
@@ -40,11 +40,10 @@
 /**
  * Limited height list of devices.
  */
-public class OutputChooserLayout extends FrameLayout {
+public class OutputChooserLayout extends LinearLayout {
     private static final String TAG = "OutputChooserLayout";
     private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
 
-    private final int mQsDetailIconOverlaySize;
     private final Context mContext;
     private final H mHandler = new H();
     private final Adapter mAdapter = new Adapter();
@@ -55,6 +54,7 @@
     private AutoSizingList mItemList;
     private View mEmpty;
     private TextView mEmptyText;
+    private TextView mTitle;
 
     private Item[] mItems;
 
@@ -62,8 +62,6 @@
         super(context, attrs);
         mContext = context;
         mTag = TAG;
-        mQsDetailIconOverlaySize = (int) getResources().getDimension(
-                R.dimen.qs_detail_icon_overlay_size);
     }
 
     @Override
@@ -74,7 +72,8 @@
         mItemList.setAdapter(mAdapter);
         mEmpty = findViewById(android.R.id.empty);
         mEmpty.setVisibility(GONE);
-        mEmptyText = mEmpty.findViewById(android.R.id.title);
+        mEmptyText = mEmpty.findViewById(R.id.empty_text);
+        mTitle = findViewById(R.id.title);
     }
 
     @Override
@@ -84,17 +83,21 @@
         int count = mItemList.getChildCount();
         for (int i = 0; i < count; i++) {
             View item = mItemList.getChildAt(i);
-            FontSizeUtils.updateFontSize(item, android.R.id.title,
+            FontSizeUtils.updateFontSize(item, R.id.empty_text,
                     R.dimen.qs_detail_item_primary_text_size);
             FontSizeUtils.updateFontSize(item, android.R.id.summary,
                     R.dimen.qs_detail_item_secondary_text_size);
+            FontSizeUtils.updateFontSize(item, android.R.id.title,
+                    R.dimen.qs_detail_header_text_size);
         }
     }
 
+    public void setTitle(int title) {
+            mTitle.setText(title);
+    }
+
     public void setEmptyState(String text) {
-        mEmpty.post(() -> {
-            mEmptyText.setText(text);
-        });
+        mEmptyText.setText(text);
     }
 
     @Override
@@ -176,11 +179,6 @@
             } else {
                 iv.setImageResource(item.iconResId);
             }
-            iv.getOverlay().clear();
-            if (item.overlay != null) {
-                item.overlay.setBounds(0, 0, mQsDetailIconOverlaySize, mQsDetailIconOverlaySize);
-                iv.getOverlay().add(item.overlay);
-            }
             final TextView title = view.findViewById(android.R.id.title);
             title.setText(item.line1);
             final TextView summary =  view.findViewById(android.R.id.summary);
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
index 9f7c5a7..e76bf57 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
@@ -47,6 +47,7 @@
 import android.os.SystemClock;
 import android.provider.Settings;
 import android.provider.Settings.Global;
+import android.support.v7.media.MediaRouter;
 import android.util.Log;
 import android.util.Slog;
 import android.util.SparseBooleanArray;
@@ -858,7 +859,8 @@
             if (mOutputChooserDialog != null) {
                 return;
             }
-            mOutputChooserDialog = new OutputChooserDialog(mContext) {
+            mOutputChooserDialog = new OutputChooserDialog(mContext,
+                    new MediaRouterWrapper(MediaRouter.getInstance(mContext))) {
                 @Override
                 protected void cleanUp() {
                     synchronized (mOutputChooserLock) {
diff --git a/packages/SystemUI/tests/AndroidManifest.xml b/packages/SystemUI/tests/AndroidManifest.xml
index e74736a..859dc2f 100644
--- a/packages/SystemUI/tests/AndroidManifest.xml
+++ b/packages/SystemUI/tests/AndroidManifest.xml
@@ -46,6 +46,7 @@
     <uses-permission android:name="android.permission.STATUS_BAR" />
     <uses-permission android:name="android.permission.MANAGE_ACTIVITY_STACKS" />
     <uses-permission android:name="android.permission.REAL_GET_TASKS" />
+    <uses-permission android:name="android.permission.INTERNAL_SYSTEM_WINDOW" />
 
     <application>
         <uses-library android:name="android.test.runner" />
diff --git a/packages/SystemUI/tests/src/com/android/systemui/SysuiTestCase.java b/packages/SystemUI/tests/src/com/android/systemui/SysuiTestCase.java
index fbcbd20..4c7c1d1 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/SysuiTestCase.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/SysuiTestCase.java
@@ -83,7 +83,7 @@
         return null;
     }
 
-    public Context getContext() {
+    public SysuiTestableContext getContext() {
         return mContext;
     }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/CommandQueueTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/CommandQueueTest.java
index 9603207..a02ef98 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/CommandQueueTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/CommandQueueTest.java
@@ -119,9 +119,9 @@
 
     @Test
     public void testShowRecentApps() {
-        mCommandQueue.showRecentApps(true, false);
+        mCommandQueue.showRecentApps(true);
         waitForIdleSync();
-        verify(mCallbacks).showRecentApps(eq(true), eq(false));
+        verify(mCallbacks).showRecentApps(eq(true));
     }
 
     @Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationGutsManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationGutsManagerTest.java
new file mode 100644
index 0000000..8f38c2c
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationGutsManagerTest.java
@@ -0,0 +1,196 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.systemui.statusbar;
+
+import static junit.framework.Assert.assertNotNull;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.fail;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.times;
+
+import android.app.Notification;
+import android.app.NotificationChannel;
+import android.app.NotificationManager;
+import android.content.Context;
+import android.os.Binder;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.UserHandle;
+import android.service.notification.StatusBarNotification;
+import android.support.test.filters.SmallTest;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+import android.view.LayoutInflater;
+import android.view.View;
+
+import com.android.systemui.R;
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin;
+import com.android.systemui.statusbar.NotificationData;
+import com.android.systemui.statusbar.NotificationGuts;
+import com.android.systemui.statusbar.NotificationTestHelper;
+import com.android.systemui.statusbar.notification.NotificationInflater;
+import com.android.systemui.statusbar.stack.NotificationStackScrollLayout;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoRule;
+import org.mockito.junit.MockitoJUnit;
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+@TestableLooper.RunWithLooper
+public class NotificationGutsManagerTest extends SysuiTestCase {
+    private static final String TEST_CHANNEL_ID = "NotificationManagerServiceTestChannelId";
+
+    private final String mPackageName = mContext.getPackageName();
+    private final int mUid = Binder.getCallingUid();
+
+    private NotificationChannel mTestNotificationChannel = new NotificationChannel(
+            TEST_CHANNEL_ID, TEST_CHANNEL_ID, NotificationManager.IMPORTANCE_DEFAULT);
+    private TestableLooper mTestableLooper;
+    private Handler mHandler;
+    private NotificationTestHelper mHelper;
+    private NotificationGutsManager mGutsManager;
+
+    @Rule public MockitoRule mockito = MockitoJUnit.rule();
+    @Mock private NotificationLockscreenUserManager mLockscreenUserManager;
+    @Mock private NotificationPresenter mPresenter;
+    @Mock private NotificationEntryManager mEntryManager;
+    @Mock private NotificationStackScrollLayout mStackScroller;
+    @Mock private NotificationInfo.CheckSaveListener mCheckSaveListener;
+    @Mock private NotificationGutsManager.OnSettingsClickListener mOnSettingsClickListener;
+
+    @Before
+    public void setUp() {
+        mTestableLooper = TestableLooper.get(this);
+        mHandler = new Handler(mTestableLooper.getLooper());
+
+        mHelper = new NotificationTestHelper(mContext);
+
+        mGutsManager = new NotificationGutsManager(mContext);
+        mGutsManager.setUpWithPresenter(mPresenter, mEntryManager, mStackScroller,
+                mCheckSaveListener, mOnSettingsClickListener);
+    }
+
+    ////////////////////////////////////////////////////////////////////////////////////////////////
+    // Test methods:
+
+    @Test
+    public void testOpenAndCloseGuts() {
+        NotificationGuts guts = spy(new NotificationGuts(mContext));
+        when(guts.post(any())).thenAnswer(invocation -> {
+            mHandler.post(((Runnable) invocation.getArguments()[0]));
+            return null;
+        });
+
+        // Test doesn't support animation since the guts view is not attached.
+        doNothing().when(guts).openControls(anyInt(), anyInt(), anyBoolean(), any(Runnable.class));
+
+        ExpandableNotificationRow realRow = createTestNotificationRow();
+        NotificationMenuRowPlugin.MenuItem menuItem = createTestMenuItem(realRow);
+
+        ExpandableNotificationRow row = spy(realRow);
+        when(row.getWindowToken()).thenReturn(new Binder());
+        when(row.getGuts()).thenReturn(guts);
+
+        mGutsManager.openGuts(row, 0, 0, menuItem);
+        assertEquals(View.INVISIBLE, guts.getVisibility());
+        mTestableLooper.processAllMessages();
+        verify(guts).openControls(anyInt(), anyInt(), anyBoolean(), any(Runnable.class));
+
+        assertEquals(View.VISIBLE, guts.getVisibility());
+        mGutsManager.closeAndSaveGuts(false, false, false, 0, 0, false);
+
+        verify(guts).closeControls(anyBoolean(), anyBoolean(), anyInt(), anyInt(), anyBoolean());
+        verify(row, times(1)).setGutsView(any());
+    }
+
+    @Test
+    public void testChangeDensityOrFontScale() {
+        NotificationGuts guts = spy(new NotificationGuts(mContext));
+        when(guts.post(any())).thenAnswer(invocation -> {
+            mHandler.post(((Runnable) invocation.getArguments()[0]));
+            return null;
+        });
+
+        // Test doesn't support animation since the guts view is not attached.
+        doNothing().when(guts).openControls(anyInt(), anyInt(), anyBoolean(), any(Runnable.class));
+
+        ExpandableNotificationRow realRow = createTestNotificationRow();
+        NotificationMenuRowPlugin.MenuItem menuItem = createTestMenuItem(realRow);
+
+        ExpandableNotificationRow row = spy(realRow);
+        when(row.getWindowToken()).thenReturn(new Binder());
+        when(row.getGuts()).thenReturn(guts);
+        doNothing().when(row).inflateGuts();
+
+        mGutsManager.openGuts(row, 0, 0, menuItem);
+        mTestableLooper.processAllMessages();
+        verify(guts).openControls(anyInt(), anyInt(), anyBoolean(), any(Runnable.class));
+
+        row.onDensityOrFontScaleChanged();
+        mGutsManager.onDensityOrFontScaleChanged(row);
+        mTestableLooper.processAllMessages();
+
+        mGutsManager.closeAndSaveGuts(false, false, false, 0, 0, false);
+
+        verify(guts).closeControls(anyBoolean(), anyBoolean(), anyInt(), anyInt(), anyBoolean());
+        verify(row, times(2)).setGutsView(any());
+    }
+
+    ////////////////////////////////////////////////////////////////////////////////////////////////
+    // Utility methods:
+
+    private ExpandableNotificationRow createTestNotificationRow() {
+        Notification.Builder nb = new Notification.Builder(mContext,
+                mTestNotificationChannel.getId())
+                                        .setContentTitle("foo")
+                                        .setColorized(true)
+                                        .setFlag(Notification.FLAG_CAN_COLORIZE, true)
+                                        .setSmallIcon(android.R.drawable.sym_def_app_icon);
+
+        try {
+            ExpandableNotificationRow row = mHelper.createRow(nb.build());
+            row.getEntry().channel = mTestNotificationChannel;
+            return row;
+        } catch (Exception e) {
+            fail();
+            return null;
+        }
+    }
+
+    private NotificationMenuRowPlugin.MenuItem createTestMenuItem(ExpandableNotificationRow row) {
+        NotificationMenuRowPlugin menuRow = new NotificationMenuRow(mContext);
+        menuRow.createMenu(row, row.getStatusBarNotification());
+
+        NotificationMenuRowPlugin.MenuItem menuItem = menuRow.getLongpressMenuItem(mContext);
+        assertNotNull(menuItem);
+        return menuItem;
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragmentTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragmentTest.java
index 66524cc..2edcd01 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragmentTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragmentTest.java
@@ -74,6 +74,8 @@
 
         assertEquals(View.VISIBLE, mFragment.getView().findViewById(R.id.system_icon_area)
                 .getVisibility());
+        assertEquals(View.VISIBLE, mFragment.getView().findViewById(R.id.clock)
+                .getVisibility());
     }
 
     @Test
@@ -87,11 +89,15 @@
 
         assertEquals(View.INVISIBLE, mFragment.getView().findViewById(R.id.system_icon_area)
                 .getVisibility());
+        assertEquals(View.INVISIBLE, mFragment.getView().findViewById(R.id.clock)
+                .getVisibility());
 
         fragment.disable(0, 0, false);
 
         assertEquals(View.VISIBLE, mFragment.getView().findViewById(R.id.system_icon_area)
                 .getVisibility());
+        assertEquals(View.VISIBLE, mFragment.getView().findViewById(R.id.clock)
+                .getVisibility());
     }
 
     @Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/volume/OutputChooserDialogTest.java b/packages/SystemUI/tests/src/com/android/systemui/volume/OutputChooserDialogTest.java
new file mode 100644
index 0000000..537d606
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/volume/OutputChooserDialogTest.java
@@ -0,0 +1,158 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.volume;
+
+import static junit.framework.Assert.assertTrue;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.bluetooth.BluetoothProfile;
+import android.net.wifi.WifiManager;
+import android.support.test.annotation.UiThreadTest;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+import android.support.v7.media.MediaRouter;
+import android.telecom.TelecomManager;
+import android.widget.TextView;
+
+import com.android.settingslib.bluetooth.CachedBluetoothDevice;
+import com.android.systemui.R;
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.statusbar.policy.BluetoothController;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class OutputChooserDialogTest extends SysuiTestCase {
+
+    OutputChooserDialog mDialog;
+
+    @Mock
+    private BluetoothController mController;
+    @Mock
+    private WifiManager mWifiManager;
+    @Mock
+    private TelecomManager mTelecomManager;
+
+    @Mock
+    private MediaRouterWrapper mRouter;
+
+
+    @Before
+    @UiThreadTest
+    public void setup() throws Exception {
+        MockitoAnnotations.initMocks(this);
+
+        mController = mDependency.injectMockDependency(BluetoothController.class);
+        when(mWifiManager.isWifiEnabled()).thenReturn(true);
+
+        getContext().addMockSystemService(WifiManager.class, mWifiManager);
+        getContext().addMockSystemService(TelecomManager.class, mTelecomManager);
+
+        mDialog = new OutputChooserDialog(getContext(), mRouter);
+    }
+
+    @After
+    @UiThreadTest
+    public void tearDown() throws Exception {
+        mDialog.dismiss();
+    }
+
+    private void showDialog() {
+        mDialog.show();
+    }
+
+    @Test
+    @UiThreadTest
+    public void testClickMediaRouterItemConnectsMedia() {
+        showDialog();
+
+        OutputChooserLayout.Item item = new OutputChooserLayout.Item();
+        item.deviceType = OutputChooserLayout.Item.DEVICE_TYPE_MEDIA_ROUTER;
+        MediaRouter.RouteInfo info = mock(MediaRouter.RouteInfo.class);
+        when(info.isEnabled()).thenReturn(true);
+        item.tag = info;
+
+        mDialog.onDetailItemClick(item);
+        verify(info, times(1)).select();
+        verify(mController, never()).connect(any());
+    }
+
+    @Test
+    @UiThreadTest
+    public void testClickBtItemConnectsBt() {
+        showDialog();
+
+        OutputChooserLayout.Item item = new OutputChooserLayout.Item();
+        item.deviceType = OutputChooserLayout.Item.DEVICE_TYPE_BT;
+        CachedBluetoothDevice btDevice = mock(CachedBluetoothDevice.class);
+        when(btDevice.getMaxConnectionState()).thenReturn(BluetoothProfile.STATE_DISCONNECTED);
+        item.tag = btDevice;
+
+        mDialog.onDetailItemClick(item);
+        verify(mController, times(1)).connect(any());
+    }
+
+    @Test
+    @UiThreadTest
+    public void testTitleNotInCall() {
+        showDialog();
+
+        assertTrue(((TextView) mDialog.findViewById(R.id.title))
+                .getText().toString().contains("Media"));
+    }
+
+    @Test
+    @UiThreadTest
+    public void testTitleInCall() {
+        mDialog.setIsInCall(true);
+        showDialog();
+
+        assertTrue(((TextView) mDialog.findViewById(R.id.title))
+                .getText().toString().contains("Phone"));
+    }
+
+    @Test
+    @UiThreadTest
+    public void testNoMediaScanIfInCall() {
+        mDialog.setIsInCall(true);
+        mDialog.onAttachedToWindow();
+
+        verify(mRouter, never()).addCallback(any(), any(), anyInt());
+    }
+
+    @Test
+    @UiThreadTest
+    public void testMediaScanIfNotInCall() {
+        mDialog.setIsInCall(false);
+        mDialog.onAttachedToWindow();
+
+        verify(mRouter, times(1)).addCallback(any(), any(), anyInt());
+    }
+}
diff --git a/proto/src/metrics_constants.proto b/proto/src/metrics_constants.proto
index f99ac5c..9b67f8f 100644
--- a/proto/src/metrics_constants.proto
+++ b/proto/src/metrics_constants.proto
@@ -5144,6 +5144,11 @@
     // OS: P
     APPLICATIONS_DIRECTORY_ACCESS_DETAIL = 1284;
 
+    // OPEN: Settings > Battery > Smart Battery > Restricted apps
+    // CATEGORY: SETTINGS
+    // OS: P
+    FUELGAUGE_RESTRICTED_APP_DETAILS = 1285;
+
     // ---- End P Constants, all P constants go above this line ----
     // Add new aosp constants above this line.
     // END OF AOSP CONSTANTS
diff --git a/services/accessibility/java/com/android/server/accessibility/GlobalActionPerformer.java b/services/accessibility/java/com/android/server/accessibility/GlobalActionPerformer.java
index 3b8d4bc..672518c 100644
--- a/services/accessibility/java/com/android/server/accessibility/GlobalActionPerformer.java
+++ b/services/accessibility/java/com/android/server/accessibility/GlobalActionPerformer.java
@@ -21,6 +21,8 @@
 import android.content.Context;
 import android.hardware.input.InputManager;
 import android.os.Binder;
+import android.os.Handler;
+import android.os.Looper;
 import android.os.PowerManager;
 import android.os.RemoteException;
 import android.os.ServiceManager;
@@ -30,20 +32,34 @@
 import android.view.KeyCharacterMap;
 import android.view.KeyEvent;
 
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.ScreenshotHelper;
 import com.android.server.LocalServices;
 import com.android.server.statusbar.StatusBarManagerInternal;
 import com.android.server.wm.WindowManagerInternal;
 
+import java.util.function.Supplier;
+
 /**
  * Handle the back-end of AccessibilityService#performGlobalAction
  */
 public class GlobalActionPerformer {
     private final WindowManagerInternal mWindowManagerService;
     private final Context mContext;
+    private Supplier<ScreenshotHelper> mScreenshotHelperSupplier;
 
     public GlobalActionPerformer(Context context, WindowManagerInternal windowManagerInternal) {
         mContext = context;
         mWindowManagerService = windowManagerInternal;
+        mScreenshotHelperSupplier = null;
+    }
+
+    // Used to mock ScreenshotHelper
+    @VisibleForTesting
+    public GlobalActionPerformer(Context context, WindowManagerInternal windowManagerInternal,
+            Supplier<ScreenshotHelper> screenshotHelperSupplier) {
+        this(context, windowManagerInternal);
+        mScreenshotHelperSupplier = screenshotHelperSupplier;
     }
 
     public boolean performGlobalAction(int action) {
@@ -79,6 +95,9 @@
                 case AccessibilityService.GLOBAL_ACTION_LOCK_SCREEN: {
                     return lockScreen();
                 }
+                case AccessibilityService.GLOBAL_ACTION_TAKE_SCREENSHOT: {
+                    return takeScreenshot();
+                }
             }
             return false;
         } finally {
@@ -167,4 +186,12 @@
         mWindowManagerService.lockNow();
         return true;
     }
+
+    private boolean takeScreenshot() {
+        ScreenshotHelper screenshotHelper = (mScreenshotHelperSupplier != null)
+                ? mScreenshotHelperSupplier.get() : new ScreenshotHelper(mContext);
+        screenshotHelper.takeScreenshot(android.view.WindowManager.TAKE_SCREENSHOT_FULLSCREEN,
+                true, true, new Handler(Looper.getMainLooper()));
+        return true;
+    }
 }
diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerService.java b/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
index 6d845f9..978ed253 100644
--- a/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
+++ b/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
@@ -52,7 +52,6 @@
 import android.os.UserManager;
 import android.os.UserManagerInternal;
 import android.provider.Settings;
-import android.service.autofill.AutofillFieldClassificationService.Scores;
 import android.service.autofill.FillEventHistory;
 import android.service.autofill.UserData;
 import android.util.LocalLog;
@@ -646,37 +645,35 @@
         }
 
         @Override
-        public void getDefaultFieldClassificationAlgorithm(RemoteCallback callback)
-                throws RemoteException {
+        public String getDefaultFieldClassificationAlgorithm() throws RemoteException {
             final int userId = UserHandle.getCallingUserId();
 
             synchronized (mLock) {
                 final AutofillManagerServiceImpl service = peekServiceForUserLocked(userId);
                 if (service != null) {
-                    service.getDefaultFieldClassificationAlgorithm(getCallingUid(), callback);
+                    return service.getDefaultFieldClassificationAlgorithm(getCallingUid());
                 } else {
                     if (sVerbose) {
                         Slog.v(TAG, "getDefaultFcAlgorithm(): no service for " + userId);
                     }
-                    callback.sendResult(null);
-                }
+                    return null;
+               }
             }
         }
 
         @Override
-        public void getAvailableFieldClassificationAlgorithms(RemoteCallback callback)
-                throws RemoteException {
+        public String[] getAvailableFieldClassificationAlgorithms() throws RemoteException {
             final int userId = UserHandle.getCallingUserId();
 
             synchronized (mLock) {
                 final AutofillManagerServiceImpl service = peekServiceForUserLocked(userId);
                 if (service != null) {
-                    service.getAvailableFieldClassificationAlgorithms(getCallingUid(), callback);
+                    return service.getAvailableFieldClassificationAlgorithms(getCallingUid());
                 } else {
                     if (sVerbose) {
                         Slog.v(TAG, "getAvailableFcAlgorithms(): no service for " + userId);
                     }
-                    callback.sendResult(null);
+                    return null;
                 }
             }
         }
diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
index a5bd59a9..6bcfc4b 100644
--- a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
+++ b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
@@ -1153,22 +1153,22 @@
         return mFieldClassificationStrategy;
     }
 
-    void getAvailableFieldClassificationAlgorithms(int callingUid, RemoteCallback callback) {
+    String[] getAvailableFieldClassificationAlgorithms(int callingUid) {
         synchronized (mLock) {
             if (!isCalledByServiceLocked("getFCAlgorithms()", callingUid)) {
-                return;
+                return null;
             }
         }
-        mFieldClassificationStrategy.getAvailableAlgorithms(callback);
+        return mFieldClassificationStrategy.getAvailableAlgorithms();
     }
 
-    void getDefaultFieldClassificationAlgorithm(int callingUid, RemoteCallback callback) {
+    String getDefaultFieldClassificationAlgorithm(int callingUid) {
         synchronized (mLock) {
             if (!isCalledByServiceLocked("getDefaultFCAlgorithm()", callingUid)) {
-                return;
+                return null;
             }
         }
-        mFieldClassificationStrategy.getDefaultAlgorithm(callback);
+        return mFieldClassificationStrategy.getDefaultAlgorithm();
     }
 
     @Override
diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceShellCommand.java b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceShellCommand.java
index 4456087..4d69ef9 100644
--- a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceShellCommand.java
+++ b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceShellCommand.java
@@ -191,10 +191,7 @@
             if (scores == null) {
                 pw.println("no score");
             } else {
-                pw.print("algorithm: ");
-                pw.print(scores.getAlgorithm());
-                pw.print(" score: ");
-                pw.println(scores.getScores()[0][0]);
+                pw.println(scores.scores[0][0]);
             }
             latch.countDown();
         }));
diff --git a/services/autofill/java/com/android/server/autofill/FieldClassificationStrategy.java b/services/autofill/java/com/android/server/autofill/FieldClassificationStrategy.java
index 7228f1d..da52201 100644
--- a/services/autofill/java/com/android/server/autofill/FieldClassificationStrategy.java
+++ b/services/autofill/java/com/android/server/autofill/FieldClassificationStrategy.java
@@ -15,12 +15,12 @@
  */
 package com.android.server.autofill;
 
-import static android.view.autofill.AutofillManager.EXTRA_AVAILABLE_ALGORITHMS;
-import static android.view.autofill.AutofillManager.EXTRA_DEFAULT_ALGORITHM;
 import static android.view.autofill.AutofillManager.FC_SERVICE_TIMEOUT;
 
 import static com.android.server.autofill.Helper.sDebug;
 import static com.android.server.autofill.Helper.sVerbose;
+import static android.service.autofill.AutofillFieldClassificationService.SERVICE_META_DATA_KEY_AVAILABLE_ALGORITHMS;
+import static android.service.autofill.AutofillFieldClassificationService.SERVICE_META_DATA_KEY_DEFAULT_ALGORITHM;
 
 import android.Manifest;
 import android.annotation.MainThread;
@@ -33,6 +33,7 @@
 import android.content.pm.PackageManager;
 import android.content.pm.ResolveInfo;
 import android.content.pm.ServiceInfo;
+import android.content.res.Resources;
 import android.os.Binder;
 import android.os.Bundle;
 import android.os.IBinder;
@@ -49,6 +50,7 @@
 
 import java.io.PrintWriter;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.List;
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.TimeUnit;
@@ -80,7 +82,8 @@
         mUserId = userId;
     }
 
-    private ComponentName getServiceComponentName() {
+    @Nullable
+    private ServiceInfo getServiceInfo() {
         final String packageName =
                 mContext.getPackageManager().getServicesSystemSharedLibraryPackageName();
         if (packageName == null) {
@@ -96,9 +99,15 @@
             Slog.w(TAG, "No valid components found.");
             return null;
         }
-        final ServiceInfo serviceInfo = resolveInfo.serviceInfo;
-        final ComponentName name = new ComponentName(serviceInfo.packageName, serviceInfo.name);
+        return resolveInfo.serviceInfo;
+    }
 
+    @Nullable
+    private ComponentName getServiceComponentName() {
+        final ServiceInfo serviceInfo = getServiceInfo();
+        if (serviceInfo == null) return null;
+
+        final ComponentName name = new ComponentName(serviceInfo.packageName, serviceInfo.name);
         if (!Manifest.permission.BIND_AUTOFILL_FIELD_CLASSIFICATION_SERVICE
                 .equals(serviceInfo.permission)) {
             Slog.w(TAG, name.flattenToShortString() + " does not require permission "
@@ -204,12 +213,40 @@
         }
     }
 
-    void getAvailableAlgorithms(RemoteCallback callback) {
-        connectAndRun((service) -> service.getAvailableAlgorithms(callback));
+    /**
+     * Gets the name of all available algorithms.
+     */
+    @Nullable
+    String[] getAvailableAlgorithms() {
+        return getMetadataValue(SERVICE_META_DATA_KEY_AVAILABLE_ALGORITHMS,
+                (res, id) -> res.getStringArray(id));
     }
 
-    void getDefaultAlgorithm(RemoteCallback callback) {
-        connectAndRun((service) -> service.getDefaultAlgorithm(callback));
+    /**
+     * Gets the default algorithm that's used when an algorithm is not specified or is invalid.
+     */
+    @Nullable
+    String getDefaultAlgorithm() {
+        return getMetadataValue(SERVICE_META_DATA_KEY_DEFAULT_ALGORITHM, (res, id) -> res.getString(id));
+    }
+
+    @Nullable
+    private <T> T getMetadataValue(String field, MetadataParser<T> parser) {
+        final ServiceInfo serviceInfo = getServiceInfo();
+        if (serviceInfo == null) return null;
+
+        final PackageManager pm = mContext.getPackageManager();
+
+        final Resources res;
+        try {
+            res = pm.getResourcesForApplication(serviceInfo.applicationInfo);
+        } catch (PackageManager.NameNotFoundException e) {
+            Log.e(TAG, "Error getting application resources for " + serviceInfo, e);
+            return null;
+        }
+
+        final int resourceId = serviceInfo.metaData.getInt(field);
+        return parser.get(res, resourceId);
     }
 
     //TODO(b/70291841): rename this method (and all others in the chain) to something like
@@ -237,43 +274,16 @@
         }
         pw.println(impl.flattenToShortString());
 
-        final CountDownLatch latch = new CountDownLatch(2);
-
-        // Lock used to make sure lines don't overlap
-        final Object lock = latch;
-
-        connectAndRun((service) -> service.getAvailableAlgorithms(new RemoteCallback((bundle) -> {
-            synchronized (lock) {
-                pw.print(prefix); pw.print("Available algorithms: ");
-                pw.println(bundle.getStringArrayList(EXTRA_AVAILABLE_ALGORITHMS));
-            }
-            latch.countDown();
-        })));
-
-        connectAndRun((service) -> service.getDefaultAlgorithm(new RemoteCallback((bundle) -> {
-            synchronized (lock) {
-                pw.print(prefix); pw.print("Default algorithm: ");
-                pw.println(bundle.getString(EXTRA_DEFAULT_ALGORITHM));
-            }
-            latch.countDown();
-        })));
-
-        try {
-            if (!latch.await(FC_SERVICE_TIMEOUT, TimeUnit.MILLISECONDS)) {
-                synchronized (lock) {
-                    pw.print(prefix); pw.print("timeout ("); pw.print(FC_SERVICE_TIMEOUT);
-                    pw.println("ms) waiting for service");
-                }
-            }
-        } catch (InterruptedException e) {
-            synchronized (lock) {
-                pw.print(prefix); pw.println("interrupted while waiting for service");
-            }
-            Thread.currentThread().interrupt();
-        }
+        pw.print(prefix); pw.print("Available algorithms: ");
+        pw.println(Arrays.toString(getAvailableAlgorithms()));
+        pw.print(prefix); pw.print("Default algorithm: "); pw.println(getDefaultAlgorithm());
     }
 
-    private interface Command {
+    private static interface Command {
         void run(IAutofillFieldClassificationService service) throws RemoteException;
     }
+
+    private static interface MetadataParser<T> {
+        T get(Resources res, int resId);
+    }
 }
diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java
index a0e23a1..63f8384 100644
--- a/services/autofill/java/com/android/server/autofill/Session.java
+++ b/services/autofill/java/com/android/server/autofill/Session.java
@@ -1172,8 +1172,6 @@
                 Slog.w(TAG, "No field classification score on " + result);
                 return;
             }
-            final float[][] scoresMatrix = scores.getScores();
-
             int i = 0, j = 0;
             try {
                 for (i = 0; i < viewsSize; i++) {
@@ -1182,8 +1180,7 @@
                     ArrayList<Match> matches = null;
                     for (j = 0; j < userValues.length; j++) {
                         String remoteId = remoteIds[j];
-                        final String actualAlgorithm = scores.getAlgorithm();
-                        final float score = scoresMatrix[i][j];
+                        final float score = scores.scores[i][j];
                         if (score > 0) {
                             if (sVerbose) {
                                 Slog.v(TAG, "adding score " + score + " at index " + j + " and id "
@@ -1192,7 +1189,7 @@
                             if (matches == null) {
                                 matches = new ArrayList<>(userValues.length);
                             }
-                            matches.add(new Match(remoteId, score, actualAlgorithm));
+                            matches.add(new Match(remoteId, score));
                         }
                         else if (sVerbose) {
                             Slog.v(TAG, "skipping score 0 at index " + j + " and id " + fieldId);
@@ -1205,7 +1202,7 @@
                 }
             } catch (ArrayIndexOutOfBoundsException e) {
                 Slog.wtf(TAG, "Error accessing FC score at " + i + " x " + j + ": "
-                        + Arrays.toString(scoresMatrix), e);
+                        + Arrays.toString(scores.scores), e);
                 return;
             }
 
diff --git a/services/backup/java/com/android/server/backup/RefactoredBackupManagerService.java b/services/backup/java/com/android/server/backup/RefactoredBackupManagerService.java
index 5188910..465bb09 100644
--- a/services/backup/java/com/android/server/backup/RefactoredBackupManagerService.java
+++ b/services/backup/java/com/android/server/backup/RefactoredBackupManagerService.java
@@ -3464,16 +3464,21 @@
         mContext.enforceCallingOrSelfPermission(
                 android.Manifest.permission.BACKUP, "isAppEligibleForBackup");
 
-        String callerLogString = "BMS.isAppEligibleForBackup";
-        TransportClient transportClient =
-                mTransportManager.getCurrentTransportClient(callerLogString);
-        boolean eligible =
-                AppBackupUtils.appIsRunningAndEligibleForBackupWithTransport(
-                        transportClient, packageName, mPackageManager);
-        if (transportClient != null) {
-            mTransportManager.disposeOfTransportClient(transportClient, callerLogString);
+        long oldToken = Binder.clearCallingIdentity();
+        try {
+            String callerLogString = "BMS.isAppEligibleForBackup";
+            TransportClient transportClient =
+                    mTransportManager.getCurrentTransportClient(callerLogString);
+            boolean eligible =
+                    AppBackupUtils.appIsRunningAndEligibleForBackupWithTransport(
+                            transportClient, packageName, mPackageManager);
+            if (transportClient != null) {
+                mTransportManager.disposeOfTransportClient(transportClient, callerLogString);
+            }
+            return eligible;
+        } finally {
+            Binder.restoreCallingIdentity(oldToken);
         }
-        return eligible;
     }
 
     @Override
@@ -3481,21 +3486,26 @@
         mContext.enforceCallingOrSelfPermission(
                 android.Manifest.permission.BACKUP, "filterAppsEligibleForBackup");
 
-        String callerLogString = "BMS.filterAppsEligibleForBackup";
-        TransportClient transportClient =
-                mTransportManager.getCurrentTransportClient(callerLogString);
-        List<String> eligibleApps = new LinkedList<>();
-        for (String packageName : packages) {
-            if (AppBackupUtils
-                    .appIsRunningAndEligibleForBackupWithTransport(
-                            transportClient, packageName, mPackageManager)) {
-                eligibleApps.add(packageName);
+        long oldToken = Binder.clearCallingIdentity();
+        try {
+            String callerLogString = "BMS.filterAppsEligibleForBackup";
+            TransportClient transportClient =
+                    mTransportManager.getCurrentTransportClient(callerLogString);
+            List<String> eligibleApps = new LinkedList<>();
+            for (String packageName : packages) {
+                if (AppBackupUtils
+                        .appIsRunningAndEligibleForBackupWithTransport(
+                                transportClient, packageName, mPackageManager)) {
+                    eligibleApps.add(packageName);
+                }
             }
+            if (transportClient != null) {
+                mTransportManager.disposeOfTransportClient(transportClient, callerLogString);
+            }
+            return eligibleApps.toArray(new String[eligibleApps.size()]);
+        } finally {
+            Binder.restoreCallingIdentity(oldToken);
         }
-        if (transportClient != null) {
-            mTransportManager.disposeOfTransportClient(transportClient, callerLogString);
-        }
-        return eligibleApps.toArray(new String[eligibleApps.size()]);
     }
 
     @Override
@@ -3514,6 +3524,9 @@
                     } else if ("agents".startsWith(arg)) {
                         dumpAgents(pw);
                         return;
+                    } else if ("transportclients".equals(arg.toLowerCase())) {
+                        mTransportManager.dump(pw);
+                        return;
                     }
                 }
             }
@@ -3576,6 +3589,8 @@
                 }
             }
 
+            mTransportManager.dump(pw);
+
             pw.println("Pending init: " + mPendingInits.size());
             for (String s : mPendingInits) {
                 pw.println("    " + s);
diff --git a/services/backup/java/com/android/server/backup/TransportManager.java b/services/backup/java/com/android/server/backup/TransportManager.java
index 6d9231d..7e179e5 100644
--- a/services/backup/java/com/android/server/backup/TransportManager.java
+++ b/services/backup/java/com/android/server/backup/TransportManager.java
@@ -43,6 +43,7 @@
 import com.android.server.backup.transport.TransportNotAvailableException;
 import com.android.server.backup.transport.TransportNotRegisteredException;
 
+import java.io.PrintWriter;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
@@ -633,6 +634,10 @@
                 !Thread.holdsLock(mTransportLock), "Can't call transport with transport lock held");
     }
 
+    public void dump(PrintWriter pw) {
+        mTransportClientManager.dump(pw);
+    }
+
     private static Predicate<ComponentName> fromPackageFilter(String packageName) {
         return transportComponent -> packageName.equals(transportComponent.getPackageName());
     }
diff --git a/services/backup/java/com/android/server/backup/internal/PerformBackupTask.java b/services/backup/java/com/android/server/backup/internal/PerformBackupTask.java
index cc3af8c..289dd14 100644
--- a/services/backup/java/com/android/server/backup/internal/PerformBackupTask.java
+++ b/services/backup/java/com/android/server/backup/internal/PerformBackupTask.java
@@ -114,14 +114,14 @@
     private RefactoredBackupManagerService backupManagerService;
     private final Object mCancelLock = new Object();
 
-    ArrayList<BackupRequest> mQueue;
-    ArrayList<BackupRequest> mOriginalQueue;
-    File mStateDir;
-    @Nullable DataChangedJournal mJournal;
-    BackupState mCurrentState;
-    List<String> mPendingFullBackups;
-    IBackupObserver mObserver;
-    IBackupManagerMonitor mMonitor;
+    private ArrayList<BackupRequest> mQueue;
+    private ArrayList<BackupRequest> mOriginalQueue;
+    private File mStateDir;
+    @Nullable private DataChangedJournal mJournal;
+    private BackupState mCurrentState;
+    private List<String> mPendingFullBackups;
+    private IBackupObserver mObserver;
+    private IBackupManagerMonitor mMonitor;
 
     private final TransportClient mTransportClient;
     private final OnTaskFinishedListener mListener;
@@ -130,18 +130,18 @@
     private volatile int mEphemeralOpToken;
 
     // carried information about the current in-flight operation
-    IBackupAgent mAgentBinder;
-    PackageInfo mCurrentPackage;
-    File mSavedStateName;
-    File mBackupDataName;
-    File mNewStateName;
-    ParcelFileDescriptor mSavedState;
-    ParcelFileDescriptor mBackupData;
-    ParcelFileDescriptor mNewState;
-    int mStatus;
-    boolean mFinished;
-    final boolean mUserInitiated;
-    final boolean mNonIncremental;
+    private IBackupAgent mAgentBinder;
+    private PackageInfo mCurrentPackage;
+    private File mSavedStateName;
+    private File mBackupDataName;
+    private File mNewStateName;
+    private ParcelFileDescriptor mSavedState;
+    private ParcelFileDescriptor mBackupData;
+    private ParcelFileDescriptor mNewState;
+    private int mStatus;
+    private boolean mFinished;
+    private final boolean mUserInitiated;
+    private final boolean mNonIncremental;
 
     private volatile boolean mCancelAll;
 
@@ -241,7 +241,7 @@
 
     // We're starting a backup pass.  Initialize the transport and send
     // the PM metadata blob if we haven't already.
-    void beginBackup() {
+    private void beginBackup() {
         if (DEBUG_BACKUP_TRACE) {
             backupManagerService.clearBackupTrace();
             StringBuilder b = new StringBuilder(256);
@@ -369,7 +369,7 @@
 
     // Transport has been initialized and the PM metadata submitted successfully
     // if that was warranted.  Now we process the single next thing in the queue.
-    void invokeNextAgent() {
+    private void invokeNextAgent() {
         mStatus = BackupTransport.TRANSPORT_OK;
         backupManagerService.addBackupTrace("invoke q=" + mQueue.size());
 
@@ -511,7 +511,7 @@
         }
     }
 
-    void finalizeBackup() {
+    private void finalizeBackup() {
         backupManagerService.addBackupTrace("finishing");
 
         // Mark packages that we didn't backup (because backup was cancelled, etc.) as needing
@@ -617,14 +617,14 @@
     }
 
     // Remove the PM metadata state. This will generate an init on the next pass.
-    void clearMetadata() {
+    private void clearMetadata() {
         final File pmState = new File(mStateDir, PACKAGE_MANAGER_SENTINEL);
         if (pmState.exists()) pmState.delete();
     }
 
     // Invoke an agent's doBackup() and start a timeout message spinning on the main
     // handler in case it doesn't get back to us.
-    int invokeAgentForBackup(String packageName, IBackupAgent agent) {
+    private int invokeAgentForBackup(String packageName, IBackupAgent agent) {
         if (DEBUG) {
             Slog.d(TAG, "invokeAgentForBackup on " + packageName);
         }
@@ -711,7 +711,7 @@
         return BackupTransport.TRANSPORT_OK;
     }
 
-    public void failAgent(IBackupAgent agent, String message) {
+    private void failAgent(IBackupAgent agent, String message) {
         try {
             agent.fail(message);
         } catch (Exception e) {
@@ -1059,7 +1059,7 @@
         }
     }
 
-    void revertAndEndBackup() {
+    private void revertAndEndBackup() {
         if (MORE_DEBUG) {
             Slog.i(TAG, "Reverting backup queue - restaging everything");
         }
@@ -1085,14 +1085,14 @@
 
     }
 
-    void errorCleanup() {
+    private void errorCleanup() {
         mBackupDataName.delete();
         mNewStateName.delete();
         clearAgentState();
     }
 
     // Cleanup common to both success and failure cases
-    void clearAgentState() {
+    private void clearAgentState() {
         try {
             if (mSavedState != null) mSavedState.close();
         } catch (IOException e) {
@@ -1123,7 +1123,7 @@
         }
     }
 
-    void executeNextState(BackupState nextState) {
+    private void executeNextState(BackupState nextState) {
         if (MORE_DEBUG) {
             Slog.i(TAG, " => executing next step on "
                     + this + " nextState=" + nextState);
diff --git a/services/backup/java/com/android/server/backup/transport/TransportClient.java b/services/backup/java/com/android/server/backup/transport/TransportClient.java
index 399f338..7b2e3df 100644
--- a/services/backup/java/com/android/server/backup/transport/TransportClient.java
+++ b/services/backup/java/com/android/server/backup/transport/TransportClient.java
@@ -16,6 +16,8 @@
 
 package com.android.server.backup.transport;
 
+import static com.android.server.backup.transport.TransportUtils.formatMessage;
+
 import android.annotation.IntDef;
 import android.annotation.Nullable;
 import android.annotation.WorkerThread;
@@ -28,6 +30,7 @@
 import android.os.IBinder;
 import android.os.Looper;
 import android.os.UserHandle;
+import android.text.format.DateFormat;
 import android.util.ArrayMap;
 import android.util.EventLog;
 import android.util.Log;
@@ -41,6 +44,9 @@
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
+import java.util.Collections;
+import java.util.LinkedList;
+import java.util.List;
 import java.util.Map;
 import java.util.concurrent.CompletableFuture;
 import java.util.concurrent.ExecutionException;
@@ -65,6 +71,7 @@
  */
 public class TransportClient {
     private static final String TAG = "TransportClient";
+    private static final int LOG_BUFFER_SIZE = 5;
 
     private final Context mContext;
     private final Intent mBindIntent;
@@ -73,6 +80,10 @@
     private final Handler mListenerHandler;
     private final String mPrefixForLog;
     private final Object mStateLock = new Object();
+    private final Object mLogBufferLock = new Object();
+
+    @GuardedBy("mLogBufferLock")
+    private final List<String> mLogBuffer = new LinkedList<>();
 
     @GuardedBy("mStateLock")
     private final Map<TransportConnectionListener, String> mListeners = new ArrayMap<>();
@@ -112,7 +123,7 @@
 
         // For logging
         String classNameForLog = mTransportComponent.getShortClassName().replaceFirst(".*\\.", "");
-        mPrefixForLog = classNameForLog + "#" + mIdentifier + ": ";
+        mPrefixForLog = classNameForLog + "#" + mIdentifier + ":";
     }
 
     public ComponentName getTransportComponent() {
@@ -229,7 +240,7 @@
 
             switch (mState) {
                 case State.UNUSABLE:
-                    log(Log.DEBUG, caller, "Async connect: UNUSABLE client");
+                    log(Log.WARN, caller, "Async connect: UNUSABLE client");
                     notifyListener(listener, null, caller);
                     break;
                 case State.IDLE:
@@ -324,14 +335,14 @@
 
         IBackupTransport transport = mTransport;
         if (transport != null) {
-            log(Log.DEBUG, caller, "Sync connect: reusing transport");
+            log(Log.INFO, caller, "Sync connect: reusing transport");
             return transport;
         }
 
         // If it's already UNUSABLE we return straight away, no need to go to main-thread
         synchronized (mStateLock) {
             if (mState == State.UNUSABLE) {
-                log(Log.DEBUG, caller, "Sync connect: UNUSABLE client");
+                log(Log.WARN, caller, "Sync connect: UNUSABLE client");
                 return null;
             }
         }
@@ -403,13 +414,16 @@
     }
 
     private void notifyListener(
-            TransportConnectionListener listener, IBackupTransport transport, String caller) {
-        log(Log.VERBOSE, caller, "Notifying listener of transport = " + transport);
+            TransportConnectionListener listener,
+            @Nullable IBackupTransport transport,
+            String caller) {
+        String transportString = (transport != null) ? "IBackupTransport" : "null";
+        log(Log.INFO, "Notifying [" + caller + "] transport = " + transportString);
         mListenerHandler.post(() -> listener.onTransportConnectionResult(transport, this));
     }
 
     @GuardedBy("mStateLock")
-    private void notifyListenersAndClearLocked(IBackupTransport transport) {
+    private void notifyListenersAndClearLocked(@Nullable IBackupTransport transport) {
         for (Map.Entry<TransportConnectionListener, String> entry : mListeners.entrySet()) {
             TransportConnectionListener listener = entry.getKey();
             String caller = entry.getValue();
@@ -509,13 +523,30 @@
     }
 
     private void log(int priority, String message) {
-        TransportUtils.log(priority, TAG, message);
+        TransportUtils.log(priority, TAG, formatMessage(mPrefixForLog, null, message));
+        saveLogEntry(formatMessage(null, null, message));
     }
 
-    private void log(int priority, String caller, String msg) {
-        TransportUtils.log(priority, TAG, mPrefixForLog, caller, msg);
-        // TODO(brufino): Log in internal list for dump
-        // CharSequence time = DateFormat.format("yyyy-MM-dd HH:mm:ss", System.currentTimeMillis());
+    private void log(int priority, String caller, String message) {
+        TransportUtils.log(priority, TAG, formatMessage(mPrefixForLog, caller, message));
+        saveLogEntry(formatMessage(null, caller, message));
+    }
+
+    private void saveLogEntry(String message) {
+        CharSequence time = DateFormat.format("yyyy-MM-dd HH:mm:ss", System.currentTimeMillis());
+        message = time + " " + message;
+        synchronized (mLogBufferLock) {
+            if (mLogBuffer.size() == LOG_BUFFER_SIZE) {
+                mLogBuffer.remove(mLogBuffer.size() - 1);
+            }
+            mLogBuffer.add(0, message);
+        }
+    }
+
+    List<String> getLogBuffer() {
+        synchronized (mLogBufferLock) {
+            return Collections.unmodifiableList(mLogBuffer);
+        }
     }
 
     @IntDef({Transition.DOWN, Transition.NO_TRANSITION, Transition.UP})
diff --git a/services/backup/java/com/android/server/backup/transport/TransportClientManager.java b/services/backup/java/com/android/server/backup/transport/TransportClientManager.java
index 1cbe7471..1132bce 100644
--- a/services/backup/java/com/android/server/backup/transport/TransportClientManager.java
+++ b/services/backup/java/com/android/server/backup/transport/TransportClientManager.java
@@ -17,19 +17,20 @@
 package com.android.server.backup.transport;
 
 import static com.android.server.backup.TransportManager.SERVICE_ACTION_TRANSPORT_HOST;
+import static com.android.server.backup.transport.TransportUtils.formatMessage;
 
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
 import android.util.Log;
-
 import com.android.server.backup.TransportManager;
+import java.io.PrintWriter;
+import java.util.Map;
+import java.util.WeakHashMap;
 
 /**
  * Manages the creation and disposal of {@link TransportClient}s. The only class that should use
  * this is {@link TransportManager}, all the other usages should go to {@link TransportManager}.
- *
- * <p>TODO(brufino): Implement pool of TransportClients
  */
 public class TransportClientManager {
     private static final String TAG = "TransportClientManager";
@@ -37,6 +38,7 @@
     private final Context mContext;
     private final Object mTransportClientsLock = new Object();
     private int mTransportClientsCreated = 0;
+    private Map<TransportClient, String> mTransportClientsCallerMap = new WeakHashMap<>();
 
     public TransportClientManager(Context context) {
         mContext = context;
@@ -62,8 +64,10 @@
                             bindIntent,
                             transportComponent,
                             Integer.toString(mTransportClientsCreated));
+            mTransportClientsCallerMap.put(transportClient, caller);
             mTransportClientsCreated++;
-            TransportUtils.log(Log.DEBUG, TAG, caller, "Retrieving " + transportClient);
+            TransportUtils.log(
+                    Log.DEBUG, TAG, formatMessage(null, caller, "Retrieving " + transportClient));
             return transportClient;
         }
     }
@@ -77,7 +81,25 @@
      *     details.
      */
     public void disposeOfTransportClient(TransportClient transportClient, String caller) {
-        TransportUtils.log(Log.DEBUG, TAG, caller, "Disposing of " + transportClient);
         transportClient.unbind(caller);
+        synchronized (mTransportClientsLock) {
+            TransportUtils.log(
+                    Log.DEBUG, TAG, formatMessage(null, caller, "Disposing of " + transportClient));
+            mTransportClientsCallerMap.remove(transportClient);
+        }
+    }
+
+    public void dump(PrintWriter pw) {
+        pw.println("Transport clients created: " + mTransportClientsCreated);
+        synchronized (mTransportClientsLock) {
+            pw.println("Current transport clients: " + mTransportClientsCallerMap.size());
+            for (TransportClient transportClient : mTransportClientsCallerMap.keySet()) {
+                String caller = mTransportClientsCallerMap.get(transportClient);
+                pw.println("    " + transportClient + " [" + caller + "]");
+                for (String logEntry : transportClient.getLogBuffer()) {
+                    pw.println("        " + logEntry);
+                }
+            }
+        }
     }
 }
diff --git a/services/backup/java/com/android/server/backup/transport/TransportUtils.java b/services/backup/java/com/android/server/backup/transport/TransportUtils.java
index 92bba9b..56b2d44 100644
--- a/services/backup/java/com/android/server/backup/transport/TransportUtils.java
+++ b/services/backup/java/com/android/server/backup/transport/TransportUtils.java
@@ -41,22 +41,21 @@
     }
 
     static void log(int priority, String tag, String message) {
-        log(priority, tag, null, message);
-    }
-
-    static void log(int priority, String tag, @Nullable String caller, String message) {
-        log(priority, tag, "", caller, message);
-    }
-
-    static void log(
-            int priority, String tag, String prefix, @Nullable String caller, String message) {
         if (Log.isLoggable(tag, priority)) {
-            if (caller != null) {
-                prefix += "[" + caller + "] ";
-            }
-            Slog.println(priority, tag, prefix + message);
+            Slog.println(priority, tag, message);
         }
     }
 
+    static String formatMessage(@Nullable String prefix, @Nullable String caller, String message) {
+        StringBuilder string = new StringBuilder();
+        if (prefix != null) {
+            string.append(prefix).append(" ");
+        }
+        if (caller != null) {
+            string.append("[").append(caller).append("] ");
+        }
+        return string.append(message).toString();
+    }
+
     private TransportUtils() {}
 }
diff --git a/services/core/java/com/android/server/AppOpsService.java b/services/core/java/com/android/server/AppOpsService.java
index 4ffa5f1..f4675fd 100644
--- a/services/core/java/com/android/server/AppOpsService.java
+++ b/services/core/java/com/android/server/AppOpsService.java
@@ -1316,14 +1316,8 @@
                             isPrivileged = (appInfo.privateFlags
                                     & ApplicationInfo.PRIVATE_FLAG_PRIVILEGED) != 0;
                         } else {
-                            if ("media".equals(packageName)) {
-                                pkgUid = Process.MEDIA_UID;
-                                isPrivileged = false;
-                            } else if ("audioserver".equals(packageName)) {
-                                pkgUid = Process.AUDIOSERVER_UID;
-                                isPrivileged = false;
-                            } else if ("cameraserver".equals(packageName)) {
-                                pkgUid = Process.CAMERASERVER_UID;
+                            pkgUid = resolveUid(packageName);
+                            if (pkgUid >= 0) {
                                 isPrivileged = false;
                             }
                         }
@@ -1957,9 +1951,8 @@
             if (nonpackageUid != -1) {
                 packageName = null;
             } else {
-                if ("root".equals(packageName)) {
-                    packageUid = 0;
-                } else {
+                packageUid = resolveUid(packageName);
+                if (packageUid < 0) {
                     packageUid = AppGlobals.getPackageManager().getPackageUid(packageName,
                             PackageManager.MATCH_UNINSTALLED_PACKAGES, userId);
                 }
@@ -2052,6 +2045,10 @@
                     }
                     if (ops == null || ops.size() <= 0) {
                         pw.println("No operations.");
+                        if (shell.op > AppOpsManager.OP_NONE && shell.op < AppOpsManager._NUM_OP) {
+                            pw.println("Default mode: " + AppOpsManager.modeToString(
+                                    AppOpsManager.opToDefaultMode(shell.op)));
+                        }
                         return 0;
                     }
                     final long now = System.currentTimeMillis();
@@ -2061,24 +2058,7 @@
                             AppOpsManager.OpEntry ent = entries.get(j);
                             pw.print(AppOpsManager.opToName(ent.getOp()));
                             pw.print(": ");
-                            switch (ent.getMode()) {
-                                case AppOpsManager.MODE_ALLOWED:
-                                    pw.print("allow");
-                                    break;
-                                case AppOpsManager.MODE_IGNORED:
-                                    pw.print("ignore");
-                                    break;
-                                case AppOpsManager.MODE_ERRORED:
-                                    pw.print("deny");
-                                    break;
-                                case AppOpsManager.MODE_DEFAULT:
-                                    pw.print("default");
-                                    break;
-                                default:
-                                    pw.print("mode=");
-                                    pw.print(ent.getMode());
-                                    break;
-                            }
+                            pw.print(AppOpsManager.modeToString(ent.getMode()));
                             if (ent.getTime() != 0) {
                                 pw.print("; time=");
                                 TimeUtils.formatDuration(now - ent.getTime(), pw);
@@ -2563,16 +2543,41 @@
     }
 
     private static String resolvePackageName(int uid, String packageName)  {
-        if (uid == 0) {
+        if (uid == Process.ROOT_UID) {
             return "root";
         } else if (uid == Process.SHELL_UID) {
             return "com.android.shell";
+        } else if (uid == Process.MEDIA_UID) {
+            return "media";
+        } else if (uid == Process.AUDIOSERVER_UID) {
+            return "audioserver";
+        } else if (uid == Process.CAMERASERVER_UID) {
+            return "cameraserver";
         } else if (uid == Process.SYSTEM_UID && packageName == null) {
             return "android";
         }
         return packageName;
     }
 
+    private static int resolveUid(String packageName)  {
+        if (packageName == null) {
+            return -1;
+        }
+        switch (packageName) {
+            case "root":
+                return Process.ROOT_UID;
+            case "shell":
+                return Process.SHELL_UID;
+            case "media":
+                return Process.MEDIA_UID;
+            case "audioserver":
+                return Process.AUDIOSERVER_UID;
+            case "cameraserver":
+                return Process.CAMERASERVER_UID;
+        }
+        return -1;
+    }
+
     private static String[] getPackagesForUid(int uid) {
         String[] packageNames = null;
         try {
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index c1f4b78..5030dce 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -69,6 +69,7 @@
 import android.net.NetworkSpecifier;
 import android.net.NetworkState;
 import android.net.NetworkUtils;
+import android.net.NetworkWatchlistManager;
 import android.net.Proxy;
 import android.net.ProxyInfo;
 import android.net.RouteInfo;
@@ -5708,6 +5709,17 @@
                 Settings.Global.NETWORK_AVOID_BAD_WIFI, null);
     }
 
+    @Override
+    public byte[] getNetworkWatchlistConfigHash() {
+        NetworkWatchlistManager nwm = mContext.getSystemService(NetworkWatchlistManager.class);
+        if (nwm == null) {
+            loge("Unable to get NetworkWatchlistManager");
+            return null;
+        }
+        // Redirect it to network watchlist service to access watchlist file and calculate hash.
+        return nwm.getWatchlistConfigHash();
+    }
+
     @VisibleForTesting
     public NetworkMonitor createNetworkMonitor(Context context, Handler handler,
             NetworkAgentInfo nai, NetworkRequest defaultRequest) {
diff --git a/services/core/java/com/android/server/ForceAppStandbyTracker.java b/services/core/java/com/android/server/ForceAppStandbyTracker.java
index a75a367..de113a6 100644
--- a/services/core/java/com/android/server/ForceAppStandbyTracker.java
+++ b/services/core/java/com/android/server/ForceAppStandbyTracker.java
@@ -26,6 +26,8 @@
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.database.ContentObserver;
+import android.net.Uri;
+import android.os.BatteryManager;
 import android.os.Handler;
 import android.os.Looper;
 import android.os.Message;
@@ -89,6 +91,9 @@
 
     private final MyHandler mHandler;
 
+    @VisibleForTesting
+    FeatureFlagsObserver mFlagsObserver;
+
     /**
      * Pair of (uid (not user-id), packageName) with OP_RUN_ANY_IN_BACKGROUND *not* allowed.
      */
@@ -113,14 +118,36 @@
     @GuardedBy("mLock")
     boolean mStarted;
 
+    /**
+     * Only used for small battery use-case.
+     */
     @GuardedBy("mLock")
-    boolean mForceAllAppsStandby;   // True if device is in extreme battery saver mode
+    boolean mIsPluggedIn;
 
     @GuardedBy("mLock")
-    boolean mForcedAppStandbyEnabled;   // True if the forced app standby feature is enabled
+    boolean mBatterySaverEnabled;
 
-    private class FeatureFlagObserver extends ContentObserver {
-        FeatureFlagObserver() {
+    /**
+     * True if the forced app standby is currently enabled
+     */
+    @GuardedBy("mLock")
+    boolean mForceAllAppsStandby;
+
+    /**
+     * True if the forced app standby for small battery devices feature is enabled in settings
+     */
+    @GuardedBy("mLock")
+    boolean mForceAllAppStandbyForSmallBattery;
+
+    /**
+     * True if the forced app standby feature is enabled in settings
+     */
+    @GuardedBy("mLock")
+    boolean mForcedAppStandbyEnabled;
+
+    @VisibleForTesting
+    class FeatureFlagsObserver extends ContentObserver {
+        FeatureFlagsObserver() {
             super(null);
         }
 
@@ -128,6 +155,9 @@
             mContext.getContentResolver().registerContentObserver(
                     Settings.Global.getUriFor(Settings.Global.FORCED_APP_STANDBY_ENABLED),
                     false, this);
+
+            mContext.getContentResolver().registerContentObserver(Settings.Global.getUriFor(
+                    Settings.Global.FORCED_APP_STANDBY_FOR_SMALL_BATTERY_ENABLED), false, this);
         }
 
         boolean isForcedAppStandbyEnabled() {
@@ -135,20 +165,43 @@
                     Settings.Global.FORCED_APP_STANDBY_ENABLED, 1) == 1;
         }
 
+        boolean isForcedAppStandbyForSmallBatteryEnabled() {
+            return Settings.Global.getInt(mContext.getContentResolver(),
+                    Settings.Global.FORCED_APP_STANDBY_FOR_SMALL_BATTERY_ENABLED, 0) == 1;
+        }
+
         @Override
-        public void onChange(boolean selfChange) {
-            final boolean enabled = isForcedAppStandbyEnabled();
-            synchronized (mLock) {
-                if (mForcedAppStandbyEnabled == enabled) {
-                    return;
+        public void onChange(boolean selfChange, Uri uri) {
+            if (Settings.Global.getUriFor(Settings.Global.FORCED_APP_STANDBY_ENABLED).equals(uri)) {
+                final boolean enabled = isForcedAppStandbyEnabled();
+                synchronized (mLock) {
+                    if (mForcedAppStandbyEnabled == enabled) {
+                        return;
+                    }
+                    mForcedAppStandbyEnabled = enabled;
+                    if (DEBUG) {
+                        Slog.d(TAG,"Forced app standby feature flag changed: "
+                                + mForcedAppStandbyEnabled);
+                    }
                 }
-                mForcedAppStandbyEnabled = enabled;
-                if (DEBUG) {
-                    Slog.d(TAG,
-                            "Forced app standby feature flag changed: " + mForcedAppStandbyEnabled);
+                mHandler.notifyForcedAppStandbyFeatureFlagChanged();
+            } else if (Settings.Global.getUriFor(
+                    Settings.Global.FORCED_APP_STANDBY_FOR_SMALL_BATTERY_ENABLED).equals(uri)) {
+                final boolean enabled = isForcedAppStandbyForSmallBatteryEnabled();
+                synchronized (mLock) {
+                    if (mForceAllAppStandbyForSmallBattery == enabled) {
+                        return;
+                    }
+                    mForceAllAppStandbyForSmallBattery = enabled;
+                    if (DEBUG) {
+                        Slog.d(TAG, "Forced app standby for small battery feature flag changed: "
+                                + mForceAllAppStandbyForSmallBattery);
+                    }
+                    updateForceAllAppStandbyState();
                 }
+            } else {
+                Slog.w(TAG, "Unexpected feature flag uri encountered: " + uri);
             }
-            mHandler.notifyFeatureFlagChanged();
         }
     }
 
@@ -289,9 +342,11 @@
             mAppOpsManager = Preconditions.checkNotNull(injectAppOpsManager());
             mAppOpsService = Preconditions.checkNotNull(injectIAppOpsService());
             mPowerManagerInternal = Preconditions.checkNotNull(injectPowerManagerInternal());
-            final FeatureFlagObserver flagObserver = new FeatureFlagObserver();
-            flagObserver.register();
-            mForcedAppStandbyEnabled = flagObserver.isForcedAppStandbyEnabled();
+            mFlagsObserver = new FeatureFlagsObserver();
+            mFlagsObserver.register();
+            mForcedAppStandbyEnabled = mFlagsObserver.isForcedAppStandbyEnabled();
+            mForceAllAppStandbyForSmallBattery =
+                    mFlagsObserver.isForcedAppStandbyForSmallBatteryEnabled();
 
             try {
                 mIActivityManager.registerUidObserver(new UidObserver(),
@@ -306,16 +361,24 @@
 
             IntentFilter filter = new IntentFilter();
             filter.addAction(Intent.ACTION_USER_REMOVED);
+            filter.addAction(Intent.ACTION_BATTERY_CHANGED);
             mContext.registerReceiver(new MyReceiver(), filter);
 
             refreshForcedAppStandbyUidPackagesLocked();
 
             mPowerManagerInternal.registerLowPowerModeObserver(
                     ServiceType.FORCE_ALL_APPS_STANDBY,
-                    (state) -> updateForceAllAppsStandby(state.batterySaverEnabled));
+                    (state) -> {
+                        synchronized (mLock) {
+                            mBatterySaverEnabled = state.batterySaverEnabled;
+                            updateForceAllAppStandbyState();
+                        }
+                    });
 
-            updateForceAllAppsStandby(mPowerManagerInternal.getLowPowerState(
-                    ServiceType.FORCE_ALL_APPS_STANDBY).batterySaverEnabled);
+            mBatterySaverEnabled = mPowerManagerInternal.getLowPowerState(
+                    ServiceType.FORCE_ALL_APPS_STANDBY).batterySaverEnabled;
+
+            updateForceAllAppStandbyState();
         }
     }
 
@@ -340,6 +403,11 @@
         return LocalServices.getService(PowerManagerInternal.class);
     }
 
+    @VisibleForTesting
+    boolean isSmallBatteryDevice() {
+        return ActivityManager.isSmallBatteryDevice();
+    }
+
     /**
      * Update {@link #mRunAnyRestrictedPackages} with the current app ops state.
      */
@@ -369,18 +437,26 @@
         }
     }
 
+    private void updateForceAllAppStandbyState() {
+        synchronized (mLock) {
+            if (mForceAllAppStandbyForSmallBattery && isSmallBatteryDevice()) {
+                toggleForceAllAppsStandbyLocked(!mIsPluggedIn);
+            } else {
+                toggleForceAllAppsStandbyLocked(mBatterySaverEnabled);
+            }
+        }
+    }
+
     /**
      * Update {@link #mForceAllAppsStandby} and notifies the listeners.
      */
-    void updateForceAllAppsStandby(boolean enable) {
-        synchronized (mLock) {
-            if (enable == mForceAllAppsStandby) {
-                return;
-            }
-            mForceAllAppsStandby = enable;
-
-            mHandler.notifyForceAllAppsStandbyChanged();
+    private void toggleForceAllAppsStandbyLocked(boolean enable) {
+        if (enable == mForceAllAppsStandby) {
+            return;
         }
+        mForceAllAppsStandby = enable;
+
+        mHandler.notifyForceAllAppsStandbyChanged();
     }
 
     private int findForcedAppStandbyUidPackageIndexLocked(int uid, @NonNull String packageName) {
@@ -515,6 +591,11 @@
                 if (userId > 0) {
                     mHandler.doUserRemoved(userId);
                 }
+            } else if (Intent.ACTION_BATTERY_CHANGED.equals(intent.getAction())) {
+                synchronized (mLock) {
+                    mIsPluggedIn = (intent.getIntExtra(BatteryManager.EXTRA_PLUGGED, 0) != 0);
+                }
+                updateForceAllAppStandbyState();
             }
         }
     }
@@ -533,7 +614,7 @@
         private static final int MSG_TEMP_WHITELIST_CHANGED = 5;
         private static final int MSG_FORCE_ALL_CHANGED = 6;
         private static final int MSG_USER_REMOVED = 7;
-        private static final int MSG_FEATURE_FLAG_CHANGED = 8;
+        private static final int MSG_FORCE_APP_STANDBY_FEATURE_FLAG_CHANGED = 8;
 
         public MyHandler(Looper looper) {
             super(looper);
@@ -563,8 +644,8 @@
             obtainMessage(MSG_FORCE_ALL_CHANGED).sendToTarget();
         }
 
-        public void notifyFeatureFlagChanged() {
-            obtainMessage(MSG_FEATURE_FLAG_CHANGED).sendToTarget();
+        public void notifyForcedAppStandbyFeatureFlagChanged() {
+            obtainMessage(MSG_FORCE_APP_STANDBY_FEATURE_FLAG_CHANGED).sendToTarget();
         }
 
         public void doUserRemoved(int userId) {
@@ -618,7 +699,7 @@
                         l.onForceAllAppsStandbyChanged(sender);
                     }
                     return;
-                case MSG_FEATURE_FLAG_CHANGED:
+                case MSG_FORCE_APP_STANDBY_FEATURE_FLAG_CHANGED:
                     // Feature flag for forced app standby changed.
                     final boolean unblockAlarms;
                     synchronized (mLock) {
@@ -737,21 +818,23 @@
      * @return whether alarms should be restricted for a UID package-name.
      */
     public boolean areAlarmsRestricted(int uid, @NonNull String packageName) {
-        return isRestricted(uid, packageName, /*useTempWhitelistToo=*/ false);
+        return isRestricted(uid, packageName, /*useTempWhitelistToo=*/ false,
+                /* exemptOnBatterySaver =*/ false);
     }
 
     /**
      * @return whether jobs should be restricted for a UID package-name.
      */
     public boolean areJobsRestricted(int uid, @NonNull String packageName) {
-        return isRestricted(uid, packageName, /*useTempWhitelistToo=*/ true);
+        return isRestricted(uid, packageName, /*useTempWhitelistToo=*/ true,
+                /* exemptOnBatterySaver =*/ false);
     }
 
     /**
      * @return whether force-app-standby is effective for a UID package-name.
      */
     private boolean isRestricted(int uid, @NonNull String packageName,
-            boolean useTempWhitelistToo) {
+            boolean useTempWhitelistToo, boolean exemptOnBatterySaver) {
         if (isInForeground(uid)) {
             return false;
         }
@@ -765,12 +848,13 @@
                     ArrayUtils.contains(mTempWhitelistedAppIds, appId)) {
                 return false;
             }
-
-            if (mForceAllAppsStandby) {
+            if (mForcedAppStandbyEnabled && isRunAnyRestrictedLocked(uid, packageName)) {
                 return true;
             }
-
-            return mForcedAppStandbyEnabled && isRunAnyRestrictedLocked(uid, packageName);
+            if (exemptOnBatterySaver) {
+                return false;
+            }
+            return mForceAllAppsStandby;
         }
     }
 
@@ -842,6 +926,18 @@
             pw.println(isForceAllAppsStandbyEnabled());
 
             pw.print(indent);
+            pw.print("Small Battery Device: ");
+            pw.println(isSmallBatteryDevice());
+
+            pw.print(indent);
+            pw.print("Force all apps standby for small battery device: ");
+            pw.println(mForceAllAppStandbyForSmallBattery);
+
+            pw.print(indent);
+            pw.print("Plugged In: ");
+            pw.println(mIsPluggedIn);
+
+            pw.print(indent);
             pw.print("Foreground uids: [");
 
             String sep = "";
@@ -880,6 +976,11 @@
             final long token = proto.start(fieldId);
 
             proto.write(ForceAppStandbyTrackerProto.FORCE_ALL_APPS_STANDBY, mForceAllAppsStandby);
+            proto.write(ForceAppStandbyTrackerProto.IS_SMALL_BATTERY_DEVICE,
+                    isSmallBatteryDevice());
+            proto.write(ForceAppStandbyTrackerProto.FORCE_ALL_APPS_STANDBY_FOR_SMALL_BATTERY,
+                    mForceAllAppStandbyForSmallBattery);
+            proto.write(ForceAppStandbyTrackerProto.IS_CHARGING, mIsPluggedIn);
 
             for (int i = 0; i < mForegroundUids.size(); i++) {
                 if (mForegroundUids.valueAt(i)) {
diff --git a/services/core/java/com/android/server/IpSecService.java b/services/core/java/com/android/server/IpSecService.java
index 46a35ec..ef6bc43 100644
--- a/services/core/java/com/android/server/IpSecService.java
+++ b/services/core/java/com/android/server/IpSecService.java
@@ -148,7 +148,7 @@
          * resources.
          *
          * <p>References to the IResource object may be held by other RefcountedResource objects,
-         * and as such, the kernel resources and quota may not be cleaned up.
+         * and as such, the underlying resources and quota may not be cleaned up.
          */
         void invalidate() throws RemoteException;
 
@@ -298,7 +298,12 @@
         }
     }
 
-    /* Very simple counting class that looks much like a counting semaphore */
+    /**
+     * Very simple counting class that looks much like a counting semaphore
+     *
+     * <p>This class is not thread-safe, and expects that that users of this class will ensure
+     * synchronization and thread safety by holding the IpSecService.this instance lock.
+     */
     @VisibleForTesting
     static class ResourceTracker {
         private final int mMax;
@@ -341,26 +346,38 @@
 
     @VisibleForTesting
     static final class UserRecord {
-        /* Type names */
-        public static final String TYPENAME_SPI = "SecurityParameterIndex";
-        public static final String TYPENAME_TRANSFORM = "IpSecTransform";
-        public static final String TYPENAME_ENCAP_SOCKET = "UdpEncapSocket";
-
         /* Maximum number of each type of resource that a single UID may possess */
         public static final int MAX_NUM_ENCAP_SOCKETS = 2;
         public static final int MAX_NUM_TRANSFORMS = 4;
         public static final int MAX_NUM_SPIS = 8;
 
+        /**
+         * Store each of the OwnedResource types in an (thinly wrapped) sparse array for indexing
+         * and explicit (user) reference management.
+         *
+         * <p>These are stored in separate arrays to improve debuggability and dump output clarity.
+         *
+         * <p>Resources are removed from this array when the user releases their explicit reference
+         * by calling one of the releaseResource() methods.
+         */
         final RefcountedResourceArray<SpiRecord> mSpiRecords =
-                new RefcountedResourceArray<>(TYPENAME_SPI);
-        final ResourceTracker mSpiQuotaTracker = new ResourceTracker(MAX_NUM_SPIS);
-
+                new RefcountedResourceArray<>(SpiRecord.class.getSimpleName());
         final RefcountedResourceArray<TransformRecord> mTransformRecords =
-                new RefcountedResourceArray<>(TYPENAME_TRANSFORM);
-        final ResourceTracker mTransformQuotaTracker = new ResourceTracker(MAX_NUM_TRANSFORMS);
-
+                new RefcountedResourceArray<>(TransformRecord.class.getSimpleName());
         final RefcountedResourceArray<EncapSocketRecord> mEncapSocketRecords =
-                new RefcountedResourceArray<>(TYPENAME_ENCAP_SOCKET);
+                new RefcountedResourceArray<>(EncapSocketRecord.class.getSimpleName());
+
+        /**
+         * Trackers for quotas for each of the OwnedResource types.
+         *
+         * <p>These trackers are separate from the resource arrays, since they are incremented and
+         * decremented at different points in time. Specifically, quota is only returned upon final
+         * resource deallocation (after all explicit and implicit references are released). Note
+         * that it is possible that calls to releaseResource() will not return the used quota if
+         * there are other resources that depend on (are parents of) the resource being released.
+         */
+        final ResourceTracker mSpiQuotaTracker = new ResourceTracker(MAX_NUM_SPIS);
+        final ResourceTracker mTransformQuotaTracker = new ResourceTracker(MAX_NUM_TRANSFORMS);
         final ResourceTracker mSocketQuotaTracker = new ResourceTracker(MAX_NUM_ENCAP_SOCKETS);
 
         void removeSpiRecord(int resourceId) {
@@ -395,11 +412,15 @@
         }
     }
 
+    /**
+     * This class is not thread-safe, and expects that that users of this class will ensure
+     * synchronization and thread safety by holding the IpSecService.this instance lock.
+     */
     @VisibleForTesting
     static final class UserResourceTracker {
         private final SparseArray<UserRecord> mUserRecords = new SparseArray<>();
 
-        /** Never-fail getter that populates the list of UIDs as-needed */
+        /** Lazy-initialization/getter that populates or retrieves the UserRecord as needed */
         public UserRecord getUserRecord(int uid) {
             checkCallerUid(uid);
 
@@ -428,18 +449,20 @@
     @VisibleForTesting final UserResourceTracker mUserResourceTracker = new UserResourceTracker();
 
     /**
-     * The KernelResourceRecord class provides a facility to cleanly and reliably track system
+     * The OwnedResourceRecord class provides a facility to cleanly and reliably track system
      * resources. It relies on a provided resourceId that should uniquely identify the kernel
      * resource. To use this class, the user should implement the invalidate() and
      * freeUnderlyingResources() methods that are responsible for cleaning up IpSecService resource
-     * tracking arrays and kernel resources, respectively
+     * tracking arrays and kernel resources, respectively.
+     *
+     * <p>This class associates kernel resources with the UID that owns and controls them.
      */
-    private abstract class KernelResourceRecord implements IResource {
+    private abstract class OwnedResourceRecord implements IResource {
         final int pid;
         final int uid;
         protected final int mResourceId;
 
-        KernelResourceRecord(int resourceId) {
+        OwnedResourceRecord(int resourceId) {
             super();
             if (resourceId == INVALID_RESOURCE_ID) {
                 throw new IllegalArgumentException("Resource ID must not be INVALID_RESOURCE_ID");
@@ -479,8 +502,6 @@
         }
     };
 
-    // TODO: Move this to right after RefcountedResource. With this here, Gerrit was showing many
-    // more things as changed.
     /**
      * Thin wrapper over SparseArray to ensure resources exist, and simplify generic typing.
      *
@@ -534,7 +555,12 @@
         }
     }
 
-    private final class TransformRecord extends KernelResourceRecord {
+    /**
+     * Tracks an SA in the kernel, and manages cleanup paths. Once a TransformRecord is
+     * created, the SpiRecord that originally tracked the SAs will reliquish the
+     * responsibility of freeing the underlying SA to this class via the mOwnedByTransform flag.
+     */
+    private final class TransformRecord extends OwnedResourceRecord {
         private final IpSecConfig mConfig;
         private final SpiRecord mSpi;
         private final EncapSocketRecord mSocket;
@@ -603,7 +629,12 @@
         }
     }
 
-    private final class SpiRecord extends KernelResourceRecord {
+    /**
+     * Tracks a single SA in the kernel, and manages cleanup paths. Once used in a Transform, the
+     * responsibility for cleaning up underlying resources will be passed to the TransformRecord
+     * object
+     */
+    private final class SpiRecord extends OwnedResourceRecord {
         private final String mSourceAddress;
         private final String mDestinationAddress;
         private int mSpi;
@@ -692,7 +723,14 @@
         }
     }
 
-    private final class EncapSocketRecord extends KernelResourceRecord {
+    /**
+     * Tracks a UDP encap socket, and manages cleanup paths
+     *
+     * <p>While this class does not manage non-kernel resources, race conditions around socket
+     * binding require that the service creates the encap socket, binds it and applies the socket
+     * policy before handing it to a user.
+     */
+    private final class EncapSocketRecord extends OwnedResourceRecord {
         private FileDescriptor mSocket;
         private final int mPort;
 
@@ -1105,16 +1143,14 @@
      * receive data.
      */
     @Override
-    public synchronized IpSecTransformResponse createTransportModeTransform(
-            IpSecConfig c, IBinder binder) throws RemoteException {
+    public synchronized IpSecTransformResponse createTransform(IpSecConfig c, IBinder binder)
+            throws RemoteException {
         checkIpSecConfig(c);
-        checkNotNull(binder, "Null Binder passed to createTransportModeTransform");
+        checkNotNull(binder, "Null Binder passed to createTransform");
         final int resourceId = mNextResourceId++;
 
         UserRecord userRecord = mUserResourceTracker.getUserRecord(Binder.getCallingUid());
-
-        // Avoid resizing by creating a dependency array of min-size 2 (1 UDP encap + 1 SPI)
-        List<RefcountedResource> dependencies = new ArrayList<>(2);
+        List<RefcountedResource> dependencies = new ArrayList<>();
 
         if (!userRecord.mTransformQuotaTracker.isAvailable()) {
             return new IpSecTransformResponse(IpSecManager.Status.RESOURCE_UNAVAILABLE);
@@ -1186,7 +1222,7 @@
      * other reasons.
      */
     @Override
-    public synchronized void deleteTransportModeTransform(int resourceId) throws RemoteException {
+    public synchronized void deleteTransform(int resourceId) throws RemoteException {
         UserRecord userRecord = mUserResourceTracker.getUserRecord(Binder.getCallingUid());
         releaseResource(userRecord.mTransformRecords, resourceId);
     }
diff --git a/services/core/java/com/android/server/PersistentDataBlockManagerInternal.java b/services/core/java/com/android/server/PersistentDataBlockManagerInternal.java
index 80f8e51..833def3 100644
--- a/services/core/java/com/android/server/PersistentDataBlockManagerInternal.java
+++ b/services/core/java/com/android/server/PersistentDataBlockManagerInternal.java
@@ -26,4 +26,7 @@
 
     /** Retrieves handle to a lockscreen credential to be used for Factory Reset Protection. */
     byte[] getFrpCredentialHandle();
+
+    /** Update the OEM unlock enabled bit, bypassing user restriction checks. */
+    void forceOemUnlockEnabled(boolean enabled);
 }
diff --git a/services/core/java/com/android/server/PersistentDataBlockService.java b/services/core/java/com/android/server/PersistentDataBlockService.java
index c32a2d1..4298140 100644
--- a/services/core/java/com/android/server/PersistentDataBlockService.java
+++ b/services/core/java/com/android/server/PersistentDataBlockService.java
@@ -668,5 +668,13 @@
                 IoUtils.closeQuietly(inputStream);
             }
         }
+
+        @Override
+        public void forceOemUnlockEnabled(boolean enabled) {
+            synchronized (mLock) {
+                doSetOemUnlockEnabledLocked(enabled);
+                computeAndWriteDigestLocked();
+            }
+        }
     };
 }
diff --git a/services/core/java/com/android/server/SystemUpdateManagerService.java b/services/core/java/com/android/server/SystemUpdateManagerService.java
new file mode 100644
index 0000000..6c1ffdd
--- /dev/null
+++ b/services/core/java/com/android/server/SystemUpdateManagerService.java
@@ -0,0 +1,255 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server;
+
+import static android.os.SystemUpdateManager.KEY_STATUS;
+import static android.os.SystemUpdateManager.STATUS_IDLE;
+import static android.os.SystemUpdateManager.STATUS_UNKNOWN;
+
+import static org.xmlpull.v1.XmlPullParser.END_DOCUMENT;
+import static org.xmlpull.v1.XmlPullParser.END_TAG;
+import static org.xmlpull.v1.XmlPullParser.START_TAG;
+
+import android.Manifest;
+import android.annotation.Nullable;
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.os.Binder;
+import android.os.Build;
+import android.os.Bundle;
+import android.os.Environment;
+import android.os.ISystemUpdateManager;
+import android.os.PersistableBundle;
+import android.os.SystemUpdateManager;
+import android.provider.Settings;
+import android.util.AtomicFile;
+import android.util.Slog;
+import android.util.Xml;
+
+import com.android.internal.util.FastXmlSerializer;
+import com.android.internal.util.XmlUtils;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+import org.xmlpull.v1.XmlSerializer;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+
+public class SystemUpdateManagerService extends ISystemUpdateManager.Stub {
+
+    private static final String TAG = "SystemUpdateManagerService";
+
+    private static final int UID_UNKNOWN = -1;
+
+    private static final String INFO_FILE = "system-update-info.xml";
+    private static final int INFO_FILE_VERSION = 0;
+    private static final String TAG_INFO = "info";
+    private static final String KEY_VERSION = "version";
+    private static final String KEY_UID = "uid";
+    private static final String KEY_BOOT_COUNT = "boot-count";
+    private static final String KEY_INFO_BUNDLE = "info-bundle";
+
+    private final Context mContext;
+    private final AtomicFile mFile;
+    private final Object mLock = new Object();
+    private int mLastUid = UID_UNKNOWN;
+    private int mLastStatus = STATUS_UNKNOWN;
+
+    public SystemUpdateManagerService(Context context) {
+        mContext = context;
+        mFile = new AtomicFile(new File(Environment.getDataSystemDirectory(), INFO_FILE));
+
+        // Populate mLastUid and mLastStatus.
+        synchronized (mLock) {
+            loadSystemUpdateInfoLocked();
+        }
+    }
+
+    @Override
+    public void updateSystemUpdateInfo(PersistableBundle infoBundle) {
+        mContext.enforceCallingOrSelfPermission(Manifest.permission.RECOVERY, TAG);
+
+        int status = infoBundle.getInt(KEY_STATUS, STATUS_UNKNOWN);
+        if (status == STATUS_UNKNOWN) {
+            Slog.w(TAG, "Invalid status info. Ignored");
+            return;
+        }
+
+        // There could be multiple updater apps running on a device. But only one at most should
+        // be active (i.e. with a pending update), with the rest reporting idle status. We will
+        // only accept the reported status if any of the following conditions holds:
+        //   a) none has been reported before;
+        //   b) the current on-file status was last reported by the same caller;
+        //   c) an active update is being reported.
+        int uid = Binder.getCallingUid();
+        if (mLastUid == UID_UNKNOWN || mLastUid == uid || status != STATUS_IDLE) {
+            synchronized (mLock) {
+                saveSystemUpdateInfoLocked(infoBundle, uid);
+            }
+        } else {
+            Slog.i(TAG, "Inactive updater reporting IDLE status. Ignored");
+        }
+    }
+
+    @Override
+    public Bundle retrieveSystemUpdateInfo() {
+        if (mContext.checkCallingOrSelfPermission(Manifest.permission.READ_SYSTEM_UPDATE_INFO)
+                == PackageManager.PERMISSION_DENIED
+                && mContext.checkCallingOrSelfPermission(Manifest.permission.RECOVERY)
+                == PackageManager.PERMISSION_DENIED) {
+            throw new SecurityException("Can't read system update info. Requiring "
+                    + "READ_SYSTEM_UPDATE_INFO or RECOVERY permission.");
+        }
+
+        synchronized (mLock) {
+            return loadSystemUpdateInfoLocked();
+        }
+    }
+
+    // Reads and validates the info file. Returns the loaded info bundle on success; or a default
+    // info bundle with UNKNOWN status.
+    private Bundle loadSystemUpdateInfoLocked() {
+        PersistableBundle loadedBundle = null;
+        try (FileInputStream fis = mFile.openRead()) {
+            XmlPullParser parser = Xml.newPullParser();
+            parser.setInput(fis, StandardCharsets.UTF_8.name());
+            loadedBundle = readInfoFileLocked(parser);
+        } catch (FileNotFoundException e) {
+            Slog.i(TAG, "No existing info file " + mFile.getBaseFile());
+        } catch (XmlPullParserException e) {
+            Slog.e(TAG, "Failed to parse the info file:", e);
+        } catch (IOException e) {
+            Slog.e(TAG, "Failed to read the info file:", e);
+        }
+
+        // Validate the loaded bundle.
+        if (loadedBundle == null) {
+            return removeInfoFileAndGetDefaultInfoBundleLocked();
+        }
+
+        int version = loadedBundle.getInt(KEY_VERSION, -1);
+        if (version == -1) {
+            Slog.w(TAG, "Invalid info file (invalid version). Ignored");
+            return removeInfoFileAndGetDefaultInfoBundleLocked();
+        }
+
+        int lastUid = loadedBundle.getInt(KEY_UID, -1);
+        if (lastUid == -1) {
+            Slog.w(TAG, "Invalid info file (invalid UID). Ignored");
+            return removeInfoFileAndGetDefaultInfoBundleLocked();
+        }
+
+        int lastBootCount = loadedBundle.getInt(KEY_BOOT_COUNT, -1);
+        if (lastBootCount == -1 || lastBootCount != getBootCount()) {
+            Slog.w(TAG, "Outdated info file. Ignored");
+            return removeInfoFileAndGetDefaultInfoBundleLocked();
+        }
+
+        PersistableBundle infoBundle = loadedBundle.getPersistableBundle(KEY_INFO_BUNDLE);
+        if (infoBundle == null) {
+            Slog.w(TAG, "Invalid info file (missing info). Ignored");
+            return removeInfoFileAndGetDefaultInfoBundleLocked();
+        }
+
+        int lastStatus = infoBundle.getInt(KEY_STATUS, STATUS_UNKNOWN);
+        if (lastStatus == STATUS_UNKNOWN) {
+            Slog.w(TAG, "Invalid info file (invalid status). Ignored");
+            return removeInfoFileAndGetDefaultInfoBundleLocked();
+        }
+
+        // Everything looks good upon reaching this point.
+        mLastStatus = lastStatus;
+        mLastUid = lastUid;
+        return new Bundle(infoBundle);
+    }
+
+    private void saveSystemUpdateInfoLocked(PersistableBundle infoBundle, int uid) {
+        // Wrap the incoming bundle with extra info (e.g. version, uid, boot count). We use nested
+        // PersistableBundle to avoid manually parsing XML attributes when loading the info back.
+        PersistableBundle outBundle = new PersistableBundle();
+        outBundle.putPersistableBundle(KEY_INFO_BUNDLE, infoBundle);
+        outBundle.putInt(KEY_VERSION, INFO_FILE_VERSION);
+        outBundle.putInt(KEY_UID, uid);
+        outBundle.putInt(KEY_BOOT_COUNT, getBootCount());
+
+        // Only update the info on success.
+        if (writeInfoFileLocked(outBundle)) {
+            mLastUid = uid;
+            mLastStatus = infoBundle.getInt(KEY_STATUS);
+        }
+    }
+
+    // Performs I/O work only, without validating the loaded info.
+    @Nullable
+    private PersistableBundle readInfoFileLocked(XmlPullParser parser)
+            throws XmlPullParserException, IOException {
+        int type;
+        while ((type = parser.next()) != END_DOCUMENT) {
+            if (type == START_TAG && TAG_INFO.equals(parser.getName())) {
+                return PersistableBundle.restoreFromXml(parser);
+            }
+        }
+        return null;
+    }
+
+    private boolean writeInfoFileLocked(PersistableBundle outBundle) {
+        FileOutputStream fos = null;
+        try {
+            fos = mFile.startWrite();
+
+            XmlSerializer out = new FastXmlSerializer();
+            out.setOutput(fos, StandardCharsets.UTF_8.name());
+            out.startDocument(null, true);
+
+            out.startTag(null, TAG_INFO);
+            outBundle.saveToXml(out);
+            out.endTag(null, TAG_INFO);
+
+            out.endDocument();
+            mFile.finishWrite(fos);
+            return true;
+        } catch (IOException | XmlPullParserException e) {
+            Slog.e(TAG, "Failed to save the info file:", e);
+            if (fos != null) {
+                mFile.failWrite(fos);
+            }
+        }
+        return false;
+    }
+
+    private Bundle removeInfoFileAndGetDefaultInfoBundleLocked() {
+        if (mFile.exists()) {
+            Slog.i(TAG, "Removing info file");
+            mFile.delete();
+        }
+
+        mLastStatus = STATUS_UNKNOWN;
+        mLastUid = UID_UNKNOWN;
+        Bundle infoBundle = new Bundle();
+        infoBundle.putInt(KEY_STATUS, STATUS_UNKNOWN);
+        return infoBundle;
+    }
+
+    private int getBootCount() {
+        return Settings.Global.getInt(mContext.getContentResolver(), Settings.Global.BOOT_COUNT, 0);
+    }
+}
diff --git a/services/core/java/com/android/server/am/ActivityDisplay.java b/services/core/java/com/android/server/am/ActivityDisplay.java
index e38148c..db21ef1 100644
--- a/services/core/java/com/android/server/am/ActivityDisplay.java
+++ b/services/core/java/com/android/server/am/ActivityDisplay.java
@@ -676,6 +676,15 @@
         }
     }
 
+    public void dumpStacks(PrintWriter pw) {
+        for (int i = mStacks.size() - 1; i >= 0; --i) {
+            pw.print(mStacks.get(i).mStackId);
+            if (i > 0) {
+                pw.print(",");
+            }
+        }
+    }
+
     public void writeToProto(ProtoOutputStream proto, long fieldId) {
         final long token = proto.start(fieldId);
         super.writeToProto(proto, CONFIGURATION_CONTAINER, false /* trim */);
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 29d33ce..dc34567 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -27,9 +27,9 @@
 import static android.Manifest.permission.READ_FRAME_BUFFER;
 import static android.Manifest.permission.REMOVE_TASKS;
 import static android.Manifest.permission.START_TASKS_FROM_RECENTS;
-import static android.app.ActivityManager.SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT;
 import static android.app.ActivityManager.LOCK_TASK_MODE_NONE;
 import static android.app.ActivityManager.RESIZE_MODE_PRESERVE_WINDOW;
+import static android.app.ActivityManager.SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT;
 import static android.app.ActivityManager.StackId.INVALID_STACK_ID;
 import static android.app.ActivityManagerInternal.ASSIST_KEY_CONTENT;
 import static android.app.ActivityManagerInternal.ASSIST_KEY_DATA;
@@ -120,6 +120,7 @@
 import static android.text.format.DateUtils.DAY_IN_MILLIS;
 import static android.view.Display.DEFAULT_DISPLAY;
 import static android.view.Display.INVALID_DISPLAY;
+
 import static com.android.internal.util.XmlUtils.readBooleanAttribute;
 import static com.android.internal.util.XmlUtils.readIntAttribute;
 import static com.android.internal.util.XmlUtils.readLongAttribute;
@@ -198,6 +199,7 @@
 import static android.view.WindowManager.TRANSIT_TASK_IN_PLACE;
 import static android.view.WindowManager.TRANSIT_TASK_OPEN;
 import static android.view.WindowManager.TRANSIT_TASK_TO_FRONT;
+
 import static org.xmlpull.v1.XmlPullParser.END_DOCUMENT;
 import static org.xmlpull.v1.XmlPullParser.START_TAG;
 
@@ -277,8 +279,8 @@
 import android.content.pm.InstrumentationInfo;
 import android.content.pm.PackageInfo;
 import android.content.pm.PackageManager;
-import android.content.pm.PackageManagerInternal;
 import android.content.pm.PackageManager.NameNotFoundException;
+import android.content.pm.PackageManagerInternal;
 import android.content.pm.ParceledListSlice;
 import android.content.pm.PathPermission;
 import android.content.pm.PermissionInfo;
@@ -356,18 +358,18 @@
 import android.util.ArrayMap;
 import android.util.ArraySet;
 import android.util.AtomicFile;
-import android.util.LongSparseArray;
-import android.util.StatsLog;
-import android.util.TimingsTraceLog;
 import android.util.DebugUtils;
 import android.util.EventLog;
 import android.util.Log;
+import android.util.LongSparseArray;
 import android.util.Pair;
 import android.util.PrintWriterPrinter;
 import android.util.Slog;
 import android.util.SparseArray;
 import android.util.SparseIntArray;
+import android.util.StatsLog;
 import android.util.TimeUtils;
+import android.util.TimingsTraceLog;
 import android.util.Xml;
 import android.util.proto.ProtoOutputStream;
 import android.view.Gravity;
@@ -394,6 +396,7 @@
 import com.android.internal.os.BackgroundThread;
 import com.android.internal.os.BatteryStatsImpl;
 import com.android.internal.os.BinderInternal;
+import com.android.internal.os.logging.MetricsLoggerWrapper;
 import com.android.internal.os.ByteTransferPipe;
 import com.android.internal.os.IResultReceiver;
 import com.android.internal.os.ProcessCpuTracker;
@@ -440,6 +443,12 @@
 import com.android.server.vr.VrManagerInternal;
 import com.android.server.wm.PinnedStackWindowController;
 import com.android.server.wm.WindowManagerService;
+
+import dalvik.system.VMRuntime;
+
+import libcore.io.IoUtils;
+import libcore.util.EmptyArray;
+
 import com.google.android.collect.Lists;
 import com.google.android.collect.Maps;
 
@@ -479,11 +488,6 @@
 import java.util.concurrent.atomic.AtomicBoolean;
 import java.util.concurrent.atomic.AtomicLong;
 
-import dalvik.system.VMRuntime;
-
-import libcore.io.IoUtils;
-import libcore.util.EmptyArray;
-
 public class ActivityManagerService extends IActivityManager.Stub
         implements Watchdog.Monitor, BatteryStatsImpl.BatteryCallback {
 
@@ -8388,8 +8392,7 @@
                     stack.setPictureInPictureAspectRatio(aspectRatio);
                     stack.setPictureInPictureActions(actions);
 
-                    MetricsLogger.action(mContext, MetricsEvent.ACTION_PICTURE_IN_PICTURE_ENTERED,
-                            r.supportsEnterPipOnTaskSwitch);
+                    MetricsLoggerWrapper.logPictureInPictureEnter(mContext, r.supportsEnterPipOnTaskSwitch);
                     logPictureInPictureArgs(params);
                 };
 
@@ -8601,6 +8604,16 @@
             }
             return false;
         }
+
+        @Override
+        public int getPackageUid(String packageName, int flags) {
+            try {
+                return mActivityManagerService.mContext.getPackageManager()
+                        .getPackageUid(packageName, flags);
+            } catch (NameNotFoundException nnfe) {
+                return -1;
+            }
+        }
     }
 
     class IntentFirewallInterface implements IntentFirewall.AMSInterface {
@@ -10404,10 +10417,9 @@
 
     @Override
     public Bitmap getTaskDescriptionIcon(String filePath, int userId) {
-        if (userId != UserHandle.getCallingUserId()) {
-            enforceCallingPermission(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL,
-                    "getTaskDescriptionIcon");
-        }
+        userId = mUserController.handleIncomingUser(Binder.getCallingPid(), Binder.getCallingUid(),
+                userId, false, ALLOW_FULL_ONLY, "getTaskDescriptionIcon", null);
+
         final File passedIconFile = new File(filePath);
         final File legitIconFile = new File(TaskPersister.getUserImagesDir(userId),
                 passedIconFile.getName());
@@ -21470,6 +21482,17 @@
     private void resizeStackWithBoundsFromWindowManager(int stackId, boolean deferResume) {
         final Rect newStackBounds = new Rect();
         final ActivityStack stack = mStackSupervisor.getStack(stackId);
+
+        // TODO(b/71548119): Revert CL introducing below once cause of mismatch is found.
+        if (stack == null) {
+            final StringWriter writer = new StringWriter();
+            final PrintWriter printWriter = new PrintWriter(writer);
+            mStackSupervisor.dumpDisplays(printWriter);
+            printWriter.flush();
+
+            Log.wtf(TAG, "stack not found:" + stackId + " displays:" + writer);
+        }
+
         stack.getBoundsForNewConfiguration(newStackBounds);
         mStackSupervisor.resizeStackLocked(
                 stack, !newStackBounds.isEmpty() ? newStackBounds : null /* bounds */,
@@ -25149,6 +25172,10 @@
         public int getMaxRunningUsers() {
             return mUserController.mMaxRunningUsers;
         }
+
+        public boolean isCallerRecents(int callingUid) {
+            return getRecentTasks().isCallerRecents(callingUid);
+        }
     }
 
     /**
diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java
index f496a67..9d06b0d 100644
--- a/services/core/java/com/android/server/am/ActivityStack.java
+++ b/services/core/java/com/android/server/am/ActivityStack.java
@@ -606,7 +606,7 @@
                         true /* onTop */);
                 recentStack.moveToFront("setWindowingMode");
                 // If task moved to docked stack - show recents if needed.
-                mService.mWindowManager.showRecentApps(false /* fromHome */);
+                mService.mWindowManager.showRecentApps();
             }
             wm.continueSurfaceLayout();
         }
diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
index 8168cba..eb14bbd 100644
--- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
@@ -167,7 +167,7 @@
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.content.ReferrerIntent;
-import com.android.internal.logging.MetricsLogger;
+import com.android.internal.os.logging.MetricsLoggerWrapper;
 import com.android.internal.os.TransferPipe;
 import com.android.internal.util.ArrayUtils;
 import com.android.server.LocalServices;
@@ -2620,8 +2620,7 @@
                 mAllowDockedStackResize = false;
             } else if (inPinnedWindowingMode && onTop) {
                 // Log if we are expanding the PiP to fullscreen
-                MetricsLogger.action(mService.mContext,
-                        ACTION_PICTURE_IN_PICTURE_EXPANDED_TO_FULLSCREEN);
+                MetricsLoggerWrapper.logPictureInPictureFullScreen(mService.mContext);
             }
 
             // If we are moving from the pinned stack, then the animation takes care of updating
@@ -3752,6 +3751,15 @@
         }
     }
 
+    public void dumpDisplays(PrintWriter pw) {
+        for (int i = mActivityDisplays.size() - 1; i >= 0; --i) {
+            final ActivityDisplay display = mActivityDisplays.valueAt(i);
+            pw.print("[id:" + display.mDisplayId + " stacks:");
+            display.dumpStacks(pw);
+            pw.print("]");
+        }
+    }
+
     public void dump(PrintWriter pw, String prefix) {
         pw.print(prefix); pw.print("mFocusedStack=" + mFocusedStack);
                 pw.print(" mLastFocusedStack="); pw.println(mLastFocusedStack);
diff --git a/services/core/java/com/android/server/am/AppErrors.java b/services/core/java/com/android/server/am/AppErrors.java
index 8910274..26d65bc 100644
--- a/services/core/java/com/android/server/am/AppErrors.java
+++ b/services/core/java/com/android/server/am/AppErrors.java
@@ -745,7 +745,7 @@
                     mContext.getContentResolver(),
                     Settings.Secure.SHOW_FIRST_CRASH_DIALOG_DEV_OPTION,
                     0,
-                    UserHandle.USER_CURRENT) != 0;
+                    mService.mUserController.getCurrentUserId()) != 0;
             final boolean crashSilenced = mAppsNotReportingCrashes != null &&
                     mAppsNotReportingCrashes.contains(proc.info.packageName);
             if ((mService.canShowErrorDialogs() || showBackground) && !crashSilenced
diff --git a/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java b/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java
index 1fcaeef..927b72c 100644
--- a/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java
+++ b/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java
@@ -99,7 +99,7 @@
     // Keep the last WiFi stats so we can compute a delta.
     @GuardedBy("mWorkerLock")
     private WifiActivityEnergyInfo mLastInfo =
-            new WifiActivityEnergyInfo(0, 0, 0, new long[]{0}, 0, 0, 0);
+            new WifiActivityEnergyInfo(0, 0, 0, new long[]{0}, 0, 0, 0, 0);
 
     BatteryExternalStatsWorker(Context context, BatteryStatsImpl stats) {
         mContext = context;
@@ -374,6 +374,7 @@
 
     private WifiActivityEnergyInfo extractDeltaLocked(WifiActivityEnergyInfo latest) {
         final long timePeriodMs = latest.mTimestamp - mLastInfo.mTimestamp;
+        final long lastScanMs = mLastInfo.mControllerScanTimeMs;
         final long lastIdleMs = mLastInfo.mControllerIdleTimeMs;
         final long lastTxMs = mLastInfo.mControllerTxTimeMs;
         final long lastRxMs = mLastInfo.mControllerRxTimeMs;
@@ -388,14 +389,16 @@
         final long txTimeMs = latest.mControllerTxTimeMs - lastTxMs;
         final long rxTimeMs = latest.mControllerRxTimeMs - lastRxMs;
         final long idleTimeMs = latest.mControllerIdleTimeMs - lastIdleMs;
+        final long scanTimeMs = latest.mControllerScanTimeMs - lastScanMs;
 
-        if (txTimeMs < 0 || rxTimeMs < 0) {
+        if (txTimeMs < 0 || rxTimeMs < 0 || scanTimeMs < 0) {
             // The stats were reset by the WiFi system (which is why our delta is negative).
             // Returns the unaltered stats.
             delta.mControllerEnergyUsed = latest.mControllerEnergyUsed;
             delta.mControllerRxTimeMs = latest.mControllerRxTimeMs;
             delta.mControllerTxTimeMs = latest.mControllerTxTimeMs;
             delta.mControllerIdleTimeMs = latest.mControllerIdleTimeMs;
+            delta.mControllerScanTimeMs = latest.mControllerScanTimeMs;
             Slog.v(TAG, "WiFi energy data was reset, new WiFi energy data is " + delta);
         } else {
             final long totalActiveTimeMs = txTimeMs + rxTimeMs;
@@ -433,6 +436,7 @@
             // These times seem to be the most reliable.
             delta.mControllerTxTimeMs = txTimeMs;
             delta.mControllerRxTimeMs = rxTimeMs;
+            delta.mControllerScanTimeMs = scanTimeMs;
             // WiFi calculates the idle time as a difference from the on time and the various
             // Rx + Tx times. There seems to be some missing time there because this sometimes
             // becomes negative. Just cap it at 0 and ensure that it is less than the expected idle
diff --git a/services/core/java/com/android/server/am/BatteryStatsService.java b/services/core/java/com/android/server/am/BatteryStatsService.java
index 81e8eb0..04b49ba 100644
--- a/services/core/java/com/android/server/am/BatteryStatsService.java
+++ b/services/core/java/com/android/server/am/BatteryStatsService.java
@@ -40,6 +40,8 @@
 import android.os.WorkSource;
 import android.os.WorkSource.WorkChain;
 import android.os.connectivity.CellularBatteryStats;
+import android.os.connectivity.WifiBatteryStats;
+import android.os.connectivity.GpsBatteryStats;
 import android.os.health.HealthStatsParceler;
 import android.os.health.HealthStatsWriter;
 import android.os.health.UidHealthStats;
@@ -594,6 +596,12 @@
         }
     }
 
+    public void noteGpsSignalQuality(int signalLevel) {
+        synchronized (mStats) {
+            mStats.noteGpsSignalQualityLocked(signalLevel);
+        }
+    }
+
     public void noteScreenState(int state) {
         enforceCallingPermission();
         if (DBG) Slog.d(TAG, "begin noteScreenState");
@@ -1448,6 +1456,26 @@
     }
 
     /**
+     * Gets a snapshot of Wifi stats
+     * @hide
+     */
+    public WifiBatteryStats getWifiBatteryStats() {
+        synchronized (mStats) {
+            return mStats.getWifiBatteryStats();
+        }
+    }
+
+    /**
+     * Gets a snapshot of Gps stats
+     * @hide
+     */
+    public GpsBatteryStats getGpsBatteryStats() {
+        synchronized (mStats) {
+            return mStats.getGpsBatteryStats();
+        }
+    }
+
+    /**
      * Gets a snapshot of the system health for a particular uid.
      */
     @Override
diff --git a/services/core/java/com/android/server/am/GlobalSettingsToPropertiesMapper.java b/services/core/java/com/android/server/am/GlobalSettingsToPropertiesMapper.java
index c9afc17..d6c6f96 100644
--- a/services/core/java/com/android/server/am/GlobalSettingsToPropertiesMapper.java
+++ b/services/core/java/com/android/server/am/GlobalSettingsToPropertiesMapper.java
@@ -23,6 +23,7 @@
 import android.provider.Settings;
 import android.text.TextUtils;
 import android.util.Slog;
+import android.view.ThreadedRenderer;
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.Preconditions;
@@ -39,6 +40,7 @@
     //  List mapping entries in the following format:
     //  {Settings.Global.SETTING_NAME, "system_property_name"},
         {Settings.Global.SYS_VDSO, "sys.vdso"},
+        {Settings.Global.FPS_DEVISOR, ThreadedRenderer.DEBUG_FPS_DIVISOR},
     };
 
 
diff --git a/services/core/java/com/android/server/am/TaskRecord.java b/services/core/java/com/android/server/am/TaskRecord.java
index b131e86..809f19f6 100644
--- a/services/core/java/com/android/server/am/TaskRecord.java
+++ b/services/core/java/com/android/server/am/TaskRecord.java
@@ -749,12 +749,7 @@
         supervisor.handleNonResizableTaskIfNeeded(this, preferredStack.getWindowingMode(),
                 DEFAULT_DISPLAY, toStack);
 
-        boolean successful = (preferredStack == toStack);
-        if (successful && toStack.getWindowingMode() == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY) {
-            // If task moved to docked stack - show recents if needed.
-            mService.mWindowManager.showRecentApps(false /* fromHome */);
-        }
-        return successful;
+        return (preferredStack == toStack);
     }
 
     /**
diff --git a/services/core/java/com/android/server/am/UserController.java b/services/core/java/com/android/server/am/UserController.java
index 1a47aa5..5ada484 100644
--- a/services/core/java/com/android/server/am/UserController.java
+++ b/services/core/java/com/android/server/am/UserController.java
@@ -1431,7 +1431,13 @@
 
         if (callingUid != 0 && callingUid != SYSTEM_UID) {
             final boolean allow;
-            if (mInjector.checkComponentPermission(INTERACT_ACROSS_USERS_FULL, callingPid,
+            if (mInjector.isCallerRecents(callingUid)
+                    && callingUserId == getCurrentUserId()
+                    && isSameProfileGroup(callingUserId, targetUserId)) {
+                // If the caller is Recents and it is running in the current user, we then allow it
+                // to access its profiles.
+                allow = true;
+            } else if (mInjector.checkComponentPermission(INTERACT_ACROSS_USERS_FULL, callingPid,
                     callingUid, -1, true) == PackageManager.PERMISSION_GRANTED) {
                 // If the caller has this permission, they always pass go.  And collect $200.
                 allow = true;
@@ -2149,5 +2155,9 @@
                 mService.mLockTaskController.clearLockedTasks(reason);
             }
         }
+
+        protected boolean isCallerRecents(int callingUid) {
+            return mService.getRecentTasks().isCallerRecents(callingUid);
+        }
     }
 }
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index 799f2a9..a714720 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -1047,9 +1047,11 @@
 
     private void checkMuteAffectedStreams() {
         // any stream with a min level > 0 is not muteable by definition
+        // STREAM_VOICE_CALL can be muted by applications that has the the MODIFY_PHONE_STATE permission.
         for (int i = 0; i < mStreamStates.length; i++) {
             final VolumeStreamState vss = mStreamStates[i];
-            if (vss.mIndexMin > 0) {
+            if (vss.mIndexMin > 0 &&
+                vss.mStreamType != AudioSystem.STREAM_VOICE_CALL) {
                 mMuteAffectedStreams &= ~(1 << vss.mStreamType);
             }
         }
@@ -1412,6 +1414,18 @@
             return;
         }
 
+        // If adjust is mute and the stream is STREAM_VOICE_CALL, make sure
+        // that the calling app have the MODIFY_PHONE_STATE permission.
+        if (isMuteAdjust &&
+            streamType == AudioSystem.STREAM_VOICE_CALL &&
+            mContext.checkCallingOrSelfPermission(
+                android.Manifest.permission.MODIFY_PHONE_STATE)
+                    != PackageManager.PERMISSION_GRANTED) {
+            Log.w(TAG, "MODIFY_PHONE_STATE Permission Denial: adjustStreamVolume from pid="
+                    + Binder.getCallingPid() + ", uid=" + Binder.getCallingUid());
+            return;
+        }
+
         // use stream type alias here so that streams with same alias have the same behavior,
         // including with regard to silent mode control (e.g the use of STREAM_RING below and in
         // checkForRingerModeChange() in place of STREAM_RING or STREAM_NOTIFICATION)
@@ -1712,6 +1726,15 @@
                     + " CHANGE_ACCESSIBILITY_VOLUME  callingPackage=" + callingPackage);
             return;
         }
+        if ((streamType == AudioManager.STREAM_VOICE_CALL) &&
+                (index == 0) &&
+                (mContext.checkCallingOrSelfPermission(
+                android.Manifest.permission.MODIFY_PHONE_STATE)
+                    != PackageManager.PERMISSION_GRANTED)) {
+            Log.w(TAG, "Trying to call setStreamVolume() for STREAM_VOICE_CALL and index 0 without"
+                    + " MODIFY_PHONE_STATE  callingPackage=" + callingPackage);
+            return;
+        }
         mVolumeLogger.log(new VolumeEvent(VolumeEvent.VOL_SET_STREAM_VOL, streamType,
                 index/*val1*/, flags/*val2*/, callingPackage));
         setStreamVolume(streamType, index, flags, callingPackage, callingPackage,
@@ -4132,22 +4155,30 @@
 
     public int setBluetoothA2dpDeviceConnectionState(BluetoothDevice device, int state, int profile)
     {
+        return setBluetoothA2dpDeviceConnectionStateSuppressNoisyIntent(
+                device, state, profile, false /* suppressNoisyIntent */);
+    }
+
+    public int setBluetoothA2dpDeviceConnectionStateSuppressNoisyIntent(BluetoothDevice device,
+                int state, int profile, boolean suppressNoisyIntent)
+    {
         if (mAudioHandler.hasMessages(MSG_SET_A2DP_SINK_CONNECTION_STATE, device)) {
             return 0;
         }
         return setBluetoothA2dpDeviceConnectionStateInt(
-                device, state, profile, AudioSystem.DEVICE_NONE);
+                device, state, profile, suppressNoisyIntent, AudioSystem.DEVICE_NONE);
     }
 
     public int setBluetoothA2dpDeviceConnectionStateInt(
-            BluetoothDevice device, int state, int profile, int musicDevice)
+            BluetoothDevice device, int state, int profile, boolean suppressNoisyIntent,
+            int musicDevice)
     {
         int delay;
         if (profile != BluetoothProfile.A2DP && profile != BluetoothProfile.A2DP_SINK) {
             throw new IllegalArgumentException("invalid profile " + profile);
         }
         synchronized (mConnectedDevices) {
-            if (profile == BluetoothProfile.A2DP) {
+            if (profile == BluetoothProfile.A2DP && !suppressNoisyIntent) {
                 int intState = (state == BluetoothA2dp.STATE_CONNECTED) ? 1 : 0;
                 delay = checkSendBecomingNoisyIntent(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP,
                         intState, musicDevice);
@@ -4503,27 +4534,30 @@
             if (mStreamType == srcStream.mStreamType) {
                 return;
             }
-            synchronized (VolumeStreamState.class) {
-                int srcStreamType = srcStream.getStreamType();
-                // apply default device volume from source stream to all devices first in case
-                // some devices are present in this stream state but not in source stream state
-                int index = srcStream.getIndex(AudioSystem.DEVICE_OUT_DEFAULT);
-                index = rescaleIndex(index, srcStreamType, mStreamType);
-                for (int i = 0; i < mIndexMap.size(); i++) {
-                    mIndexMap.put(mIndexMap.keyAt(i), index);
-                }
-                // Now apply actual volume for devices in source stream state
-                SparseIntArray srcMap = srcStream.mIndexMap;
-                for (int i = 0; i < srcMap.size(); i++) {
-                    int device = srcMap.keyAt(i);
-                    index = srcMap.valueAt(i);
+            synchronized (mSettingsLock) {
+                synchronized (VolumeStreamState.class) {
+                    int srcStreamType = srcStream.getStreamType();
+                    // apply default device volume from source stream to all devices first in case
+                    // some devices are present in this stream state but not in source stream state
+                    int index = srcStream.getIndex(AudioSystem.DEVICE_OUT_DEFAULT);
                     index = rescaleIndex(index, srcStreamType, mStreamType);
+                    for (int i = 0; i < mIndexMap.size(); i++) {
+                        mIndexMap.put(mIndexMap.keyAt(i), index);
+                    }
+                    // Now apply actual volume for devices in source stream state
+                    SparseIntArray srcMap = srcStream.mIndexMap;
+                    for (int i = 0; i < srcMap.size(); i++) {
+                        int device = srcMap.keyAt(i);
+                        index = srcMap.valueAt(i);
+                        index = rescaleIndex(index, srcStreamType, mStreamType);
 
-                    setIndex(index, device, caller);
+                        setIndex(index, device, caller);
+                    }
                 }
             }
         }
 
+        @GuardedBy("mSettingsLock")
         public void setAllIndexesToMax() {
             synchronized (VolumeStreamState.class) {
                 for (int i = 0; i < mIndexMap.size(); i++) {
@@ -5397,7 +5431,7 @@
                    // consistent with audio policy manager state
                    setBluetoothA2dpDeviceConnectionStateInt(
                            btDevice, BluetoothA2dp.STATE_DISCONNECTED, BluetoothProfile.A2DP,
-                           musicDevice);
+                           false /* suppressNoisyIntent */, musicDevice);
                }
             }
         }
diff --git a/services/core/java/com/android/server/broadcastradio/hal2/TunerSession.java b/services/core/java/com/android/server/broadcastradio/hal2/TunerSession.java
index e093c9d..1ae7d20 100644
--- a/services/core/java/com/android/server/broadcastradio/hal2/TunerSession.java
+++ b/services/core/java/com/android/server/broadcastradio/hal2/TunerSession.java
@@ -222,7 +222,7 @@
             MutableInt halResult = new MutableInt(Result.UNKNOWN_ERROR);
             MutableBoolean flagState = new MutableBoolean(false);
             try {
-                mHwSession.getConfigFlag(flag, (int result, boolean value) -> {
+                mHwSession.isConfigFlagSet(flag, (int result, boolean value) -> {
                     halResult.value = result;
                     flagState.value = value;
                 });
diff --git a/services/core/java/com/android/server/job/JobSchedulerService.java b/services/core/java/com/android/server/job/JobSchedulerService.java
index bd1dbf9..fa5fdf5 100644
--- a/services/core/java/com/android/server/job/JobSchedulerService.java
+++ b/services/core/java/com/android/server/job/JobSchedulerService.java
@@ -63,6 +63,7 @@
 import android.util.Slog;
 import android.util.SparseArray;
 import android.util.SparseIntArray;
+import android.util.StatsLog;
 import android.util.TimeUtils;
 import android.util.proto.ProtoOutputStream;
 
@@ -823,6 +824,8 @@
                 jobStatus.enqueueWorkLocked(ActivityManager.getService(), work);
             }
             startTrackingJobLocked(jobStatus, toCancel);
+            StatsLog.write_non_chained(StatsLog.SCHEDULED_JOB_STATE_CHANGED,
+                    uId, null, jobStatus.getBatteryName(), 2);
 
             // If the job is immediately ready to run, then we can just immediately
             // put it in the pending list and try to schedule it.  This is especially
diff --git a/services/core/java/com/android/server/location/GnssLocationProvider.java b/services/core/java/com/android/server/location/GnssLocationProvider.java
index 6dc5403..48d275c 100644
--- a/services/core/java/com/android/server/location/GnssLocationProvider.java
+++ b/services/core/java/com/android/server/location/GnssLocationProvider.java
@@ -827,7 +827,7 @@
                 return isEnabled();
             }
         };
-        mGnssMetrics = new GnssMetrics();
+        mGnssMetrics = new GnssMetrics(mBatteryStats);
     }
 
     /**
diff --git a/services/core/java/com/android/server/locksettings/recoverablekeystore/KeySyncTask.java b/services/core/java/com/android/server/locksettings/recoverablekeystore/KeySyncTask.java
index 724073a..452c9ee 100644
--- a/services/core/java/com/android/server/locksettings/recoverablekeystore/KeySyncTask.java
+++ b/services/core/java/com/android/server/locksettings/recoverablekeystore/KeySyncTask.java
@@ -36,6 +36,7 @@
 import java.nio.ByteOrder;
 import java.nio.charset.StandardCharsets;
 import java.security.GeneralSecurityException;
+import java.security.InvalidAlgorithmParameterException;
 import java.security.InvalidKeyException;
 import java.security.KeyStoreException;
 import java.security.MessageDigest;
@@ -301,7 +302,8 @@
      */
     private Map<String, SecretKey> getKeysToSync(int recoveryAgentUid)
             throws InsecureUserException, KeyStoreException, UnrecoverableKeyException,
-            NoSuchAlgorithmException, NoSuchPaddingException, BadPlatformKeyException {
+            NoSuchAlgorithmException, NoSuchPaddingException, BadPlatformKeyException,
+            InvalidKeyException, InvalidAlgorithmParameterException {
         PlatformKeyManager platformKeyManager = mPlatformKeyManagerFactory.newInstance();
         PlatformDecryptionKey decryptKey = platformKeyManager.getDecryptKey(mUserId);
         Map<String, WrappedKey> wrappedKeys = mRecoverableKeyStoreDb.getAllKeys(
diff --git a/services/core/java/com/android/server/locksettings/recoverablekeystore/WrappedKey.java b/services/core/java/com/android/server/locksettings/recoverablekeystore/WrappedKey.java
index c33c9de..d85e89e 100644
--- a/services/core/java/com/android/server/locksettings/recoverablekeystore/WrappedKey.java
+++ b/services/core/java/com/android/server/locksettings/recoverablekeystore/WrappedKey.java
@@ -184,7 +184,8 @@
     public static Map<String, SecretKey> unwrapKeys(
             PlatformDecryptionKey platformKey,
             Map<String, WrappedKey> wrappedKeys)
-            throws NoSuchAlgorithmException, NoSuchPaddingException, BadPlatformKeyException {
+            throws NoSuchAlgorithmException, NoSuchPaddingException, BadPlatformKeyException,
+            InvalidKeyException, InvalidAlgorithmParameterException {
         HashMap<String, SecretKey> unwrappedKeys = new HashMap<>();
         Cipher cipher = Cipher.getInstance(KEY_WRAP_CIPHER_ALGORITHM);
         int platformKeyGenerationId = platformKey.getGenerationId();
@@ -201,20 +202,10 @@
                         platformKey.getGenerationId()));
             }
 
-            try {
-                cipher.init(
-                        Cipher.UNWRAP_MODE,
-                        platformKey.getKey(),
-                        new GCMParameterSpec(GCM_TAG_LENGTH_BITS, wrappedKey.getNonce()));
-            } catch (InvalidKeyException | InvalidAlgorithmParameterException e) {
-                Log.e(TAG,
-                        String.format(
-                                Locale.US,
-                                "Could not init Cipher to unwrap recoverable key with alias '%s'",
-                                alias),
-                        e);
-                continue;
-            }
+            cipher.init(
+                    Cipher.UNWRAP_MODE,
+                    platformKey.getKey(),
+                    new GCMParameterSpec(GCM_TAG_LENGTH_BITS, wrappedKey.getNonce()));
             SecretKey key;
             try {
                 key = (SecretKey) cipher.unwrap(
diff --git a/services/core/java/com/android/server/media/MediaSession2Record.java b/services/core/java/com/android/server/media/MediaSession2Record.java
new file mode 100644
index 0000000..b25eaa7
--- /dev/null
+++ b/services/core/java/com/android/server/media/MediaSession2Record.java
@@ -0,0 +1,181 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.media;
+
+import android.annotation.CallSuper;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.Context;
+import android.media.IMediaSession2;
+import android.media.MediaController2;
+import android.media.MediaSession2;
+import android.media.SessionToken;
+import android.os.Handler;
+import android.os.Looper;
+import android.util.Log;
+import java.util.concurrent.Executor;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+/**
+ * Records a {@link MediaSession2} and holds {@link MediaController2}.
+ * <p>
+ * Owner of this object should handle synchronization.
+ */
+class MediaSession2Record {
+    interface SessionDestroyedListener {
+        void onSessionDestroyed(MediaSession2Record record);
+    }
+
+    private static final String TAG = "Session2Record";
+    private static final boolean DEBUG = true; // TODO(jaewan): Change
+
+    private final Context mContext;
+    private final SessionDestroyedListener mSessionDestroyedListener;
+
+    // TODO(jaewan): Replace these with the mContext.getMainExecutor()
+    private final Handler mMainHandler;
+    private final Executor mMainExecutor;
+
+    private MediaController2 mController;
+    private ControllerCallback mControllerCallback;
+
+    private int mSessionPid;
+
+    /**
+     * Constructor
+     */
+    public MediaSession2Record(@NonNull Context context,
+            @NonNull SessionDestroyedListener listener) {
+        mContext = context;
+        mSessionDestroyedListener = listener;
+
+        mMainHandler = new Handler(Looper.getMainLooper());
+        mMainExecutor = (runnable) -> {
+            mMainHandler.post(runnable);
+        };
+    }
+
+    public int getSessionPid() {
+        return mSessionPid;
+    }
+
+    public Context getContext() {
+        return mContext;
+    }
+
+    @CallSuper
+    public void onSessionDestroyed() {
+        if (mController != null) {
+            mControllerCallback.destroy();
+            mController.release();
+            mController = null;
+        }
+        mSessionPid = 0;
+    }
+
+    /**
+     * Create session token and tell server that session is now active.
+     *
+     * @param sessionPid session's pid
+     * @return a token if successfully set, {@code null} if sanity check fails.
+     */
+    // TODO(jaewan): also add uid for multiuser support
+    @CallSuper
+    public @Nullable
+    SessionToken createSessionToken(int sessionPid, String packageName, String id,
+            IMediaSession2 sessionBinder) {
+        if (mController != null) {
+            if (mSessionPid != sessionPid) {
+                // A package uses the same id for session across the different process.
+                return null;
+            }
+            // If a session becomes inactive and then active again very quickly, previous 'inactive'
+            // may not have delivered yet. Check if it's the case and destroy controller before
+            // creating its session record to prevents getXXTokens() API from returning duplicated
+            // tokens.
+            // TODO(jaewan): Change this. If developer is really creating two sessions with the same
+            //               id, this will silently invalidate previous session and no way for
+            //               developers to know that.
+            //               Instead, keep the list of static session ids from our APIs.
+            //               Also change Controller2Impl.onConnectionChanged / getController.
+            //               Also clean up ControllerCallback#destroy().
+            if (DEBUG) {
+                Log.d(TAG, "Session is recreated almost immediately. " + this);
+            }
+            onSessionDestroyed();
+        }
+        mController = onCreateMediaController(packageName, id, sessionBinder);
+        mSessionPid = sessionPid;
+        return mController.getSessionToken();
+    }
+
+    /**
+     * Called when session becomes active and needs controller to listen session's activeness.
+     * <p>
+     * Should be overridden by subclasses to create token with its own extra information.
+     */
+    MediaController2 onCreateMediaController(
+            String packageName, String id, IMediaSession2 sessionBinder) {
+        SessionToken token = new SessionToken(
+                SessionToken.TYPE_SESSION, packageName, id, null, sessionBinder);
+        return createMediaController(token);
+    }
+
+    final MediaController2 createMediaController(SessionToken token) {
+        mControllerCallback = new ControllerCallback();
+        return new MediaController2(mContext, token, mControllerCallback, mMainExecutor);
+    }
+
+    /**
+     * @return controller. Note that framework can only call oneway calls.
+     */
+    public SessionToken getToken() {
+        return mController == null ? null : mController.getSessionToken();
+    }
+
+    @Override
+    public String toString() {
+        return getToken() == null
+                ? "Token {null}"
+                : "SessionRecord {pid=" + mSessionPid + ", " + getToken().toString() + "}";
+    }
+
+    private class ControllerCallback extends MediaController2.ControllerCallback {
+        private final AtomicBoolean mIsActive = new AtomicBoolean(true);
+
+        // This is called on the main thread with no lock. So place ensure followings.
+        //   1. Don't touch anything in the parent class that needs synchronization.
+        //      All other APIs in the MediaSession2Record assumes that server would use them with
+        //      the lock hold.
+        //   2. This can be called after the controller registered is released.
+        @Override
+        public void onDisconnected() {
+            if (!mIsActive.get()) {
+                return;
+            }
+            if (DEBUG) {
+                Log.d(TAG, "onDisconnected, token=" + getToken());
+            }
+            mSessionDestroyedListener.onSessionDestroyed(MediaSession2Record.this);
+        }
+
+        // TODO(jaewan): Remove this API when we revisit createSessionToken()
+        public void destroy() {
+            mIsActive.set(false);
+        }
+    };
+}
diff --git a/services/core/java/com/android/server/media/MediaSessionService.java b/services/core/java/com/android/server/media/MediaSessionService.java
index 06f4f5e..6812778 100644
--- a/services/core/java/com/android/server/media/MediaSessionService.java
+++ b/services/core/java/com/android/server/media/MediaSessionService.java
@@ -28,13 +28,18 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.content.pm.ServiceInfo;
 import android.content.pm.UserInfo;
 import android.database.ContentObserver;
 import android.media.AudioManager;
 import android.media.AudioPlaybackConfiguration;
 import android.media.AudioSystem;
 import android.media.IAudioService;
+import android.media.IMediaSession2;
 import android.media.IRemoteVolumeController;
+import android.media.MediaSessionService2;
+import android.media.SessionToken;
 import android.media.session.IActiveSessionsListener;
 import android.media.session.ICallback;
 import android.media.session.IOnMediaKeyListener;
@@ -118,6 +123,24 @@
     // better way to handle this.
     private IRemoteVolumeController mRvc;
 
+    // MediaSession2 support
+    // TODO(jaewan): Support multi-user and managed profile.
+    // TODO(jaewan): Make it priority list for handling volume/media key.
+    private final List<MediaSession2Record> mSessions = new ArrayList<>();
+
+    private final MediaSession2Record.SessionDestroyedListener mSessionDestroyedListener =
+            (MediaSession2Record record) -> {
+                synchronized (mLock) {
+                    if (DEBUG) {
+                        Log.d(TAG, record.toString() + " becomes inactive");
+                    }
+                    record.onSessionDestroyed();
+                    if (!(record instanceof MediaSessionService2Record)) {
+                        mSessions.remove(record);
+                    }
+                }
+            };
+
     public MediaSessionService(Context context) {
         super(context);
         mSessionManagerImpl = new SessionManagerImpl();
@@ -158,6 +181,11 @@
                 PackageManager.FEATURE_LEANBACK);
 
         updateUser();
+
+        // TODO(jaewan): Query per users
+        // TODO(jaewan): Add listener to know changes in list of services.
+        //               Refer TvInputManagerService.registerBroadcastReceivers()
+        buildMediaSessionService2List();
     }
 
     private IAudioService getAudioService() {
@@ -411,6 +439,64 @@
         mHandler.postSessionsChanged(session.getUserId());
     }
 
+    private void buildMediaSessionService2List() {
+        if (DEBUG) {
+            Log.d(TAG, "buildMediaSessionService2List");
+        }
+
+        // TODO(jaewan): Query per users.
+        List<ResolveInfo> services = getContext().getPackageManager().queryIntentServices(
+                new Intent(MediaSessionService2.SERVICE_INTERFACE),
+                PackageManager.GET_META_DATA);
+        synchronized (mLock) {
+            mSessions.clear();
+            if (services == null) {
+                return;
+            }
+            for (int i = 0; i < services.size(); i++) {
+                if (services.get(i) == null || services.get(i).serviceInfo == null) {
+                    continue;
+                }
+                ServiceInfo serviceInfo = services.get(i).serviceInfo;
+                String id = (serviceInfo.metaData != null) ? serviceInfo.metaData.getString(
+                        MediaSessionService2.SERVICE_META_DATA) : null;
+                // Do basic sanity check
+                // TODO(jaewan): also santity check if it's protected with the system|privileged
+                //               permission
+                boolean conflict = (getSessionRecordLocked(serviceInfo.name, id) != null);
+                if (conflict) {
+                    Log.w(TAG, serviceInfo.packageName + " contains multiple"
+                            + " MediaSessionService2s declared in the manifest with"
+                            + " the same ID=" + id + ". Ignoring "
+                            + serviceInfo.packageName + "/" + serviceInfo.name);
+                } else {
+                    MediaSessionService2Record record =
+                            new MediaSessionService2Record(getContext(), mSessionDestroyedListener,
+                                    SessionToken.TYPE_SESSION_SERVICE,
+                                    serviceInfo.packageName, serviceInfo.name, id);
+                    mSessions.add(record);
+                }
+            }
+        }
+        if (DEBUG) {
+            Log.d(TAG, "Found " + mSessions.size() + " session services");
+            for (int i = 0; i < mSessions.size(); i++) {
+                Log.d(TAG, "   " + mSessions.get(i).getToken());
+            }
+        }
+    }
+
+    MediaSession2Record getSessionRecordLocked(String packageName, String id) {
+        for (int i = 0; i < mSessions.size(); i++) {
+            MediaSession2Record record = mSessions.get(i);
+            if (record.getToken().getPackageName().equals(packageName)
+                    && record.getToken().getId().equals(id)) {
+                return record;
+            }
+        }
+        return null;
+    }
+
     private void enforcePackageName(String packageName, int uid) {
         if (TextUtils.isEmpty(packageName)) {
             throw new IllegalArgumentException("packageName may not be empty");
@@ -1312,6 +1398,57 @@
             }
         }
 
+        @Override
+        public Bundle createSessionToken(String sessionPackage, String id,
+                IMediaSession2 sessionBinder) throws RemoteException {
+            int uid = Binder.getCallingUid();
+            int pid = Binder.getCallingPid();
+
+            MediaSession2Record record;
+            SessionToken token;
+            // TODO(jaewan): Add sanity check for the token if calling package is from uid.
+            synchronized (mLock) {
+                record = getSessionRecordLocked(sessionPackage, id);
+                if (record == null) {
+                    record = new MediaSession2Record(getContext(), mSessionDestroyedListener);
+                    mSessions.add(record);
+                }
+                token = record.createSessionToken(pid, sessionPackage, id, sessionBinder);
+                if (token == null) {
+                    Log.d(TAG, "failed to create session token for " + sessionPackage
+                            + " from pid=" + pid + ". Previously " + record);
+                } else {
+                    Log.d(TAG, "session " + token + " is created");
+                }
+            }
+            return token == null ? null : token.toBundle();
+        }
+
+        // TODO(jaewan): Protect this API with permission
+        // TODO(jaewan): Add listeners for change in operations..
+        @Override
+        public List<Bundle> getSessionTokens(boolean activeSessionOnly,
+                boolean sessionServiceOnly) throws RemoteException {
+            List<Bundle> tokens = new ArrayList<>();
+            synchronized (mLock) {
+                for (int i = 0; i < mSessions.size(); i++) {
+                    MediaSession2Record record = mSessions.get(i);
+                    boolean isSessionService = (record instanceof MediaSessionService2Record);
+                    boolean isActive = record.getSessionPid() != 0;
+                    if ((!activeSessionOnly && isSessionService)
+                            || (!sessionServiceOnly && isActive)) {
+                        SessionToken token = record.getToken();
+                        if (token != null) {
+                            tokens.add(token.toBundle());
+                        } else {
+                            Log.wtf(TAG, "Null token for record=" + record);
+                        }
+                    }
+                }
+            }
+            return tokens;
+        }
+
         private int verifySessionsRequest(ComponentName componentName, int userId, final int pid,
                 final int uid) {
             String packageName = null;
diff --git a/services/core/java/com/android/server/media/MediaSessionService2Record.java b/services/core/java/com/android/server/media/MediaSessionService2Record.java
new file mode 100644
index 0000000..bd97dbc
--- /dev/null
+++ b/services/core/java/com/android/server/media/MediaSessionService2Record.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.media;
+
+import android.content.Context;
+import android.media.IMediaSession2;
+import android.media.MediaController2;
+import android.media.SessionToken;
+import android.media.MediaSessionService2;
+
+/**
+ * Records a {@link MediaSessionService2}.
+ * <p>
+ * Owner of this object should handle synchronization.
+ */
+class MediaSessionService2Record extends MediaSession2Record {
+    private static final boolean DEBUG = true; // TODO(jaewan): Modify
+    private static final String TAG = "SessionService2Record";
+
+    private final int mType;
+    private final String mServiceName;
+    private final SessionToken mToken;
+
+    public MediaSessionService2Record(Context context,
+            SessionDestroyedListener sessionDestroyedListener, int type,
+            String packageName, String serviceName, String id) {
+        super(context, sessionDestroyedListener);
+        mType = type;
+        mServiceName = serviceName;
+        mToken = new SessionToken(mType, packageName, id, mServiceName, null);
+    }
+
+    /**
+     * Overriden to change behavior of
+     * {@link #createSessionToken(int, String, String, IMediaSession2)}}.
+     */
+    @Override
+    MediaController2 onCreateMediaController(
+            String packageName, String id, IMediaSession2 sessionBinder) {
+        SessionToken token = new SessionToken(mType, packageName, id, mServiceName, sessionBinder);
+        return createMediaController(token);
+    }
+
+    /**
+     * @return token with no session binder information.
+     */
+    @Override
+    public SessionToken getToken() {
+        return mToken;
+    }
+}
diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerInternal.java b/services/core/java/com/android/server/net/NetworkPolicyManagerInternal.java
index e458f48..971ac8b 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyManagerInternal.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyManagerInternal.java
@@ -66,4 +66,9 @@
      * given network.
      */
     public abstract long getSubscriptionOpportunisticQuota(Network network, int quotaType);
+
+    /**
+     * Informs that admin data is loaded and available.
+     */
+    public abstract void onAdminDataAvailable();
 }
diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
index 96b06c2..e406d51 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
@@ -199,6 +199,7 @@
 import com.android.internal.notification.SystemNotificationChannels;
 import com.android.internal.telephony.PhoneConstants;
 import com.android.internal.util.ArrayUtils;
+import com.android.internal.util.ConcurrentUtils;
 import com.android.internal.util.DumpUtils;
 import com.android.internal.util.FastXmlSerializer;
 import com.android.internal.util.IndentingPrintWriter;
@@ -333,6 +334,11 @@
 
     private static final long TIME_CACHE_MAX_AGE = DAY_IN_MILLIS;
 
+    /**
+     * Indicates the maximum wait time for admin data to be available;
+     */
+    private static final long WAIT_FOR_ADMIN_DATA_TIMEOUT_MS = 10_000;
+
     private static final int MSG_RULES_CHANGED = 1;
     private static final int MSG_METERED_IFACES_CHANGED = 2;
     private static final int MSG_LIMIT_REACHED = 5;
@@ -384,6 +390,8 @@
 
     private final boolean mSuppressDefaultPolicy;
 
+    private final CountDownLatch mAdminDataAvailableLatch = new CountDownLatch(1);
+
     /** Defined network policies. */
     @GuardedBy("mNetworkPoliciesSecondLock")
     final ArrayMap<NetworkTemplate, NetworkPolicy> mNetworkPolicy = new ArrayMap<>();
@@ -672,6 +680,8 @@
 
                     mSystemReady = true;
 
+                    waitForAdminData();
+
                     // read policy from disk
                     readPolicyAL();
 
@@ -4590,6 +4600,11 @@
                 return mSubscriptionOpportunisticQuota.get(getSubIdLocked(network));
             }
         }
+
+        @Override
+        public void onAdminDataAvailable() {
+            mAdminDataAvailableLatch.countDown();
+        }
     }
 
     private int parseSubId(NetworkState state) {
@@ -4617,6 +4632,16 @@
         return ArrayUtils.isEmpty(plans) ? null : plans[0];
     }
 
+    /**
+     * This will only ever be called once - during device boot.
+     */
+    private void waitForAdminData() {
+        if (mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_DEVICE_ADMIN)) {
+            ConcurrentUtils.waitForCountDownNoInterrupt(mAdminDataAvailableLatch,
+                    WAIT_FOR_ADMIN_DATA_TIMEOUT_MS, "Wait for admin data");
+        }
+    }
+
     private static boolean hasRule(int uidRules, int rule) {
         return (uidRules & rule) != 0;
     }
diff --git a/services/core/java/com/android/server/net/watchlist/NetworkWatchlistService.java b/services/core/java/com/android/server/net/watchlist/NetworkWatchlistService.java
index 239ddbe..7165e60 100644
--- a/services/core/java/com/android/server/net/watchlist/NetworkWatchlistService.java
+++ b/services/core/java/com/android/server/net/watchlist/NetworkWatchlistService.java
@@ -16,6 +16,7 @@
 
 package com.android.server.net.watchlist;
 
+import android.annotation.Nullable;
 import android.content.Context;
 import android.net.IIpConnectivityMetrics;
 import android.net.INetdEventCallback;
@@ -210,6 +211,12 @@
         return stopWatchlistLoggingImpl();
     }
 
+    @Nullable
+    @Override
+    public byte[] getWatchlistConfigHash() {
+        return mConfig.getWatchlistConfigHash();
+    }
+
     private void enforceWatchlistLoggingPermission() {
         final int uid = Binder.getCallingUid();
         if (uid != Process.SYSTEM_UID) {
diff --git a/services/core/java/com/android/server/net/watchlist/WatchlistConfig.java b/services/core/java/com/android/server/net/watchlist/WatchlistConfig.java
index fbf11fc..7387ad4 100644
--- a/services/core/java/com/android/server/net/watchlist/WatchlistConfig.java
+++ b/services/core/java/com/android/server/net/watchlist/WatchlistConfig.java
@@ -161,7 +161,7 @@
     public boolean containsDomain(String domain) {
         final CrcShaDigests domainDigests = mDomainDigests;
         if (domainDigests == null) {
-            Slog.wtf(TAG, "domainDigests should not be null");
+            // mDomainDigests is not initialized
             return false;
         }
         // First it does a quick CRC32 check.
@@ -177,7 +177,7 @@
     public boolean containsIp(String ip) {
         final CrcShaDigests ipDigests = mIpDigests;
         if (ipDigests == null) {
-            Slog.wtf(TAG, "ipDigests should not be null");
+            // mIpDigests is not initialized
             return false;
         }
         // First it does a quick CRC32 check.
@@ -234,12 +234,20 @@
 
     public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
         pw.println("Domain CRC32 digest list:");
-        mDomainDigests.crc32Digests.dump(fd, pw, args);
+        if (mDomainDigests != null) {
+            mDomainDigests.crc32Digests.dump(fd, pw, args);
+        }
         pw.println("Domain SHA256 digest list:");
-        mDomainDigests.sha256Digests.dump(fd, pw, args);
+        if (mDomainDigests != null) {
+            mDomainDigests.sha256Digests.dump(fd, pw, args);
+        }
         pw.println("Ip CRC32 digest list:");
-        mIpDigests.crc32Digests.dump(fd, pw, args);
+        if (mIpDigests != null) {
+            mIpDigests.crc32Digests.dump(fd, pw, args);
+        }
         pw.println("Ip SHA256 digest list:");
-        mIpDigests.sha256Digests.dump(fd, pw, args);
+        if (mIpDigests != null) {
+            mIpDigests.sha256Digests.dump(fd, pw, args);
+        }
     }
 }
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 2f6618f..39b7c7c 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -137,6 +137,7 @@
 import android.service.notification.NotificationRecordProto;
 import android.service.notification.NotificationServiceDumpProto;
 import android.service.notification.NotificationStats;
+import android.service.notification.NotifyingApp;
 import android.service.notification.SnoozeCriterion;
 import android.service.notification.StatusBarNotification;
 import android.service.notification.ZenModeConfig;
@@ -329,6 +330,7 @@
     final ArrayMap<Integer, ArrayMap<String, String>> mAutobundledSummaries = new ArrayMap<>();
     final ArrayList<ToastRecord> mToastQueue = new ArrayList<>();
     final ArrayMap<String, NotificationRecord> mSummaryByGroupKey = new ArrayMap<>();
+    final ArrayMap<Integer, ArrayList<NotifyingApp>> mRecentApps = new ArrayMap<>();
 
     // The last key in this list owns the hardware.
     ArrayList<String> mLights = new ArrayList<>();
@@ -2110,6 +2112,16 @@
         }
 
         @Override
+        public ParceledListSlice<NotifyingApp> getRecentNotifyingAppsForUser(int userId) {
+            checkCallerIsSystem();
+            synchronized (mNotificationLock) {
+                List<NotifyingApp> apps = new ArrayList<>(
+                        mRecentApps.getOrDefault(userId, new ArrayList<>()));
+                return new ParceledListSlice<>(apps);
+            }
+        }
+
+        @Override
         public void clearData(String packageName, int uid, boolean fromApp) throws RemoteException {
             checkCallerIsSystem();
 
@@ -4096,6 +4108,10 @@
 
                     mNotificationsByKey.put(n.getKey(), r);
 
+                    if (!r.isUpdate) {
+                        logRecentLocked(r);
+                    }
+
                     // Ensure if this is a foreground service that the proper additional
                     // flags are set.
                     if ((notification.flags & Notification.FLAG_FOREGROUND_SERVICE) != 0) {
@@ -4153,6 +4169,38 @@
     }
 
     /**
+     * Keeps the last 5 packages that have notified, by user.
+     */
+    @GuardedBy("mNotificationLock")
+    @VisibleForTesting
+    protected void logRecentLocked(NotificationRecord r) {
+        if (r.isUpdate) {
+            return;
+        }
+        ArrayList<NotifyingApp> recentAppsForUser =
+                mRecentApps.getOrDefault(r.getUser().getIdentifier(), new ArrayList<>(6));
+        NotifyingApp na = new NotifyingApp()
+                .setPackage(r.sbn.getPackageName())
+                .setUid(r.sbn.getUid())
+                .setLastNotified(r.sbn.getPostTime());
+        // A new notification gets an app moved to the front of the list
+        for (int i = recentAppsForUser.size() - 1; i >= 0; i--) {
+            NotifyingApp naExisting = recentAppsForUser.get(i);
+            if (na.getPackage().equals(naExisting.getPackage())
+                    && na.getUid() == naExisting.getUid()) {
+                recentAppsForUser.remove(i);
+                break;
+            }
+        }
+        // time is always increasing, so always add to the front of the list
+        recentAppsForUser.add(0, na);
+        if (recentAppsForUser.size() > 5) {
+            recentAppsForUser.remove(recentAppsForUser.size() -1);
+        }
+        mRecentApps.put(r.getUser().getIdentifier(), recentAppsForUser);
+    }
+
+    /**
      * Ensures that grouped notification receive their special treatment.
      *
      * <p>Cancels group children if the new notification causes a group to lose
diff --git a/services/core/java/com/android/server/oemlock/OemLockService.java b/services/core/java/com/android/server/oemlock/OemLockService.java
index 2a2ff06..a6200bf 100644
--- a/services/core/java/com/android/server/oemlock/OemLockService.java
+++ b/services/core/java/com/android/server/oemlock/OemLockService.java
@@ -31,10 +31,10 @@
 import android.os.UserManagerInternal;
 import android.os.UserManagerInternal.UserRestrictionsListener;
 import android.service.oemlock.IOemLockService;
-import android.service.persistentdata.PersistentDataBlockManager;
 import android.util.Slog;
 
 import com.android.server.LocalServices;
+import com.android.server.PersistentDataBlockManagerInternal;
 import com.android.server.SystemService;
 import com.android.server.pm.UserRestrictionsUtils;
 
@@ -217,13 +217,12 @@
      * is used to erase FRP information on a unlockable device.
      */
     private void setPersistentDataBlockOemUnlockAllowedBit(boolean allowed) {
-        final PersistentDataBlockManager pdbm = (PersistentDataBlockManager)
-                mContext.getSystemService(Context.PERSISTENT_DATA_BLOCK_SERVICE);
+        final PersistentDataBlockManagerInternal pdbmi
+                = LocalServices.getService(PersistentDataBlockManagerInternal.class);
         // if mOemLock is PersistentDataBlockLock, then the bit should have already been set
-        if (pdbm != null && !(mOemLock instanceof PersistentDataBlockLock)
-                && pdbm.getOemUnlockEnabled() != allowed) {
+        if (pdbmi != null && !(mOemLock instanceof PersistentDataBlockLock)) {
             Slog.i(TAG, "Update OEM Unlock bit in pst partition to " + allowed);
-            pdbm.setOemUnlockEnabled(allowed);
+            pdbmi.forceOemUnlockEnabled(allowed);
         }
     }
 
diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java
index cc448da..a6ff4f7 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerSession.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java
@@ -344,9 +344,13 @@
         final boolean isSelfUpdatePermissionGranted =
                 (mPm.checkUidPermission(android.Manifest.permission.INSTALL_SELF_UPDATES,
                         mInstallerUid) == PackageManager.PERMISSION_GRANTED);
+        final boolean isUpdatePermissionGranted =
+                (mPm.checkUidPermission(android.Manifest.permission.INSTALL_PACKAGE_UPDATES,
+                        mInstallerUid) == PackageManager.PERMISSION_GRANTED);
+        final int targetPackageUid = mPm.getPackageUid(mPackageName, 0, userId);
         final boolean isPermissionGranted = isInstallPermissionGranted
-                || (isSelfUpdatePermissionGranted
-                    && mPm.getPackageUid(mPackageName, 0, userId) == mInstallerUid);
+                || (isUpdatePermissionGranted && targetPackageUid != -1)
+                || (isSelfUpdatePermissionGranted && targetPackageUid == mInstallerUid);
         final boolean isInstallerRoot = (mInstallerUid == Process.ROOT_UID);
         final boolean isInstallerSystem = (mInstallerUid == Process.SYSTEM_UID);
         final boolean forcePermissionPrompt =
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 2585cf3..faf6114 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -48,7 +48,6 @@
 import static android.content.pm.PackageManager.INSTALL_FAILED_INVALID_APK;
 import static android.content.pm.PackageManager.INSTALL_FAILED_INVALID_INSTALL_LOCATION;
 import static android.content.pm.PackageManager.INSTALL_FAILED_MISSING_SHARED_LIBRARY;
-import static android.content.pm.PackageManager.INSTALL_FAILED_NEWER_SDK;
 import static android.content.pm.PackageManager.INSTALL_FAILED_PACKAGE_CHANGED;
 import static android.content.pm.PackageManager.INSTALL_FAILED_REPLACE_COULDNT_DELETE;
 import static android.content.pm.PackageManager.INSTALL_FAILED_SANDBOX_VERSION_DOWNGRADE;
@@ -119,6 +118,7 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.app.ActivityManager;
+import android.app.ActivityManagerInternal;
 import android.app.AppOpsManager;
 import android.app.IActivityManager;
 import android.app.ResourcesManager;
@@ -993,6 +993,7 @@
     private List<String> mKeepUninstalledPackages;
 
     private UserManagerInternal mUserManagerInternal;
+    private ActivityManagerInternal mActivityManagerInternal;
 
     private DeviceIdleController.LocalService mDeviceIdleController;
 
@@ -4576,6 +4577,14 @@
         return mUserManagerInternal;
     }
 
+    private ActivityManagerInternal getActivityManagerInternal() {
+        if (mActivityManagerInternal == null) {
+            mActivityManagerInternal = LocalServices.getService(ActivityManagerInternal.class);
+        }
+        return mActivityManagerInternal;
+    }
+
+
     private DeviceIdleController.LocalService getDeviceIdleController() {
         if (mDeviceIdleController == null) {
             mDeviceIdleController =
@@ -4736,8 +4745,12 @@
             int filterCallingUid, int userId) {
         if (!sUserManager.exists(userId)) return null;
         flags = updateFlagsForComponent(flags, userId, component);
-        mPermissionManager.enforceCrossUserPermission(Binder.getCallingUid(), userId,
-                false /* requireFullPermission */, false /* checkShell */, "get activity info");
+
+        if (!isRecentsAccessingChildProfiles(Binder.getCallingUid(), userId)) {
+            mPermissionManager.enforceCrossUserPermission(Binder.getCallingUid(), userId,
+                    false /* requireFullPermission */, false /* checkShell */, "get activity info");
+        }
+
         synchronized (mPackages) {
             PackageParser.Activity a = mActivities.mActivities.get(component);
 
@@ -4759,6 +4772,22 @@
         return null;
     }
 
+    private boolean isRecentsAccessingChildProfiles(int callingUid, int targetUserId) {
+        if (!getActivityManagerInternal().isCallerRecents(callingUid)) {
+            return false;
+        }
+        final long token = Binder.clearCallingIdentity();
+        try {
+            final int callingUserId = UserHandle.getUserId(callingUid);
+            if (ActivityManager.getCurrentUser() != callingUserId) {
+                return false;
+            }
+            return sUserManager.isSameProfileGroup(callingUserId, targetUserId);
+        } finally {
+            Binder.restoreCallingIdentity(token);
+        }
+    }
+
     @Override
     public boolean activitySupportsIntent(ComponentName component, Intent intent,
             String resolvedType) {
@@ -16495,16 +16524,6 @@
             Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
         }
 
-        // App targetSdkVersion is below min supported version
-        if (!forceSdk && pkg.applicationInfo.isTargetingDeprecatedSdkVersion()) {
-            Slog.w(TAG, "App " + pkg.packageName + " targets deprecated sdk");
-
-            res.setError(INSTALL_FAILED_NEWER_SDK,
-                    "App is targeting deprecated sdk (targetSdkVersion should be at least "
-                    + Build.VERSION.MIN_SUPPORTED_TARGET_SDK_INT + ").");
-            return;
-        }
-
         // Instant apps have several additional install-time checks.
         if (instantApp) {
             if (pkg.applicationInfo.targetSdkVersion < Build.VERSION_CODES.O) {
diff --git a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
index a33f071..47cd813 100644
--- a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
+++ b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
@@ -236,6 +236,8 @@
                     return runHasFeature();
                 case "set-harmful-app-warning":
                     return runSetHarmfulAppWarning();
+                case "get-harmful-app-warning":
+                    return runGetHarmfulAppWarning();
                 default: {
                     String nextArg = getNextArg();
                     if (nextArg == null) {
@@ -2125,6 +2127,31 @@
         return 0;
     }
 
+    private int runGetHarmfulAppWarning() throws RemoteException {
+        int userId = UserHandle.USER_CURRENT;
+
+        String opt;
+        while ((opt = getNextOption()) != null) {
+            if (opt.equals("--user")) {
+                userId = UserHandle.parseUserArg(getNextArgRequired());
+            } else {
+                getErrPrintWriter().println("Error: Unknown option: " + opt);
+                return -1;
+            }
+        }
+
+        userId = translateUserId(userId, false /*allowAll*/, "runGetHarmfulAppWarning");
+
+        final String packageName = getNextArgRequired();
+        final CharSequence warning = mInterface.getHarmfulAppWarning(packageName, userId);
+        if (!TextUtils.isEmpty(warning)) {
+            getOutPrintWriter().println(warning);
+            return 0;
+        } else {
+            return 1;
+        }
+    }
+
     private static String checkAbiArgument(String abi) {
         if (TextUtils.isEmpty(abi)) {
             throw new IllegalArgumentException("Missing ABI argument");
@@ -2684,6 +2711,9 @@
         pw.println("");
         pw.println("  set-harmful-app-warning [--user <USER_ID>] <PACKAGE> [<WARNING>]");
         pw.println("    Mark the app as harmful with the given warning message.");
+        pw.println("");
+        pw.println("  get-harmful-app-warning [--user <USER_ID>] <PACKAGE>");
+        pw.println("    Return the harmful app warning message for the given app, if present");
         pw.println();
         Intent.printIntentArgsHelp(pw , "");
     }
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index 88e4270..0f394a4 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -66,7 +66,6 @@
 import static android.view.WindowManager.LayoutParams.LAST_SYSTEM_WINDOW;
 import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
 import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_DEFAULT;
-import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_NEVER;
 import static android.view.WindowManager.LayoutParams.MATCH_PARENT;
 import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_ACQUIRES_SLEEP_TOKEN;
 import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_FORCE_DRAW_STATUS_BAR_BACKGROUND;
@@ -163,13 +162,10 @@
 import android.app.UiModeManager;
 import android.content.ActivityNotFoundException;
 import android.content.BroadcastReceiver;
-import android.content.ComponentCallbacks;
-import android.content.ComponentName;
 import android.content.ContentResolver;
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
-import android.content.ServiceConnection;
 import android.content.pm.ActivityInfo;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageManager;
@@ -202,7 +198,6 @@
 import android.os.IDeviceIdleController;
 import android.os.Looper;
 import android.os.Message;
-import android.os.Messenger;
 import android.os.PowerManager;
 import android.os.PowerManagerInternal;
 import android.os.Process;
@@ -850,7 +845,7 @@
                     dispatchMediaKeyRepeatWithWakeLock((KeyEvent)msg.obj);
                     break;
                 case MSG_DISPATCH_SHOW_RECENTS:
-                    showRecentApps(false, msg.arg1 != 0);
+                    showRecentApps(false);
                     break;
                 case MSG_DISPATCH_SHOW_GLOBAL_ACTIONS:
                     showGlobalActionsInternal();
@@ -1030,8 +1025,10 @@
             public void run() {
                 // send interaction hint to improve redraw performance
                 mPowerManagerInternal.powerHint(PowerHint.INTERACTION, 0);
-                if (showRotationChoice(mCurrentAppOrientation, mRotation)) {
-                    sendProposedRotationChangeToStatusBarInternal(mRotation);
+                if (isRotationChoiceEnabled()) {
+                    final boolean isValid = isValidRotationChoice(mCurrentAppOrientation,
+                            mRotation);
+                    sendProposedRotationChangeToStatusBarInternal(mRotation, isValid);
                 } else {
                     updateRotation(false);
                 }
@@ -3817,7 +3814,7 @@
                 final int shiftlessModifiers = event.getModifiers() & ~KeyEvent.META_SHIFT_MASK;
                 if (KeyEvent.metaStateHasModifiers(shiftlessModifiers, KeyEvent.META_ALT_ON)) {
                     mRecentAppsHeldModifiers = shiftlessModifiers;
-                    showRecentApps(true, false);
+                    showRecentApps(true);
                     return -1;
                 }
             }
@@ -4164,16 +4161,16 @@
     }
 
     @Override
-    public void showRecentApps(boolean fromHome) {
+    public void showRecentApps() {
         mHandler.removeMessages(MSG_DISPATCH_SHOW_RECENTS);
-        mHandler.obtainMessage(MSG_DISPATCH_SHOW_RECENTS, fromHome ? 1 : 0, 0).sendToTarget();
+        mHandler.obtainMessage(MSG_DISPATCH_SHOW_RECENTS).sendToTarget();
     }
 
-    private void showRecentApps(boolean triggeredFromAltTab, boolean fromHome) {
+    private void showRecentApps(boolean triggeredFromAltTab) {
         mPreloadedRecentApps = false; // preloading no longer needs to be canceled
         StatusBarManagerInternal statusbar = getStatusBarManagerInternal();
         if (statusbar != null) {
-            statusbar.showRecentApps(triggeredFromAltTab, fromHome);
+            statusbar.showRecentApps(triggeredFromAltTab);
         }
     }
 
@@ -4355,6 +4352,7 @@
             DisplayFrames displayFrames, Rect outContentInsets, Rect outStableInsets,
             Rect outOutsets, DisplayCutout.ParcelableWrapper outDisplayCutout) {
         final int fl = PolicyControl.getWindowFlags(null, attrs);
+        final int pfl = attrs.privateFlags;
         final int sysuiVis = PolicyControl.getSystemUiVisibility(null, attrs);
         final int systemUiVisibility = (sysuiVis | attrs.subtreeSystemUiVisibility);
         final int displayRotation = displayFrames.mRotation;
@@ -4377,8 +4375,12 @@
             }
         }
 
-        if ((fl & (FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR))
-                == (FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR)) {
+        final boolean layoutInScreenAndInsetDecor =
+                (fl & (FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR))
+                        == (FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR);
+        final boolean screenDecor = (pfl & PRIVATE_FLAG_IS_SCREEN_DECOR) != 0;
+
+        if (layoutInScreenAndInsetDecor && !screenDecor) {
             Rect frame;
             int availRight, availBottom;
             if (canHideNavigationBar() &&
@@ -4498,7 +4500,7 @@
                             mHandler.obtainMessage(MSG_DISPOSE_INPUT_CONSUMER, mInputConsumer));
                     mInputConsumer = null;
                 }
-            } else if (mInputConsumer == null) {
+            } else if (mInputConsumer == null && mStatusBar != null && canHideNavigationBar()) {
                 mInputConsumer = mWindowManagerFuncs.createInputConsumer(mHandler.getLooper(),
                         INPUT_CONSUMER_NAVIGATION,
                         (channel, looper) -> new HideNavInputEventReceiver(channel, looper));
@@ -6193,10 +6195,10 @@
     /**
      * Notify the StatusBar that system rotation suggestion has changed.
      */
-    private void sendProposedRotationChangeToStatusBarInternal(int rotation) {
+    private void sendProposedRotationChangeToStatusBarInternal(int rotation, boolean isValid) {
         StatusBarManagerInternal statusBar = getStatusBarManagerInternal();
         if (statusBar != null) {
-            statusBar.onProposedRotationChanged(rotation);
+            statusBar.onProposedRotationChanged(rotation, isValid);
         }
     }
 
@@ -7142,15 +7144,14 @@
         mOrientationListener.setCurrentRotation(rotation);
     }
 
-    public boolean showRotationChoice(int orientation, final int preferredRotation) {
+    public boolean isRotationChoiceEnabled() {
         // Rotation choice is only shown when the user is in locked mode.
         if (mUserRotationMode != WindowManagerPolicy.USER_ROTATION_LOCKED) return false;
 
-        // We should only show a rotation choice if:
-        // 1. The rotation isn't forced by the lid, dock, demo, hdmi, vr, etc mode
-        // 2. The user choice won't be ignored due to screen orientation settings
+        // We should only enable rotation choice if the rotation isn't forced by the lid, dock,
+        // demo, hdmi, vr, etc mode
 
-        // Determine if the rotation currently forced
+        // Determine if the rotation is currently forced
         if (mForceDefaultOrientation) {
             return false; // Rotation is forced to default orientation
 
@@ -7183,7 +7184,14 @@
             return false;
         }
 
-        // Determine if the orientation will ignore user choice
+        // Rotation isn't forced, enable choice
+        return true;
+    }
+
+    public boolean isValidRotationChoice(int orientation, final int preferredRotation) {
+        // Determine if the given app orientation can be chosen and, if so, if it is compatible
+        // with the provided rotation choice
+
         switch (orientation) {
             case ActivityInfo.SCREEN_ORIENTATION_PORTRAIT:
             case ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE:
diff --git a/services/core/java/com/android/server/policy/WindowManagerPolicy.java b/services/core/java/com/android/server/policy/WindowManagerPolicy.java
index c05dd2a..e9c4c5c 100644
--- a/services/core/java/com/android/server/policy/WindowManagerPolicy.java
+++ b/services/core/java/com/android/server/policy/WindowManagerPolicy.java
@@ -1571,7 +1571,7 @@
      * Show the recents task list app.
      * @hide
      */
-    public void showRecentApps(boolean fromHome);
+    public void showRecentApps();
 
     /**
      * Show the global actions dialog.
diff --git a/services/core/java/com/android/server/slice/PinnedSliceState.java b/services/core/java/com/android/server/slice/PinnedSliceState.java
index cf930f5..5811714 100644
--- a/services/core/java/com/android/server/slice/PinnedSliceState.java
+++ b/services/core/java/com/android/server/slice/PinnedSliceState.java
@@ -21,7 +21,10 @@
 import android.content.ContentProviderClient;
 import android.net.Uri;
 import android.os.Bundle;
+import android.os.IBinder;
+import android.os.IBinder.DeathRecipient;
 import android.os.RemoteException;
+import android.util.ArrayMap;
 import android.util.ArraySet;
 import android.util.Log;
 
@@ -48,9 +51,13 @@
     @GuardedBy("mLock")
     private final ArraySet<String> mPinnedPkgs = new ArraySet<>();
     @GuardedBy("mLock")
-    private final ArraySet<ISliceListener> mListeners = new ArraySet<>();
+    private final ArrayMap<IBinder, ISliceListener> mListeners = new ArrayMap<>();
     @GuardedBy("mLock")
     private SliceSpec[] mSupportedSpecs = null;
+    @GuardedBy("mLock")
+    private final ArrayMap<IBinder, String> mPkgMap = new ArrayMap<>();
+
+    private final DeathRecipient mDeathRecipient = this::handleRecheckListeners;
 
     public PinnedSliceState(SliceManagerService service, Uri uri) {
         mService = service;
@@ -102,20 +109,29 @@
         mService.getHandler().post(this::handleBind);
     }
 
-    public void addSliceListener(ISliceListener listener, SliceSpec[] specs) {
+    public void addSliceListener(ISliceListener listener, String pkg, SliceSpec[] specs) {
         synchronized (mLock) {
-            if (mListeners.add(listener) && mListeners.size() == 1) {
+            if (mListeners.size() == 0) {
                 mService.listen(mUri);
             }
+            try {
+                listener.asBinder().linkToDeath(mDeathRecipient, 0);
+            } catch (RemoteException e) {
+            }
+            mListeners.put(listener.asBinder(), listener);
+            mPkgMap.put(listener.asBinder(), pkg);
             mergeSpecs(specs);
         }
     }
 
     public boolean removeSliceListener(ISliceListener listener) {
         synchronized (mLock) {
-            if (mListeners.remove(listener) && mListeners.size() == 0) {
+            listener.asBinder().unlinkToDeath(mDeathRecipient, 0);
+            mPkgMap.remove(listener.asBinder());
+            if (mListeners.containsKey(listener.asBinder()) && mListeners.size() == 1) {
                 mService.unlisten(mUri);
             }
+            mListeners.remove(listener.asBinder());
         }
         return !isPinned();
     }
@@ -154,38 +170,68 @@
         return client;
     }
 
+    private void handleRecheckListeners() {
+        if (!isPinned()) return;
+        synchronized (mLock) {
+            for (int i = mListeners.size() - 1; i >= 0; i--) {
+                ISliceListener l = mListeners.valueAt(i);
+                if (!l.asBinder().isBinderAlive()) {
+                    mListeners.removeAt(i);
+                }
+            }
+            if (!isPinned()) {
+                // All the listeners died, remove from pinned state.
+                mService.removePinnedSlice(mUri);
+            }
+        }
+    }
+
     private void handleBind() {
-        Slice s;
+        Slice cachedSlice = doBind(null);
+        synchronized (mLock) {
+            if (!isPinned()) return;
+            for (int i = mListeners.size() - 1; i >= 0; i--) {
+                ISliceListener l = mListeners.valueAt(i);
+                Slice s = cachedSlice;
+                if (s == null || s.hasHint(Slice.HINT_CALLER_NEEDED)) {
+                    s = doBind(mPkgMap.get(l));
+                }
+                if (s == null) {
+                    mListeners.removeAt(i);
+                    continue;
+                }
+                try {
+                    l.onSliceUpdated(s);
+                } catch (RemoteException e) {
+                    Log.e(TAG, "Unable to notify slice " + mUri, e);
+                    mListeners.removeAt(i);
+                    continue;
+                }
+            }
+            if (!isPinned()) {
+                // All the listeners died, remove from pinned state.
+                mService.removePinnedSlice(mUri);
+            }
+        }
+    }
+
+    private Slice doBind(String overridePkg) {
         try (ContentProviderClient client = getClient()) {
             Bundle extras = new Bundle();
             extras.putParcelable(SliceProvider.EXTRA_BIND_URI, mUri);
             extras.putParcelableArrayList(SliceProvider.EXTRA_SUPPORTED_SPECS,
                     new ArrayList<>(Arrays.asList(mSupportedSpecs)));
+            extras.putString(SliceProvider.EXTRA_OVERRIDE_PKG, overridePkg);
             final Bundle res;
             try {
                 res = client.call(SliceProvider.METHOD_SLICE, null, extras);
             } catch (RemoteException e) {
                 Log.e(TAG, "Unable to bind slice " + mUri, e);
-                return;
+                return null;
             }
-            if (res == null) return;
+            if (res == null) return null;
             Bundle.setDefusable(res, true);
-            s = res.getParcelable(SliceProvider.EXTRA_SLICE);
-        }
-        synchronized (mLock) {
-            mListeners.removeIf(l -> {
-                try {
-                    l.onSliceUpdated(s);
-                    return false;
-                } catch (RemoteException e) {
-                    Log.e(TAG, "Unable to notify slice " + mUri, e);
-                    return true;
-                }
-            });
-            if (!isPinned()) {
-                // All the listeners died, remove from pinned state.
-                mService.removePinnedSlice(mUri);
-            }
+            return res.getParcelable(SliceProvider.EXTRA_SLICE);
         }
     }
 
diff --git a/services/core/java/com/android/server/slice/SliceManagerService.java b/services/core/java/com/android/server/slice/SliceManagerService.java
index 2d9e772..c191580 100644
--- a/services/core/java/com/android/server/slice/SliceManagerService.java
+++ b/services/core/java/com/android/server/slice/SliceManagerService.java
@@ -16,27 +16,35 @@
 
 package com.android.server.slice;
 
+import static android.content.ContentProvider.getUriWithoutUserId;
 import static android.content.ContentProvider.getUserIdFromUri;
 import static android.content.ContentProvider.maybeAddUserId;
 
 import android.Manifest.permission;
+import android.app.ActivityManager;
 import android.app.AppOpsManager;
+import android.app.ContentProviderHolder;
+import android.app.IActivityManager;
 import android.app.slice.ISliceListener;
 import android.app.slice.ISliceManager;
+import android.app.slice.SliceManager;
 import android.app.slice.SliceSpec;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
+import android.content.pm.PackageManager;
 import android.content.pm.PackageManagerInternal;
 import android.content.pm.ResolveInfo;
 import android.database.ContentObserver;
 import android.net.Uri;
 import android.os.Binder;
 import android.os.Handler;
+import android.os.IBinder;
 import android.os.Looper;
 import android.os.Process;
 import android.os.RemoteException;
 import android.util.ArrayMap;
+import android.util.ArraySet;
 import android.util.Log;
 
 import com.android.internal.annotations.GuardedBy;
@@ -63,6 +71,8 @@
 
     @GuardedBy("mLock")
     private final ArrayMap<Uri, PinnedSliceState> mPinnedSlicesByUri = new ArrayMap<>();
+    @GuardedBy("mLock")
+    private final ArraySet<SliceGrant> mUserGrants = new ArraySet<>();
     private final Handler mHandler;
     private final ContentObserver mObserver;
 
@@ -81,9 +91,9 @@
 
         mObserver = new ContentObserver(mHandler) {
             @Override
-            public void onChange(boolean selfChange, Uri uri) {
+            public void onChange(boolean selfChange, Uri uri, int userId) {
                 try {
-                    getPinnedSlice(uri).onChange();
+                    getPinnedSlice(maybeAddUserId(uri, userId)).onChange();
                 } catch (IllegalStateException e) {
                     Log.e(TAG, "Received change for unpinned slice " + uri, e);
                 }
@@ -111,7 +121,7 @@
         verifyCaller(pkg);
         uri = maybeAddUserId(uri, Binder.getCallingUserHandle().getIdentifier());
         enforceAccess(pkg, uri);
-        getOrCreatePinnedSlice(uri).addSliceListener(listener, specs);
+        getOrCreatePinnedSlice(uri).addSliceListener(listener, pkg, specs);
     }
 
     @Override
@@ -156,8 +166,45 @@
         return getPinnedSlice(uri).getSpecs();
     }
 
+    @Override
+    public int checkSlicePermission(Uri uri, String pkg, int pid, int uid) throws RemoteException {
+        if (mContext.checkUriPermission(uri, pid, uid, Intent.FLAG_GRANT_WRITE_URI_PERMISSION)
+                == PackageManager.PERMISSION_GRANTED) {
+            return SliceManager.PERMISSION_GRANTED;
+        }
+        if (hasFullSliceAccess(pkg, uid)) {
+            return SliceManager.PERMISSION_GRANTED;
+        }
+        synchronized (mLock) {
+            if (mUserGrants.contains(new SliceGrant(uri, pkg))) {
+                return SliceManager.PERMISSION_USER_GRANTED;
+            }
+        }
+        return SliceManager.PERMISSION_DENIED;
+    }
+
+    @Override
+    public void grantPermissionFromUser(Uri uri, String pkg, String callingPkg, boolean allSlices) {
+        verifyCaller(callingPkg);
+        getContext().enforceCallingOrSelfPermission(permission.MANAGE_SLICE_PERMISSIONS,
+                "Slice granting requires MANAGE_SLICE_PERMISSIONS");
+        if (allSlices) {
+            // TODO: Manage full access grants.
+        } else {
+            synchronized (mLock) {
+                mUserGrants.add(new SliceGrant(uri, pkg));
+            }
+            long ident = Binder.clearCallingIdentity();
+            try {
+                mContext.getContentResolver().notifyChange(uri, null);
+            } finally {
+                Binder.restoreCallingIdentity(ident);
+            }
+        }
+    }
+
     ///  ----- internal code -----
-    void removePinnedSlice(Uri uri) {
+    protected void removePinnedSlice(Uri uri) {
         synchronized (mLock) {
             mPinnedSlicesByUri.remove(uri).destroy();
         }
@@ -186,7 +233,7 @@
     }
 
     @VisibleForTesting
-    PinnedSliceState createPinnedSlice(Uri uri) {
+    protected PinnedSliceState createPinnedSlice(Uri uri) {
         return new PinnedSliceState(this, uri);
     }
 
@@ -202,12 +249,45 @@
         return mHandler;
     }
 
-    private void enforceAccess(String pkg, Uri uri) {
-        getContext().enforceUriPermission(uri, permission.BIND_SLICE,
-                permission.BIND_SLICE, Binder.getCallingPid(), Binder.getCallingUid(),
-                Intent.FLAG_GRANT_WRITE_URI_PERMISSION,
-                "Slice binding requires the permission BIND_SLICE");
+    private void enforceAccess(String pkg, Uri uri) throws RemoteException {
         int user = Binder.getCallingUserHandle().getIdentifier();
+        // Check for default launcher/assistant.
+        if (!hasFullSliceAccess(pkg, Binder.getCallingUid())) {
+            try {
+                // Also allow things with uri access.
+                getContext().enforceUriPermission(uri, Binder.getCallingPid(),
+                        Binder.getCallingUid(),
+                        Intent.FLAG_GRANT_WRITE_URI_PERMISSION,
+                        "Slice binding requires permission to the Uri");
+            } catch (SecurityException e) {
+                // Last fallback (if the calling app owns the authority, then it can have access).
+                long ident = Binder.clearCallingIdentity();
+                try {
+                    IBinder token = new Binder();
+                    IActivityManager activityManager = ActivityManager.getService();
+                    ContentProviderHolder holder = null;
+                    String providerName = getUriWithoutUserId(uri).getAuthority();
+                    try {
+                        holder = activityManager.getContentProviderExternal(
+                                providerName, getUserIdFromUri(uri, user), token);
+                        if (holder == null || holder.info == null
+                                || !Objects.equals(holder.info.packageName, pkg)) {
+                            // No more fallbacks, no access.
+                            throw e;
+                        }
+                    } finally {
+                        if (holder != null && holder.provider != null) {
+                            activityManager.removeContentProviderExternal(providerName, token);
+                        }
+                    }
+                } finally {
+                    // I know, the double finally seems ugly, but seems safest for the identity.
+                    Binder.restoreCallingIdentity(ident);
+                }
+            }
+        }
+        // Lastly check for any multi-userness. Any return statements above here will break this
+        // important check.
         if (getUserIdFromUri(uri, user) != user) {
             getContext().enforceCallingOrSelfPermission(permission.INTERACT_ACROSS_USERS_FULL,
                     "Slice interaction across users requires INTERACT_ACROSS_USERS_FULL");
@@ -230,8 +310,14 @@
     }
 
     private boolean hasFullSliceAccess(String pkg, int userId) {
-        return isDefaultHomeApp(pkg, userId) || isAssistant(pkg, userId)
-                || isGrantedFullAccess(pkg, userId);
+        long ident = Binder.clearCallingIdentity();
+        try {
+            boolean ret = isDefaultHomeApp(pkg, userId) || isAssistant(pkg, userId)
+                    || isGrantedFullAccess(pkg, userId);
+            return ret;
+        } finally {
+            Binder.restoreCallingIdentity(ident);
+        }
     }
 
     private boolean isAssistant(String pkg, int userId) {
@@ -259,13 +345,14 @@
 
     private boolean isDefaultHomeApp(String pkg, int userId) {
         String defaultHome = getDefaultHome(userId);
-        return Objects.equals(pkg, defaultHome);
+
+        return pkg != null && Objects.equals(pkg, defaultHome);
     }
 
     // Based on getDefaultHome in ShortcutService.
     // TODO: Unify if possible
     @VisibleForTesting
-    String getDefaultHome(int userId) {
+    protected String getDefaultHome(int userId) {
         final long token = Binder.clearCallingIdentity();
         try {
             final List<ResolveInfo> allHomeCandidates = new ArrayList<>();
@@ -301,7 +388,7 @@
                     lastPriority = ri.priority;
                 }
             }
-            return detected.getPackageName();
+            return detected != null ? detected.getPackageName() : null;
         } finally {
             Binder.restoreCallingIdentity(token);
         }
@@ -349,4 +436,26 @@
             mService.onStopUser(userHandle);
         }
     }
+
+    private class SliceGrant {
+        private final Uri mUri;
+        private final String mPkg;
+
+        public SliceGrant(Uri uri, String pkg) {
+            mUri = uri;
+            mPkg = pkg;
+        }
+
+        @Override
+        public int hashCode() {
+            return mUri.hashCode() + mPkg.hashCode();
+        }
+
+        @Override
+        public boolean equals(Object obj) {
+            if (!(obj instanceof SliceGrant)) return false;
+            SliceGrant other = (SliceGrant) obj;
+            return Objects.equals(other.mUri, mUri) && Objects.equals(other.mPkg, mPkg);
+        }
+    }
 }
diff --git a/services/core/java/com/android/server/stats/StatsCompanionService.java b/services/core/java/com/android/server/stats/StatsCompanionService.java
index 2f5e2f8..f82dc24 100644
--- a/services/core/java/com/android/server/stats/StatsCompanionService.java
+++ b/services/core/java/com/android/server/stats/StatsCompanionService.java
@@ -28,11 +28,10 @@
 import android.net.NetworkStats;
 import android.net.wifi.IWifiManager;
 import android.net.wifi.WifiActivityEnergyInfo;
-import android.telephony.ModemActivityInfo;
-import android.telephony.TelephonyManager;
 import android.os.BatteryStatsInternal;
 import android.os.Binder;
 import android.os.Bundle;
+import android.os.Environment;
 import android.os.IBinder;
 import android.os.IStatsCompanionService;
 import android.os.IStatsManager;
@@ -40,18 +39,22 @@
 import android.os.Process;
 import android.os.RemoteException;
 import android.os.ServiceManager;
+import android.os.StatFs;
 import android.os.StatsLogEventWrapper;
 import android.os.SynchronousResultReceiver;
+import android.os.SystemClock;
 import android.os.UserHandle;
 import android.os.UserManager;
+import android.telephony.ModemActivityInfo;
+import android.telephony.TelephonyManager;
 import android.util.Slog;
 import android.util.StatsLog;
 
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.net.NetworkStatsFactory;
+import com.android.internal.os.KernelCpuSpeedReader;
 import com.android.internal.os.KernelWakelockReader;
 import com.android.internal.os.KernelWakelockStats;
-import com.android.internal.os.KernelCpuSpeedReader;
 import com.android.internal.os.PowerProfile;
 import com.android.server.LocalServices;
 import com.android.server.SystemService;
@@ -96,6 +99,11 @@
     private final KernelCpuSpeedReader[] mKernelCpuSpeedReaders;
     private IWifiManager mWifiManager = null;
     private TelephonyManager mTelephony = null;
+    private final StatFs mStatFsData = new StatFs(Environment.getDataDirectory().getAbsolutePath());
+    private final StatFs mStatFsSystem =
+        new StatFs(Environment.getRootDirectory().getAbsolutePath());
+    private final StatFs mStatFsTemp =
+        new StatFs(Environment.getDownloadCacheDirectory().getAbsolutePath());
 
     public StatsCompanionService(Context context) {
         super();
@@ -559,7 +567,7 @@
                     if (clusterTimeMs != null) {
                         for (int speed = clusterTimeMs.length - 1; speed >= 0; --speed) {
                             StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, 3);
-                            e.writeInt(tagId);
+                            e.writeInt(cluster);
                             e.writeInt(speed);
                             e.writeLong(clusterTimeMs[speed]);
                             ret.add(e);
@@ -588,6 +596,7 @@
                         e.writeLong(wifiInfo.getControllerIdleTimeMillis());
                         e.writeLong(wifiInfo.getControllerEnergyUsed());
                         ret.add(e);
+                        return ret.toArray(new StatsLogEventWrapper[ret.size()]);
                     } catch (RemoteException e) {
                         Slog.e(TAG, "Pulling wifiManager for wifi controller activity energy info has error", e);
                     } finally {
@@ -618,9 +627,40 @@
                     e.writeLong(modemInfo.getRxTimeMillis());
                     e.writeLong(modemInfo.getEnergyUsed());
                     ret.add(e);
+                    return ret.toArray(new StatsLogEventWrapper[ret.size()]);
                 }
                 break;
             }
+            case StatsLog.CPU_SUSPEND_TIME: {
+                List<StatsLogEventWrapper> ret = new ArrayList();
+                StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, 1);
+                e.writeLong(SystemClock.elapsedRealtime());
+                ret.add(e);
+                return ret.toArray(new StatsLogEventWrapper[ret.size()]);
+            }
+            case StatsLog.CPU_IDLE_TIME: {
+                List<StatsLogEventWrapper> ret = new ArrayList();
+                StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, 1);
+                e.writeLong(SystemClock.uptimeMillis());
+                ret.add(e);
+                return ret.toArray(new StatsLogEventWrapper[ret.size()]);
+            }
+            case StatsLog.DISK_SPACE: {
+              List<StatsLogEventWrapper> ret = new ArrayList();
+              StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, 3);
+              e.writeLong(mStatFsData.getAvailableBytes());
+              e.writeLong(mStatFsSystem.getAvailableBytes());
+              e.writeLong(mStatFsTemp.getAvailableBytes());
+              ret.add(e);
+              return ret.toArray(new StatsLogEventWrapper[ret.size()]);
+            }
+            case StatsLog.SYSTEM_UPTIME: {
+              List<StatsLogEventWrapper> ret = new ArrayList();
+              StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, 1);
+              e.writeLong(SystemClock.uptimeMillis());
+              ret.add(e);
+              return ret.toArray(new StatsLogEventWrapper[ret.size()]);
+            }
             default:
                 Slog.w(TAG, "No such tagId data as " + tagId);
                 return null;
diff --git a/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java b/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java
index 3792bc6..95006ff 100644
--- a/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java
+++ b/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java
@@ -30,7 +30,7 @@
 
     void cancelPreloadRecentApps();
 
-    void showRecentApps(boolean triggeredFromAltTab, boolean fromHome);
+    void showRecentApps(boolean triggeredFromAltTab);
 
     void hideRecentApps(boolean triggeredFromAltTab, boolean triggeredFromHomeKey);
 
@@ -95,7 +95,7 @@
      *
      * @param rotation rotation suggestion
      */
-    void onProposedRotationChanged(int rotation);
+    void onProposedRotationChanged(int rotation, boolean isValid);
 
     public interface GlobalActionsListener {
         /**
diff --git a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
index c7c03b4..c58c208 100644
--- a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
+++ b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
@@ -282,10 +282,10 @@
         }
 
         @Override
-        public void showRecentApps(boolean triggeredFromAltTab, boolean fromHome) {
+        public void showRecentApps(boolean triggeredFromAltTab) {
             if (mBar != null) {
                 try {
-                    mBar.showRecentApps(triggeredFromAltTab, fromHome);
+                    mBar.showRecentApps(triggeredFromAltTab);
                 } catch (RemoteException ex) {}
             }
         }
@@ -408,10 +408,10 @@
         }
 
         @Override
-        public void onProposedRotationChanged(int rotation) {
+        public void onProposedRotationChanged(int rotation, boolean isValid) {
             if (mBar != null){
                 try {
-                    mBar.onProposedRotationChanged(rotation);
+                    mBar.onProposedRotationChanged(rotation, isValid);
                 } catch (RemoteException ex) {}
             }
         }
diff --git a/services/core/java/com/android/server/wm/AppWindowToken.java b/services/core/java/com/android/server/wm/AppWindowToken.java
index b474c62..ce3f512 100644
--- a/services/core/java/com/android/server/wm/AppWindowToken.java
+++ b/services/core/java/com/android/server/wm/AppWindowToken.java
@@ -463,10 +463,14 @@
                 mService.mActivityManagerAppTransitionNotifier.onAppTransitionFinishedLocked(token);
             }
 
-            // Update the client visibility if we are not running an animation. Otherwise, we'll
-            // update client visibility state in onAnimationFinished.
-            if (!visible && !delayed) {
-                setClientHidden(true);
+            // If we're becoming visible, immediately change client visibility as well although it
+            // usually gets changed in AppWindowContainerController.setVisibility already. However,
+            // there seem to be some edge cases where we change our visibility but client visibility
+            // never gets updated.
+            // If we're becoming invisible, update the client visibility if we are not running an
+            // animation. Otherwise, we'll update client visibility in onAnimationFinished.
+            if (visible || !delayed) {
+                setClientHidden(!visible);
             }
 
             // If we are hidden but there is no delay needed we immediately
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index fba404e..6dc384a 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -49,6 +49,7 @@
 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING;
 import static android.view.WindowManager.LayoutParams.TYPE_BOOT_PROGRESS;
+import static android.view.WindowManager.LayoutParams.TYPE_DOCK_DIVIDER;
 import static android.view.WindowManager.LayoutParams.TYPE_DRAWN_APPLICATION;
 import static android.view.WindowManager.LayoutParams.TYPE_DREAM;
 import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD;
@@ -3180,6 +3181,21 @@
          */
         SurfaceControl mAppAnimationLayer = null;
 
+        /**
+         * Given that the split-screen divider does not have an AppWindowToken, it
+         * will have to live inside of a "NonAppWindowContainer", in particular
+         * {@link DisplayContent#mAboveAppWindowsContainers}. However, in visual Z order
+         * it will need to be interleaved with some of our children, appearing on top of
+         * both docked stacks but underneath any assistant stacks.
+         *
+         * To solve this problem we have this anchor control, which will always exist so
+         * we can always assign it the correct value in our {@link #assignChildLayers}.
+         * Likewise since it always exists, {@link AboveAppWindowContainers} can always
+         * assign the divider a layer relative to it. This way we prevent linking lifecycle
+         * events between the two containers.
+         */
+        SurfaceControl mSplitScreenDividerAnchor = null;
+
         // Cached reference to some special stacks we tend to get a lot so we don't need to loop
         // through the list to find them.
         private TaskStack mHomeStack = null;
@@ -3496,37 +3512,39 @@
 
         @Override
         void assignChildLayers(SurfaceControl.Transaction t) {
+
+            final int HOME_STACK_STATE = 0;
+            final int NORMAL_STACK_STATE = 1;
+            final int ALWAYS_ON_TOP_STATE = 2;
+
             int layer = 0;
-
-            // We allow stacks to change visual order from the AM specified order due to
-            // Z-boosting during animations. However we must take care to ensure TaskStacks
-            // which are marked as alwaysOnTop remain that way.
-            for (int i = 0; i < mChildren.size(); i++) {
-                final TaskStack s = mChildren.get(i);
-                s.assignChildLayers();
-                if (!s.needsZBoost() && !s.isAlwaysOnTop()) {
-                    s.assignLayer(t, layer++);
+            for (int state = 0; state <= ALWAYS_ON_TOP_STATE; state++) {
+                for (int i = 0; i < mChildren.size(); i++) {
+                    final TaskStack s = mChildren.get(i);
+                    if (state == HOME_STACK_STATE && s.isActivityTypeHome()) {
+                        s.assignLayer(t, layer++);
+                    } else if (state == NORMAL_STACK_STATE && !s.isActivityTypeHome()
+                            && !s.isAlwaysOnTop()) {
+                        s.assignLayer(t, layer++);
+                        if (s.inSplitScreenWindowingMode() && mSplitScreenDividerAnchor != null) {
+                            t.setLayer(mSplitScreenDividerAnchor, layer++);
+                        }
+                    } else if (state == ALWAYS_ON_TOP_STATE && s.isAlwaysOnTop()) {
+                        s.assignLayer(t, layer++);
+                    }
+                }
+                // The appropriate place for App-Transitions to occur is right
+                // above all other animations but still below things in the Picture-and-Picture
+                // windowing mode.
+                if (state == NORMAL_STACK_STATE && mAppAnimationLayer != null) {
+                    t.setLayer(mAppAnimationLayer, layer++);
                 }
             }
             for (int i = 0; i < mChildren.size(); i++) {
                 final TaskStack s = mChildren.get(i);
-                if (s.needsZBoost() && !s.isAlwaysOnTop()) {
-                    s.assignLayer(t, layer++);
-                }
-            }
-            for (int i = 0; i < mChildren.size(); i++) {
-                final TaskStack s = mChildren.get(i);
-                if (s.isAlwaysOnTop()) {
-                    s.assignLayer(t, layer++);
-                }
+                s.assignChildLayers(t);
             }
 
-            // The appropriate place for App-Transitions to occur is right
-            // above all other animations but still below things in the Picture-and-Picture
-            // windowing mode.
-            if (mAppAnimationLayer != null) {
-                t.setLayer(mAppAnimationLayer, layer++);
-            }
         }
 
         @Override
@@ -3534,6 +3552,10 @@
             return mAppAnimationLayer;
         }
 
+        SurfaceControl getSplitScreenDividerAnchor() {
+            return mSplitScreenDividerAnchor;
+        }
+
         @Override
         void onParentSet() {
             super.onParentSet();
@@ -3541,11 +3563,18 @@
                 mAppAnimationLayer = makeChildSurface(null)
                         .setName("animationLayer")
                         .build();
-                getPendingTransaction().show(mAppAnimationLayer);
+                mSplitScreenDividerAnchor = makeChildSurface(null)
+                        .setName("splitScreenDividerAnchor")
+                        .build();
+                getPendingTransaction()
+                        .show(mAppAnimationLayer)
+                        .show(mSplitScreenDividerAnchor);
                 scheduleAnimation();
             } else {
                 mAppAnimationLayer.destroy();
                 mAppAnimationLayer = null;
+                mSplitScreenDividerAnchor.destroy();
+                mSplitScreenDividerAnchor = null;
             }
         }
     }
@@ -3560,6 +3589,12 @@
                     && imeContainer.getSurfaceControl() != null;
             for (int j = 0; j < mChildren.size(); ++j) {
                 final WindowToken wt = mChildren.get(j);
+
+                // See {@link mSplitScreenDividerAnchor}
+                if (wt.windowType == TYPE_DOCK_DIVIDER) {
+                    wt.assignRelativeLayer(t, mTaskStackContainers.getSplitScreenDividerAnchor(), 1);
+                    continue;
+                }
                 wt.assignLayer(t, j);
                 wt.assignChildLayers(t);
 
diff --git a/services/core/java/com/android/server/wm/DockedStackDividerController.java b/services/core/java/com/android/server/wm/DockedStackDividerController.java
index 36e5d10..7ae1f24 100644
--- a/services/core/java/com/android/server/wm/DockedStackDividerController.java
+++ b/services/core/java/com/android/server/wm/DockedStackDividerController.java
@@ -607,7 +607,7 @@
         if (wasMinimized && mMinimizedDock && containsAppInDockedStack(openingApps)
                 && appTransition != TRANSIT_NONE &&
                 !AppTransition.isKeyguardGoingAwayTransit(appTransition)) {
-            mService.showRecentApps(true /* fromHome */);
+            mService.showRecentApps();
         }
     }
 
diff --git a/services/core/java/com/android/server/wm/RemoteAnimationController.java b/services/core/java/com/android/server/wm/RemoteAnimationController.java
index 688b4ff..8515dcb 100644
--- a/services/core/java/com/android/server/wm/RemoteAnimationController.java
+++ b/services/core/java/com/android/server/wm/RemoteAnimationController.java
@@ -99,11 +99,15 @@
     }
 
     private RemoteAnimationTarget[] createAnimations() {
-        final RemoteAnimationTarget[] result = new RemoteAnimationTarget[mPendingAnimations.size()];
+        final ArrayList<RemoteAnimationTarget> targets = new ArrayList<>();
         for (int i = mPendingAnimations.size() - 1; i >= 0; i--) {
-            result[i] = mPendingAnimations.get(i).createRemoteAppAnimation();
+            final RemoteAnimationTarget target =
+                    mPendingAnimations.get(i).createRemoteAppAnimation();
+            if (target != null) {
+                targets.add(target);
+            }
         }
-        return result;
+        return targets.toArray(new RemoteAnimationTarget[targets.size()]);
     }
 
     private void onAnimationFinished() {
@@ -145,9 +149,17 @@
         }
 
         RemoteAnimationTarget createRemoteAppAnimation() {
-            return new RemoteAnimationTarget(mAppWindowToken.getTask().mTaskId, getMode(),
+            final Task task = mAppWindowToken.getTask();
+            final WindowState mainWindow = mAppWindowToken.findMainWindow();
+            if (task == null) {
+                return null;
+            }
+            if (mainWindow == null) {
+                return null;
+            }
+            return new RemoteAnimationTarget(task.mTaskId, getMode(),
                     mCapturedLeash, !mAppWindowToken.fillsParent(),
-                    mAppWindowToken.findMainWindow().mWinAnimator.mLastClipRect,
+                    mainWindow.mWinAnimator.mLastClipRect,
                     mAppWindowToken.getPrefixOrderIndex(), mPosition, mStackBounds);
         }
 
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 53086f7..de1e7ec 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -5967,8 +5967,8 @@
         mPolicy.lockNow(options);
     }
 
-    public void showRecentApps(boolean fromHome) {
-        mPolicy.showRecentApps(fromHome);
+    public void showRecentApps() {
+        mPolicy.showRecentApps();
     }
 
     @Override
diff --git a/services/core/jni/Android.bp b/services/core/jni/Android.bp
index b18c1a0..0bc58e0 100644
--- a/services/core/jni/Android.bp
+++ b/services/core/jni/Android.bp
@@ -55,6 +55,17 @@
         "frameworks/native/services",
         "system/gatekeeper/include",
     ],
+
+    product_variables: {
+        arc: {
+            cflags: [
+                "-DUSE_ARC",
+            ],
+            srcs: [
+                "com_android_server_ArcVideoService.cpp",
+            ],
+        }
+    }
 }
 
 cc_defaults {
@@ -119,4 +130,17 @@
         "android.hardware.broadcastradio@common-utils-1x-lib",
         "libscrypt_static",
     ],
+
+    product_variables: {
+        arc: {
+            // TODO: remove the suffix "_bp" after finishing migration to Android.bp.
+            shared_libs: [
+                "libarcbridge",
+                "libarcbridgeservice",
+                "libarcvideobridge",
+                "libchrome",
+                "libmojo_bp",
+            ],
+        }
+    }
 }
diff --git a/services/core/jni/com_android_server_ArcVideoService.cpp b/services/core/jni/com_android_server_ArcVideoService.cpp
new file mode 100644
index 0000000..7df8276
--- /dev/null
+++ b/services/core/jni/com_android_server_ArcVideoService.cpp
@@ -0,0 +1,149 @@
+/*
+ * Copyright 2016, 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.
+ */
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "ArcVideoService"
+
+#include <binder/IPCThreadState.h>
+#include <binder/IServiceManager.h>
+#include <media/arcvideobridge/IArcVideoBridge.h>
+#include <utils/Log.h>
+
+#include <base/bind.h>
+#include <base/bind_helpers.h>
+#include <mojo/edk/embedder/embedder.h>
+#include <mojo/public/cpp/bindings/binding.h>
+
+#include <arc/ArcBridgeSupport.h>
+#include <arc/ArcService.h>
+#include <arc/Future.h>
+#include <arc/IArcBridgeService.h>
+#include <arc/MojoProcessSupport.h>
+#include <video.mojom.h>
+
+namespace {
+
+// [MinVersion] of OnVideoInstanceReady method in arc_bridge.mojom.
+constexpr int kMinimumArcBridgeHostVersion = 6;
+
+void onCaptureResult(arc::Future<arc::MojoBootstrapResult>* future, uint32_t version,
+                     mojo::ScopedHandle handle, const std::string& token) {
+    mojo::edk::ScopedPlatformHandle scoped_platform_handle;
+    MojoResult result =
+            mojo::edk::PassWrappedPlatformHandle(handle.release().value(), &scoped_platform_handle);
+    if (result != MOJO_RESULT_OK) {
+        ALOGE("Received invalid file descriptor.");
+        future->set(arc::MojoBootstrapResult());
+        return;
+    }
+
+    base::ScopedFD fd(scoped_platform_handle.release().handle);
+    future->set(arc::MojoBootstrapResult(std::move(fd), token, version));
+}
+
+}  // namespace
+
+namespace arc {
+
+class VideoService : public mojom::VideoInstance,
+                     public ArcService,
+                     public android::BnArcVideoBridge {
+public:
+    explicit VideoService(MojoProcessSupport* mojoProcessSupport)
+          : mMojoProcessSupport(mojoProcessSupport), mBinding(this) {
+        mMojoProcessSupport->arc_bridge_support().requestArcBridgeProxyAsync(
+                this, kMinimumArcBridgeHostVersion);
+    }
+
+    ~VideoService() override { mMojoProcessSupport->disconnect(&mBinding, &mHostPtr); }
+
+    // VideoInstance overrides:
+    void InitDeprecated(mojom::VideoHostPtr hostPtr) override {
+        Init(std::move(hostPtr), base::Bind(&base::DoNothing));
+    }
+
+    void Init(mojom::VideoHostPtr hostPtr, const InitCallback& callback) override {
+        ALOGV("Init");
+        mHostPtr = std::move(hostPtr);
+        // A method must be called while we are still in a Mojo thread so the
+        // proxy can perform lazy initialization and be able to be called from
+        // non-Mojo threads later.
+        // This also caches the version number so it can be obtained by calling
+        // .version().
+        mHostPtr.QueryVersion(base::Bind(
+            [](const InitCallback& callback, uint32_t version) {
+                ALOGI("VideoService ready (version=%d)", version);
+                callback.Run();
+            },
+            callback));
+        ALOGV("Init done");
+    }
+
+    // ArcService overrides:
+    void ready(mojom::ArcBridgeHostPtr* bridgeHost) override {
+        (*bridgeHost)->OnVideoInstanceReady(mBinding.CreateInterfacePtrAndBind());
+    }
+
+    void versionMismatch(uint32_t version) override {
+        ALOGE("ArcBridgeHost version %d, does not support video (version %d)\n", version,
+              kMinimumArcBridgeHostVersion);
+    }
+
+    // BnArcVideoBridge overrides:
+    MojoBootstrapResult bootstrapVideoAcceleratorFactory() override {
+        ALOGV("VideoService::bootstrapVideoAcceleratorFactory");
+
+        Future<MojoBootstrapResult> future;
+        mMojoProcessSupport->mojo_thread().getTaskRunner()->PostTask(
+                FROM_HERE, base::Bind(&VideoService::bootstrapVideoAcceleratorFactoryOnMojoThread,
+                                      base::Unretained(this), &future));
+        return future.get();
+    }
+
+    int32_t hostVersion() override {
+        ALOGV("VideoService::hostVersion");
+        return mHostPtr.version();
+    }
+
+private:
+    void bootstrapVideoAcceleratorFactoryOnMojoThread(Future<MojoBootstrapResult>* future) {
+        if (!mHostPtr) {
+            ALOGE("mHostPtr is not ready yet");
+            future->set(MojoBootstrapResult());
+            return;
+        }
+        mHostPtr->OnBootstrapVideoAcceleratorFactory(
+                base::Bind(&onCaptureResult, base::Unretained(future), mHostPtr.version()));
+    }
+
+    // Outlives VideoService.
+    MojoProcessSupport* const mMojoProcessSupport;
+    mojo::Binding<mojom::VideoInstance> mBinding;
+    mojom::VideoHostPtr mHostPtr;
+};
+
+}  // namespace arc
+
+namespace android {
+
+int register_android_server_ArcVideoService() {
+    defaultServiceManager()->addService(
+            String16("android.os.IArcVideoBridge"),
+            new arc::VideoService(arc::MojoProcessSupport::getLeakyInstance()));
+    return 0;
+}
+
+}  // namespace android
diff --git a/services/core/jni/onload.cpp b/services/core/jni/onload.cpp
index 071b6b8..07ddb05 100644
--- a/services/core/jni/onload.cpp
+++ b/services/core/jni/onload.cpp
@@ -52,6 +52,9 @@
 int register_android_server_GraphicsStatsService(JNIEnv* env);
 int register_android_hardware_display_DisplayViewport(JNIEnv* env);
 int register_android_server_net_NetworkStatsService(JNIEnv* env);
+#ifdef USE_ARC
+int register_android_server_ArcVideoService();
+#endif
 };
 
 using namespace android;
@@ -97,6 +100,8 @@
     register_android_server_GraphicsStatsService(env);
     register_android_hardware_display_DisplayViewport(env);
     register_android_server_net_NetworkStatsService(env);
-
+#ifdef USE_ARC
+    register_android_server_ArcVideoService();
+#endif
     return JNI_VERSION_1_4;
 }
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java b/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java
index 99275e8..7a0b1bf 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java
@@ -39,12 +39,6 @@
  */
 abstract class BaseIDevicePolicyManager extends IDevicePolicyManager.Stub {
     /**
-     * To be called by {@link DevicePolicyManagerService#Lifecycle} when the service is started.
-     *
-     * @see {@link SystemService#onStart}.
-     */
-    abstract void handleStart();
-    /**
      * To be called by {@link DevicePolicyManagerService#Lifecycle} during the various boot phases.
      *
      * @see {@link SystemService#onBootPhase}.
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 956b185..6bee9d6 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -198,8 +198,10 @@
 import com.android.internal.util.XmlUtils;
 import com.android.internal.widget.LockPatternUtils;
 import com.android.server.LocalServices;
+import com.android.server.SystemServerInitThreadPool;
 import com.android.server.SystemService;
 import com.android.server.devicepolicy.DevicePolicyManagerService.ActiveAdmin.TrustAgentInfo;
+import com.android.server.net.NetworkPolicyManagerInternal;
 import com.android.server.pm.UserRestrictionsUtils;
 
 import com.google.android.collect.Sets;
@@ -511,7 +513,6 @@
         @Override
         public void onStart() {
             publishBinderService(Context.DEVICE_POLICY_SERVICE, mService);
-            mService.handleStart();
         }
 
         @Override
@@ -653,14 +654,14 @@
             }
 
             if (Intent.ACTION_USER_ADDED.equals(action)) {
-                sendUserAddedOrRemovedCommand(DeviceAdminReceiver.ACTION_USER_ADDED, userHandle);
+                sendDeviceOwnerUserCommand(DeviceAdminReceiver.ACTION_USER_ADDED, userHandle);
                 synchronized (DevicePolicyManagerService.this) {
                     // It might take a while for the user to become affiliated. Make security
                     // and network logging unavailable in the meantime.
                     maybePauseDeviceWideLoggingLocked();
                 }
             } else if (Intent.ACTION_USER_REMOVED.equals(action)) {
-                sendUserAddedOrRemovedCommand(DeviceAdminReceiver.ACTION_USER_REMOVED, userHandle);
+                sendDeviceOwnerUserCommand(DeviceAdminReceiver.ACTION_USER_REMOVED, userHandle);
                 synchronized (DevicePolicyManagerService.this) {
                     // Check whether the user is affiliated, *before* removing its data.
                     boolean isRemovedUserAffiliated = isUserAffiliatedWithDeviceLocked(userHandle);
@@ -674,12 +675,17 @@
                     }
                 }
             } else if (Intent.ACTION_USER_STARTED.equals(action)) {
+                sendDeviceOwnerUserCommand(DeviceAdminReceiver.ACTION_USER_STARTED, userHandle);
                 synchronized (DevicePolicyManagerService.this) {
                     maybeSendAdminEnabledBroadcastLocked(userHandle);
                     // Reset the policy data
                     mUserData.remove(userHandle);
                 }
                 handlePackagesChanged(null /* check all admins */, userHandle);
+            } else if (Intent.ACTION_USER_STOPPED.equals(action)) {
+                sendDeviceOwnerUserCommand(DeviceAdminReceiver.ACTION_USER_STOPPED, userHandle);
+            } else if (Intent.ACTION_USER_SWITCHED.equals(action)) {
+                sendDeviceOwnerUserCommand(DeviceAdminReceiver.ACTION_USER_SWITCHED, userHandle);
             } else if (Intent.ACTION_USER_UNLOCKED.equals(action)) {
                 synchronized (DevicePolicyManagerService.this) {
                     maybeSendAdminEnabledBroadcastLocked(userHandle);
@@ -688,7 +694,7 @@
                 handlePackagesChanged(null /* check all admins */, userHandle);
             } else if (Intent.ACTION_PACKAGE_CHANGED.equals(action)
                     || (Intent.ACTION_PACKAGE_ADDED.equals(action)
-                            && intent.getBooleanExtra(Intent.EXTRA_REPLACING, false))) {
+                    && intent.getBooleanExtra(Intent.EXTRA_REPLACING, false))) {
                 handlePackagesChanged(intent.getData().getSchemeSpecificPart(), userHandle);
             } else if (Intent.ACTION_PACKAGE_REMOVED.equals(action)
                     && !intent.getBooleanExtra(Intent.EXTRA_REPLACING, false)) {
@@ -698,7 +704,7 @@
             }
         }
 
-        private void sendUserAddedOrRemovedCommand(String action, int userHandle) {
+        private void sendDeviceOwnerUserCommand(String action, int userHandle) {
             synchronized (DevicePolicyManagerService.this) {
                 ActiveAdmin deviceOwner = getDeviceOwnerAdminLocked();
                 if (deviceOwner != null) {
@@ -1738,6 +1744,10 @@
             return LocalServices.getService(UsageStatsManagerInternal.class);
         }
 
+        NetworkPolicyManagerInternal getNetworkPolicyManagerInternal() {
+            return LocalServices.getService(NetworkPolicyManagerInternal.class);
+        }
+
         NotificationManager getNotificationManager() {
             return mContext.getSystemService(NotificationManager.class);
         }
@@ -1991,6 +2001,10 @@
         KeyChainConnection keyChainBindAsUser(UserHandle user) throws InterruptedException {
             return KeyChain.bindAsUser(mContext, user);
         }
+
+        void postOnSystemServerInitThreadPool(Runnable runnable) {
+            SystemServerInitThreadPool.get().submit(runnable, LOG_TAG);
+        }
     }
 
     /**
@@ -2046,6 +2060,8 @@
         filter.addAction(Intent.ACTION_USER_ADDED);
         filter.addAction(Intent.ACTION_USER_REMOVED);
         filter.addAction(Intent.ACTION_USER_STARTED);
+        filter.addAction(Intent.ACTION_USER_STOPPED);
+        filter.addAction(Intent.ACTION_USER_SWITCHED);
         filter.addAction(Intent.ACTION_USER_UNLOCKED);
         filter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY);
         mContext.registerReceiverAsUser(mReceiver, UserHandle.ALL, filter, null, mHandler);
@@ -3232,6 +3248,7 @@
         switch (phase) {
             case SystemService.PHASE_LOCK_SETTINGS_READY:
                 onLockSettingsReady();
+                loadAdminDataAsync();
                 break;
             case SystemService.PHASE_BOOT_COMPLETED:
                 ensureDeviceOwnerUserStarted(); // TODO Consider better place to do this.
@@ -3300,11 +3317,6 @@
     }
 
     @Override
-    void handleStart() {
-        pushActiveAdminPackages();
-    }
-
-    @Override
     void handleStartUser(int userId) {
         updateScreenCaptureDisabledInWindowManager(userId,
                 getScreenCaptureDisabled(null, userId));
@@ -3486,6 +3498,14 @@
         }
     }
 
+    private void loadAdminDataAsync() {
+        mInjector.postOnSystemServerInitThreadPool(() -> {
+            pushActiveAdminPackages();
+            mUsageStatsManagerInternal.onAdminDataAvailable();
+            mInjector.getNetworkPolicyManagerInternal().onAdminDataAvailable();
+        });
+    }
+
     private void pushActiveAdminPackages() {
         synchronized (this) {
             final List<UserInfo> users = mUserManager.getUsers();
@@ -10209,7 +10229,8 @@
         final int userId = UserHandle.getUserId(uid);
         Intent intent = null;
         if (DevicePolicyManager.POLICY_DISABLE_CAMERA.equals(restriction) ||
-                DevicePolicyManager.POLICY_DISABLE_SCREEN_CAPTURE.equals(restriction)) {
+                DevicePolicyManager.POLICY_DISABLE_SCREEN_CAPTURE.equals(restriction) ||
+                DevicePolicyManager.POLICY_MANDATORY_BACKUPS.equals(restriction)) {
             synchronized(this) {
                 final DevicePolicyData policy = getUserData(userId);
                 final int N = policy.mAdminList.size();
@@ -10218,7 +10239,9 @@
                     if ((admin.disableCamera &&
                                 DevicePolicyManager.POLICY_DISABLE_CAMERA.equals(restriction)) ||
                         (admin.disableScreenCapture && DevicePolicyManager
-                                .POLICY_DISABLE_SCREEN_CAPTURE.equals(restriction))) {
+                                .POLICY_DISABLE_SCREEN_CAPTURE.equals(restriction)) ||
+                        (admin.mandatoryBackupTransport != null && DevicePolicyManager
+                                .POLICY_MANDATORY_BACKUPS.equals(restriction))) {
                         intent = createShowAdminSupportIntent(admin.info.getComponent(), userId);
                         break;
                     }
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index e660c50..94a356e 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -1172,6 +1172,15 @@
             }
             traceEnd();
 
+            traceBeginAndSlog("StartSystemUpdateManagerService");
+            try {
+                ServiceManager.addService(Context.SYSTEM_UPDATE_SERVICE,
+                        new SystemUpdateManagerService(context));
+            } catch (Throwable e) {
+                reportWtf("starting SystemUpdateManagerService", e);
+            }
+            traceEnd();
+
             traceBeginAndSlog("StartUpdateLockService");
             try {
                 ServiceManager.addService(Context.UPDATE_LOCK_SERVICE,
diff --git a/services/tests/servicestests/src/com/android/server/ForceAppStandbyTrackerTest.java b/services/tests/servicestests/src/com/android/server/ForceAppStandbyTrackerTest.java
index 66d0da1..de54e52 100644
--- a/services/tests/servicestests/src/com/android/server/ForceAppStandbyTrackerTest.java
+++ b/services/tests/servicestests/src/com/android/server/ForceAppStandbyTrackerTest.java
@@ -42,6 +42,7 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
+import android.os.BatteryManager;
 import android.os.Handler;
 import android.os.Looper;
 import android.os.PowerManager.ServiceType;
@@ -50,13 +51,17 @@
 import android.os.Process;
 import android.os.RemoteException;
 import android.os.UserHandle;
+import android.provider.Settings;
+import android.provider.Settings.Global;
 import android.support.test.filters.SmallTest;
 import android.support.test.runner.AndroidJUnit4;
+import android.test.mock.MockContentResolver;
 import android.util.ArraySet;
 import android.util.Pair;
 
 import com.android.internal.app.IAppOpsCallback;
 import com.android.internal.app.IAppOpsService;
+import com.android.internal.util.test.FakeSettingsProvider;
 import com.android.server.ForceAppStandbyTracker.Listener;
 
 import org.junit.Before;
@@ -102,6 +107,9 @@
         PowerManagerInternal injectPowerManagerInternal() {
             return mMockPowerManagerInternal;
         }
+
+        @Override
+        boolean isSmallBatteryDevice() { return mIsSmallBatteryDevice; };
     }
 
     private static final int UID_1 = Process.FIRST_APPLICATION_UID + 1;
@@ -137,7 +145,11 @@
     private Consumer<PowerSaveState> mPowerSaveObserver;
     private BroadcastReceiver mReceiver;
 
+    private MockContentResolver mMockContentResolver;
+    private FakeSettingsProvider mFakeSettingsProvider;
+
     private boolean mPowerSaveMode;
+    private boolean mIsSmallBatteryDevice;
 
     private final ArraySet<Pair<Integer, String>> mRestrictedPackages = new ArraySet();
 
@@ -182,6 +194,11 @@
                 any(int[].class)
                 )).thenAnswer(inv -> new ArrayList<AppOpsManager.PackageOps>());
 
+        mMockContentResolver = new MockContentResolver();
+        mFakeSettingsProvider = new FakeSettingsProvider();
+        when(mMockContext.getContentResolver()).thenReturn(mMockContentResolver);
+        mMockContentResolver.addProvider(Settings.AUTHORITY, mFakeSettingsProvider);
+
         // Call start.
         instance.start();
 
@@ -221,6 +238,7 @@
         assertNotNull(mAppOpsCallback);
         assertNotNull(mPowerSaveObserver);
         assertNotNull(mReceiver);
+        assertNotNull(instance.mFlagsObserver);
     }
 
     private void setAppOps(int uid, String packageName, boolean restrict) throws RemoteException {
@@ -495,8 +513,8 @@
         verify(l, times(0)).unblockAlarmsForUidPackage(anyInt(), anyString());
         reset(l);
 
-        // Power save on.
-        mPowerSaveMode = true;
+        // Updating to the same state should not fire listener
+        mPowerSaveMode = false;
         mPowerSaveObserver.accept(getPowerSaveState());
 
         assertNoCallbacks(l);
@@ -534,14 +552,14 @@
 
         verify(l, times(0)).unblockAllUnrestrictedAlarms();
         verify(l, times(0)).unblockAlarmsForUid(anyInt());
-        verify(l, times(0)).unblockAlarmsForUidPackage(eq(UID_10_2), eq(PACKAGE_2));
+        verify(l, times(1)).unblockAlarmsForUidPackage(eq(UID_10_2), eq(PACKAGE_2));
         reset(l);
 
         setAppOps(UID_10_2, PACKAGE_2, false);
 
         verify(l, times(0)).updateAllJobs();
         verify(l, times(0)).updateJobsForUid(anyInt());
-        verify(l, times(1)).updateJobsForUidPackage(eq(UID_10_2), eq(PACKAGE_2));
+        verify(l, times(0)).updateJobsForUidPackage(anyInt(), anyString());
 
         verify(l, times(0)).unblockAllUnrestrictedAlarms();
         verify(l, times(0)).unblockAlarmsForUid(anyInt());
@@ -634,10 +652,20 @@
         mPowerSaveMode = true;
         mPowerSaveObserver.accept(getPowerSaveState());
 
+        verify(l, times(1)).updateAllJobs();
+        verify(l, times(0)).updateJobsForUid(anyInt());
+        verify(l, times(0)).updateJobsForUidPackage(anyInt(), anyString());
+
+        verify(l, times(0)).unblockAllUnrestrictedAlarms();
+        verify(l, times(0)).unblockAlarmsForUid(anyInt());
+        verify(l, times(0)).unblockAlarmsForUidPackage(anyInt(), anyString());
+        reset(l);
+
         instance.setPowerSaveWhitelistAppIds(new int[] {UID_1, UID_2}, new int[] {});
 
         waitUntilMainHandlerDrain();
-        verify(l, times(1)).updateAllJobs();
+        // Called once for updating all whitelist and once for updating temp whitelist
+        verify(l, times(2)).updateAllJobs();
         verify(l, times(0)).updateJobsForUid(anyInt());
         verify(l, times(0)).updateJobsForUidPackage(anyInt(), anyString());
 
@@ -699,7 +727,7 @@
         verify(l, times(0)).updateJobsForUidPackage(anyInt(), anyString());
 
         verify(l, times(0)).unblockAllUnrestrictedAlarms();
-        verify(l, times(0)).unblockAlarmsForUid(anyInt());
+        verify(l, times(1)).unblockAlarmsForUid(eq(UID_10_1));
         verify(l, times(0)).unblockAlarmsForUidPackage(anyInt(), anyString());
         reset(l);
 
@@ -723,7 +751,7 @@
         verify(l, times(0)).updateJobsForUidPackage(anyInt(), anyString());
 
         verify(l, times(0)).unblockAllUnrestrictedAlarms();
-        verify(l, times(0)).unblockAlarmsForUid(anyInt());
+        verify(l, times(1)).unblockAlarmsForUid(eq(UID_10_1));
         verify(l, times(0)).unblockAlarmsForUidPackage(anyInt(), anyString());
         reset(l);
 
@@ -743,6 +771,15 @@
         mPowerSaveMode = false;
         mPowerSaveObserver.accept(getPowerSaveState());
 
+        verify(l, times(1)).updateAllJobs();
+        verify(l, times(0)).updateJobsForUid(eq(UID_10_1));
+        verify(l, times(0)).updateJobsForUidPackage(anyInt(), anyString());
+
+        verify(l, times(1)).unblockAllUnrestrictedAlarms();
+        verify(l, times(0)).unblockAlarmsForUid(anyInt());
+        verify(l, times(0)).unblockAlarmsForUidPackage(anyInt(), anyString());
+        reset(l);
+
         mIUidObserver.onUidActive(UID_10_1);
 
         waitUntilMainHandlerDrain();
@@ -751,7 +788,7 @@
         verify(l, times(0)).updateJobsForUidPackage(anyInt(), anyString());
 
         verify(l, times(0)).unblockAllUnrestrictedAlarms();
-        verify(l, times(0)).unblockAlarmsForUid(anyInt());
+        verify(l, times(1)).unblockAlarmsForUid(eq(UID_10_1));
         verify(l, times(0)).unblockAlarmsForUidPackage(anyInt(), anyString());
         reset(l);
 
@@ -775,7 +812,7 @@
         verify(l, times(0)).updateJobsForUidPackage(anyInt(), anyString());
 
         verify(l, times(0)).unblockAllUnrestrictedAlarms();
-        verify(l, times(0)).unblockAlarmsForUid(anyInt());
+        verify(l, times(1)).unblockAlarmsForUid(eq(UID_10_1));
         verify(l, times(0)).unblockAlarmsForUidPackage(anyInt(), anyString());
         reset(l);
 
@@ -822,6 +859,56 @@
         assertTrue(instance.isRunAnyInBackgroundAppOpsAllowed(UID_10_2, PACKAGE_2));
     }
 
+    @Test
+    public void testSmallBatteryAndPluggedIn() throws Exception {
+        // This is a small battery device
+        mIsSmallBatteryDevice = true;
+
+        final ForceAppStandbyTrackerTestable instance = newInstance();
+        callStart(instance);
+        assertFalse(instance.isForceAllAppsStandbyEnabled());
+
+        // Setting/experiment for all app standby for small battery is enabled
+        Global.putInt(mMockContentResolver, Global.FORCED_APP_STANDBY_FOR_SMALL_BATTERY_ENABLED, 1);
+        instance.mFlagsObserver.onChange(true,
+                Global.getUriFor(Global.FORCED_APP_STANDBY_FOR_SMALL_BATTERY_ENABLED));
+        assertTrue(instance.isForceAllAppsStandbyEnabled());
+
+        // When battery is plugged in, force app standby is disabled
+        Intent intent = new Intent(Intent.ACTION_BATTERY_CHANGED);
+        intent.putExtra(BatteryManager.EXTRA_PLUGGED, BatteryManager.BATTERY_PLUGGED_USB);
+        mReceiver.onReceive(mMockContext, intent);
+        assertFalse(instance.isForceAllAppsStandbyEnabled());
+
+        // When battery stops plugged in, force app standby is enabled
+        mReceiver.onReceive(mMockContext, new Intent(Intent.ACTION_BATTERY_CHANGED));
+        assertTrue(instance.isForceAllAppsStandbyEnabled());
+    }
+
+    @Test
+    public void testNotSmallBatteryAndPluggedIn() throws Exception {
+        // Not a small battery device, so plugged in status should not affect forced app standby
+        mIsSmallBatteryDevice = false;
+
+        final ForceAppStandbyTrackerTestable instance = newInstance();
+        callStart(instance);
+        assertFalse(instance.isForceAllAppsStandbyEnabled());
+
+        mPowerSaveMode = true;
+        mPowerSaveObserver.accept(getPowerSaveState());
+        assertTrue(instance.isForceAllAppsStandbyEnabled());
+
+        // When battery is plugged in, force app standby is unaffected
+        Intent intent = new Intent(Intent.ACTION_BATTERY_CHANGED);
+        intent.putExtra(BatteryManager.EXTRA_PLUGGED, BatteryManager.BATTERY_PLUGGED_USB);
+        mReceiver.onReceive(mMockContext, intent);
+        assertTrue(instance.isForceAllAppsStandbyEnabled());
+
+        // When battery stops plugged in, force app standby is unaffected
+        mReceiver.onReceive(mMockContext, new Intent(Intent.ACTION_BATTERY_CHANGED));
+        assertTrue(instance.isForceAllAppsStandbyEnabled());
+    }
+
     static int[] array(int... appIds) {
         Arrays.sort(appIds);
         return appIds;
diff --git a/services/tests/servicestests/src/com/android/server/NetworkPolicyManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/NetworkPolicyManagerServiceTest.java
index 7c3082f..045b73c 100644
--- a/services/tests/servicestests/src/com/android/server/NetworkPolicyManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/NetworkPolicyManagerServiceTest.java
@@ -134,7 +134,6 @@
 
 import org.junit.After;
 import org.junit.Before;
-import org.junit.Ignore;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.rules.MethodRule;
@@ -185,7 +184,6 @@
     "com.android.frameworks.servicestests/android.support.test.runner.AndroidJUnitRunner"
  * </code></pre>
  */
-@Ignore
 @RunWith(AndroidJUnit4.class)
 @MediumTest
 public class NetworkPolicyManagerServiceTest {
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/GlobalActionPerformerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/GlobalActionPerformerTest.java
index 1213e81..e72e460 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/GlobalActionPerformerTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/GlobalActionPerformerTest.java
@@ -16,13 +16,18 @@
 
 package com.android.server.accessibility;
 
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
 import android.accessibilityservice.AccessibilityService;
 import android.app.StatusBarManager;
 import android.content.Context;
+import android.os.Handler;
 
+import com.android.internal.util.ScreenshotHelper;
 import com.android.server.wm.WindowManagerInternal;
 
 import org.junit.Before;
@@ -39,6 +44,7 @@
     @Mock Context mMockContext;
     @Mock WindowManagerInternal mMockWindowManagerInternal;
     @Mock StatusBarManager mMockStatusBarManager;
+    @Mock ScreenshotHelper mMockScreenshotHelper;
 
     @Before
     public void setup() {
@@ -48,7 +54,8 @@
                 .thenReturn(mMockStatusBarManager);
 
         mGlobalActionPerformer =
-                new GlobalActionPerformer(mMockContext, mMockWindowManagerInternal);
+                new GlobalActionPerformer(mMockContext, mMockWindowManagerInternal,
+                        () -> mMockScreenshotHelper);
     }
 
     @Test
@@ -70,4 +77,13 @@
         mGlobalActionPerformer.performGlobalAction(AccessibilityService.GLOBAL_ACTION_POWER_DIALOG);
         verify(mMockWindowManagerInternal).showGlobalActions();
     }
+
+    @Test
+    public void testScreenshot_requestsFromScreenshotHelper() {
+        mGlobalActionPerformer.performGlobalAction(
+                AccessibilityService.GLOBAL_ACTION_TAKE_SCREENSHOT);
+        verify(mMockScreenshotHelper).takeScreenshot(
+                eq(android.view.WindowManager.TAKE_SCREENSHOT_FULLSCREEN), anyBoolean(),
+                anyBoolean(), any(Handler.class));
+    }
 }
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceTestable.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceTestable.java
index 5134f52..06f138b 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceTestable.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceTestable.java
@@ -45,6 +45,7 @@
 
 import com.android.internal.util.FunctionalUtils.ThrowingRunnable;
 import com.android.internal.widget.LockPatternUtils;
+import com.android.server.net.NetworkPolicyManagerInternal;
 
 import java.io.File;
 import java.io.IOException;
@@ -159,6 +160,11 @@
         }
 
         @Override
+        NetworkPolicyManagerInternal getNetworkPolicyManagerInternal() {
+            return services.networkPolicyManagerInternal;
+        }
+
+        @Override
         PackageManagerInternal getPackageManagerInternal() {
             return services.packageManagerInternal;
         }
@@ -438,5 +444,10 @@
         KeyChain.KeyChainConnection keyChainBindAsUser(UserHandle user) {
             return services.keyChainConnection;
         }
+
+        @Override
+        void postOnSystemServerInitThreadPool(Runnable runnable) {
+            runnable.run();
+        }
     }
 }
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
index 1df0ff2..725ede8 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
@@ -186,6 +186,7 @@
         initializeDpms();
 
         Mockito.reset(getServices().usageStatsManagerInternal);
+        Mockito.reset(getServices().networkPolicyManagerInternal);
         setUpPackageManagerForAdmin(admin1, DpmMockContext.CALLER_UID);
         setUpPackageManagerForAdmin(admin2, DpmMockContext.CALLER_UID);
         setUpPackageManagerForAdmin(admin3, DpmMockContext.CALLER_UID);
@@ -211,7 +212,6 @@
         LocalServices.removeServiceForTest(DevicePolicyManagerInternal.class);
 
         dpms = new DevicePolicyManagerServiceTestable(getServices(), mContext);
-        dpms.handleStart();
         dpms.systemReady(SystemService.PHASE_LOCK_SETTINGS_READY);
         dpms.systemReady(SystemService.PHASE_BOOT_COMPLETED);
 
@@ -283,7 +283,7 @@
         assertNull(LocalServices.getService(DevicePolicyManagerInternal.class));
     }
 
-    public void testHandleStart() throws Exception {
+    public void testLoadAdminData() throws Exception {
         // Device owner in SYSTEM_USER
         setDeviceOwner();
         // Profile owner in CALLER_USER_HANDLE
@@ -307,6 +307,23 @@
                 MockUtils.checkAdminApps(admin2.getPackageName(),
                         adminAnotherPackage.getPackageName()),
                 eq(DpmMockContext.CALLER_USER_HANDLE));
+        verify(getServices().usageStatsManagerInternal).onAdminDataAvailable();
+        verify(getServices().networkPolicyManagerInternal).onAdminDataAvailable();
+    }
+
+    public void testLoadAdminData_noAdmins() throws Exception {
+        final int ANOTHER_USER_ID = 15;
+        getServices().addUser(ANOTHER_USER_ID, 0);
+
+        initializeDpms();
+
+        // Verify
+        verify(getServices().usageStatsManagerInternal).setActiveAdminApps(
+                null, DpmMockContext.CALLER_USER_HANDLE);
+        verify(getServices().usageStatsManagerInternal).setActiveAdminApps(
+                null, ANOTHER_USER_ID);
+        verify(getServices().usageStatsManagerInternal).onAdminDataAvailable();
+        verify(getServices().networkPolicyManagerInternal).onAdminDataAvailable();
     }
 
     /**
@@ -2119,8 +2136,8 @@
         assertEquals(UserManager.DISALLOW_ADJUST_VOLUME,
                 intent.getStringExtra(DevicePolicyManager.EXTRA_RESTRICTION));
 
-        // Try with POLICY_DISABLE_CAMERA and POLICY_DISABLE_SCREEN_CAPTURE, which are not
-        // user restrictions
+        // Try with POLICY_DISABLE_CAMERA, POLICY_DISABLE_SCREEN_CAPTURE and
+        // POLICY_MANDATORY_BACKUPS, which are not user restrictions
 
         // Camera is not disabled
         intent = dpm.createAdminSupportIntent(DevicePolicyManager.POLICY_DISABLE_CAMERA);
@@ -2144,6 +2161,19 @@
         assertEquals(DevicePolicyManager.POLICY_DISABLE_SCREEN_CAPTURE,
                 intent.getStringExtra(DevicePolicyManager.EXTRA_RESTRICTION));
 
+        // Backups are not mandatory
+        intent = dpm.createAdminSupportIntent(DevicePolicyManager.POLICY_MANDATORY_BACKUPS);
+        assertNull(intent);
+
+        // Backups are mandatory
+        ComponentName transportComponent = ComponentName.unflattenFromString(
+                "android/com.android.internal.backup.LocalTransport");
+        dpm.setMandatoryBackupTransport(admin1, transportComponent);
+        intent = dpm.createAdminSupportIntent(DevicePolicyManager.POLICY_MANDATORY_BACKUPS);
+        assertNotNull(intent);
+        assertEquals(DevicePolicyManager.POLICY_MANDATORY_BACKUPS,
+                intent.getStringExtra(DevicePolicyManager.EXTRA_RESTRICTION));
+
         // Same checks for different user
         mContext.binder.callingUid = DpmMockContext.CALLER_UID;
         // Camera should be disabled by device owner
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/MockSystemServices.java b/services/tests/servicestests/src/com/android/server/devicepolicy/MockSystemServices.java
index 268d424..0343a52 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/MockSystemServices.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/MockSystemServices.java
@@ -58,6 +58,7 @@
 
 import com.android.internal.util.test.FakeSettingsProvider;
 import com.android.internal.widget.LockPatternUtils;
+import com.android.server.net.NetworkPolicyManagerInternal;
 
 import java.io.File;
 import java.io.IOException;
@@ -76,6 +77,7 @@
     public final UserManager userManager;
     public final UserManagerInternal userManagerInternal;
     public final UsageStatsManagerInternal usageStatsManagerInternal;
+    public final NetworkPolicyManagerInternal networkPolicyManagerInternal;
     public final PackageManagerInternal packageManagerInternal;
     public final UserManagerForMock userManagerForMock;
     public final PowerManagerForMock powerManager;
@@ -113,6 +115,8 @@
         userManager = mock(UserManager.class);
         userManagerInternal = mock(UserManagerInternal.class);
         usageStatsManagerInternal = mock(UsageStatsManagerInternal.class);
+        networkPolicyManagerInternal = mock(NetworkPolicyManagerInternal.class);
+
         userManagerForMock = mock(UserManagerForMock.class);
         packageManagerInternal = mock(PackageManagerInternal.class);
         powerManager = mock(PowerManagerForMock.class);
diff --git a/services/tests/servicestests/src/com/android/server/net/watchlist/PrivacyUtilsTests.java b/services/tests/servicestests/src/com/android/server/net/watchlist/PrivacyUtilsTests.java
index a31b46c..999dce5 100644
--- a/services/tests/servicestests/src/com/android/server/net/watchlist/PrivacyUtilsTests.java
+++ b/services/tests/servicestests/src/com/android/server/net/watchlist/PrivacyUtilsTests.java
@@ -77,9 +77,9 @@
         assertEquals(6, result.size());
         assertTrue(result.get("C86F9D37425340B635F43D6BC2506630761ADA71F5E6BBDBCA4651C479F9FB48"));
         assertTrue(result.get("C86F9D37425340B635F43D6BC2506630761ADA71F5E6BBDBCA4651C479F9FB49"));
-        assertFalse(result.get("C86F9D37425340B635F43D6BC2506630761ADA71F5E6BBDBCA4651C479F9FB47"));
-        assertTrue(result.get("E86F9D37425340B635F43D6BC2506630761ADA71F5E6BBDBCA4651C479F9FB45"));
-        assertFalse(result.get("C86F9D37425340B635F43D6BC2506630761ADA71F5E6BBDBCA4651C479F9FB44"));
+        assertTrue(result.get("C86F9D37425340B635F43D6BC2506630761ADA71F5E6BBDBCA4651C479F9FB47"));
+        assertFalse(result.get("E86F9D37425340B635F43D6BC2506630761ADA71F5E6BBDBCA4651C479F9FB45"));
+        assertTrue(result.get("C86F9D37425340B635F43D6BC2506630761ADA71F5E6BBDBCA4651C479F9FB44"));
         assertTrue(result.get("B86F9D37425340B635F43D6BC2506630761ADA71F5E6BBDBCA4651C479F9FB43"));
     }
 
@@ -87,7 +87,7 @@
     public void testPrivacyUtils_createInsecureDPEncoderForTest() throws Exception {
         DifferentialPrivacyEncoder encoder = PrivacyUtils.createInsecureDPEncoderForTest("foo");
         assertEquals(
-                "EncoderId: watchlist_encoder:foo, ProbabilityF: 0.400, ProbabilityP: 0.250, "
+                "EncoderId: watchlist_encoder:foo, ProbabilityF: 0.469, ProbabilityP: 0.280, "
                         + "ProbabilityQ: 1.000",
                 encoder.getConfig().toString());
         assertTrue(encoder.isInsecureEncoderForTest());
@@ -97,7 +97,7 @@
     public void testPrivacyUtils_createSecureDPEncoderTest() throws Exception {
         DifferentialPrivacyEncoder encoder = PrivacyUtils.createSecureDPEncoder(TEST_SECRET, "foo");
         assertEquals(
-                "EncoderId: watchlist_encoder:foo, ProbabilityF: 0.400, ProbabilityP: 0.250, "
+                "EncoderId: watchlist_encoder:foo, ProbabilityF: 0.469, ProbabilityP: 0.280, "
                         + "ProbabilityQ: 1.000",
                 encoder.getConfig().toString());
         assertFalse(encoder.isInsecureEncoderForTest());
diff --git a/services/tests/servicestests/src/com/android/server/net/watchlist/WatchlistConfigTests.java b/services/tests/servicestests/src/com/android/server/net/watchlist/WatchlistConfigTests.java
index 851d2c6..654acc2 100644
--- a/services/tests/servicestests/src/com/android/server/net/watchlist/WatchlistConfigTests.java
+++ b/services/tests/servicestests/src/com/android/server/net/watchlist/WatchlistConfigTests.java
@@ -33,12 +33,15 @@
 import org.junit.runner.RunWith;
 
 import java.io.BufferedReader;
+import java.io.ByteArrayOutputStream;
 import java.io.File;
 import java.io.FileWriter;
 import java.io.IOException;
 import java.io.InputStreamReader;
+import java.io.PrintWriter;
 import java.util.Arrays;
 
+
 /**
  * runtest frameworks-services -c com.android.server.net.watchlist.WatchlistConfigTests
  */
@@ -117,6 +120,15 @@
         assertEquals(TEST_XML_1_HASH, HexDump.toHexString(config.getWatchlistConfigHash()));
     }
 
+    @Test
+    public void testWatchlistConfig_testDumpDoesNotCrash() throws Exception {
+        WatchlistConfig config = new WatchlistConfig(new File("/not_exist_path.xml"));
+        ByteArrayOutputStream bs = new ByteArrayOutputStream(2048);
+        PrintWriter pw = new PrintWriter(bs);
+        // Make sure dump still works even watchlist does not exist
+        config.dump(null, pw, null);
+    }
+
     private static void copyWatchlistConfigXml(Context context, String xmlAsset, File outFile)
             throws IOException {
         writeToFile(outFile, readAsset(context, xmlAsset));
diff --git a/services/tests/servicestests/src/com/android/server/policy/PhoneWindowManagerLayoutTest.java b/services/tests/servicestests/src/com/android/server/policy/PhoneWindowManagerLayoutTest.java
index b6c370e..293f9af 100644
--- a/services/tests/servicestests/src/com/android/server/policy/PhoneWindowManagerLayoutTest.java
+++ b/services/tests/servicestests/src/com/android/server/policy/PhoneWindowManagerLayoutTest.java
@@ -27,14 +27,19 @@
 import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
 import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_NEVER;
 import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_FORCE_DRAW_STATUS_BAR_BACKGROUND;
+import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_IS_SCREEN_DECOR;
 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
 
+import static org.hamcrest.Matchers.equalTo;
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertThat;
 
 import android.graphics.PixelFormat;
+import android.graphics.Rect;
 import android.platform.test.annotations.Presubmit;
 import android.support.test.filters.SmallTest;
 import android.support.test.runner.AndroidJUnit4;
+import android.view.DisplayCutout;
 import android.view.WindowManager;
 
 import org.junit.Before;
@@ -262,4 +267,23 @@
         assertInsetBy(mAppWindow.decorFrame, 0, 0, 0, 0);
     }
 
+    @Test
+    public void insetHint_screenDecorWindow() {
+        addDisplayCutout();
+        mAppWindow.attrs.privateFlags |= PRIVATE_FLAG_IS_SCREEN_DECOR;
+
+        mPolicy.beginLayoutLw(mFrames, 0 /* UI mode */);
+
+        final Rect content = new Rect();
+        final Rect stable = new Rect();
+        final Rect outsets = new Rect();
+        final DisplayCutout.ParcelableWrapper cutout = new DisplayCutout.ParcelableWrapper();
+        mPolicy.getInsetHintLw(mAppWindow.attrs, null /* taskBounds */, mFrames, content,
+                stable, outsets, cutout);
+
+        assertThat(content, equalTo(new Rect()));
+        assertThat(stable, equalTo(new Rect()));
+        assertThat(outsets, equalTo(new Rect()));
+        assertThat(cutout.get(), equalTo(DisplayCutout.NO_CUTOUT));
+    }
 }
\ No newline at end of file
diff --git a/services/tests/servicestests/src/com/android/server/wm/TestWindowManagerPolicy.java b/services/tests/servicestests/src/com/android/server/wm/TestWindowManagerPolicy.java
index 5ed17cc..35ca493 100644
--- a/services/tests/servicestests/src/com/android/server/wm/TestWindowManagerPolicy.java
+++ b/services/tests/servicestests/src/com/android/server/wm/TestWindowManagerPolicy.java
@@ -539,7 +539,7 @@
     }
 
     @Override
-    public void showRecentApps(boolean fromHome) {
+    public void showRecentApps() {
 
     }
 
diff --git a/services/tests/servicestests/src/com/android/server/wm/ZOrderingTests.java b/services/tests/servicestests/src/com/android/server/wm/ZOrderingTests.java
index 6468763..5f44fb6 100644
--- a/services/tests/servicestests/src/com/android/server/wm/ZOrderingTests.java
+++ b/services/tests/servicestests/src/com/android/server/wm/ZOrderingTests.java
@@ -17,14 +17,17 @@
 package com.android.server.wm;
 
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_ASSISTANT;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
 import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
 import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
 import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
+import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_ATTACHED_DIALOG;
 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_MEDIA;
 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_MEDIA_OVERLAY;
 import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
+import static android.view.WindowManager.LayoutParams.TYPE_DOCK_DIVIDER;
 import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL;
 import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR_PANEL;
 import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR_SUB_PANEL;
@@ -74,11 +77,11 @@
             return super.setRelativeLayer(sc, relativeTo, layer);
         }
 
-        int getLayer(SurfaceControl sc) {
+        private int getLayer(SurfaceControl sc) {
             return mLayersForControl.getOrDefault(sc, 0);
         }
 
-        SurfaceControl getRelativeLayer(SurfaceControl sc) {
+        private SurfaceControl getRelativeLayer(SurfaceControl sc) {
             return mRelativeLayersForControl.get(sc);
         }
     };
@@ -146,8 +149,9 @@
         return p;
     }
 
-    void assertZOrderGreaterThan(LayerRecordingTransaction t,
-            SurfaceControl left, SurfaceControl right) throws Exception {
+
+    void assertZOrderGreaterThan(LayerRecordingTransaction t, SurfaceControl left,
+            SurfaceControl right) throws Exception {
         final LinkedList<SurfaceControl> leftParentChain = getAncestors(t, left);
         final LinkedList<SurfaceControl> rightParentChain = getAncestors(t, right);
 
@@ -171,9 +175,12 @@
         }
     }
 
-    void assertWindowLayerGreaterThan(LayerRecordingTransaction t,
-            WindowState left, WindowState right) throws Exception {
-        assertZOrderGreaterThan(t, left.getSurfaceControl(), right.getSurfaceControl());
+    void assertWindowHigher(WindowState left, WindowState right) throws Exception {
+        assertZOrderGreaterThan(mTransaction, left.getSurfaceControl(), right.getSurfaceControl());
+    }
+
+    WindowState createWindow(String name) {
+        return createWindow(null, TYPE_BASE_APPLICATION, mDisplayContent, name);
     }
 
     @Test
@@ -184,38 +191,37 @@
         // The Ime has an higher base layer than app windows and lower base layer than system
         // windows, so it should be above app windows and below system windows if there isn't an IME
         // target.
-        assertWindowLayerGreaterThan(mTransaction, mImeWindow, mChildAppWindowAbove);
-        assertWindowLayerGreaterThan(mTransaction, mImeWindow, mAppWindow);
-        assertWindowLayerGreaterThan(mTransaction, mNavBarWindow, mImeWindow);
-        assertWindowLayerGreaterThan(mTransaction, mStatusBarWindow, mImeWindow);
+        assertWindowHigher(mImeWindow, mChildAppWindowAbove);
+        assertWindowHigher(mImeWindow, mAppWindow);
+        assertWindowHigher(mNavBarWindow, mImeWindow);
+        assertWindowHigher(mStatusBarWindow, mImeWindow);
 
         // And, IME dialogs should always have an higher layer than the IME.
-        assertWindowLayerGreaterThan(mTransaction, mImeDialogWindow, mImeWindow);
+        assertWindowHigher(mImeDialogWindow, mImeWindow);
     }
 
     @Test
     public void testAssignWindowLayers_ForImeWithAppTarget() throws Exception {
-        final WindowState imeAppTarget =
-                createWindow(null, TYPE_BASE_APPLICATION, mDisplayContent, "imeAppTarget");
+        final WindowState imeAppTarget = createWindow("imeAppTarget");
         sWm.mInputMethodTarget = imeAppTarget;
+
         mDisplayContent.assignChildLayers(mTransaction);
 
         // Ime should be above all app windows and below system windows if it is targeting an app
         // window.
-        assertWindowLayerGreaterThan(mTransaction, mImeWindow, imeAppTarget);
-        assertWindowLayerGreaterThan(mTransaction, mImeWindow, mChildAppWindowAbove);
-        assertWindowLayerGreaterThan(mTransaction, mImeWindow, mAppWindow);
-        assertWindowLayerGreaterThan(mTransaction, mNavBarWindow, mImeWindow);
-        assertWindowLayerGreaterThan(mTransaction, mStatusBarWindow, mImeWindow);
+        assertWindowHigher(mImeWindow, imeAppTarget);
+        assertWindowHigher(mImeWindow, mChildAppWindowAbove);
+        assertWindowHigher(mImeWindow, mAppWindow);
+        assertWindowHigher(mNavBarWindow, mImeWindow);
+        assertWindowHigher(mStatusBarWindow, mImeWindow);
 
         // And, IME dialogs should always have an higher layer than the IME.
-        assertWindowLayerGreaterThan(mTransaction, mImeDialogWindow, mImeWindow);
+        assertWindowHigher(mImeDialogWindow, mImeWindow);
     }
 
     @Test
     public void testAssignWindowLayers_ForImeWithAppTargetWithChildWindows() throws Exception {
-        final WindowState imeAppTarget =
-                createWindow(null, TYPE_BASE_APPLICATION, mDisplayContent, "imeAppTarget");
+        final WindowState imeAppTarget = createWindow("imeAppTarget");
         final WindowState imeAppTargetChildAboveWindow = createWindow(imeAppTarget,
                 TYPE_APPLICATION_ATTACHED_DIALOG, imeAppTarget.mToken,
                 "imeAppTargetChildAboveWindow");
@@ -228,41 +234,38 @@
 
         // Ime should be above all app windows except for child windows that are z-ordered above it
         // and below system windows if it is targeting an app window.
-        assertWindowLayerGreaterThan(mTransaction, mImeWindow, imeAppTarget);
-        assertWindowLayerGreaterThan(mTransaction, imeAppTargetChildAboveWindow, mImeWindow);
-        assertWindowLayerGreaterThan(mTransaction, mImeWindow, mChildAppWindowAbove);
-        assertWindowLayerGreaterThan(mTransaction, mImeWindow, mAppWindow);
-        assertWindowLayerGreaterThan(mTransaction, mNavBarWindow, mImeWindow);
-        assertWindowLayerGreaterThan(mTransaction, mStatusBarWindow, mImeWindow);
+        assertWindowHigher(mImeWindow, imeAppTarget);
+        assertWindowHigher(imeAppTargetChildAboveWindow, mImeWindow);
+        assertWindowHigher(mImeWindow, mChildAppWindowAbove);
+        assertWindowHigher(mImeWindow, mAppWindow);
+        assertWindowHigher(mNavBarWindow, mImeWindow);
+        assertWindowHigher(mStatusBarWindow, mImeWindow);
 
         // And, IME dialogs should always have an higher layer than the IME.
-        assertWindowLayerGreaterThan(mTransaction, mImeDialogWindow, mImeWindow);
+        assertWindowHigher(mImeDialogWindow, mImeWindow);
     }
 
     @Test
     public void testAssignWindowLayers_ForImeWithAppTargetAndAppAbove() throws Exception {
-        final WindowState appBelowImeTarget =
-                createWindow(null, TYPE_BASE_APPLICATION, mDisplayContent, "appBelowImeTarget");
-        final WindowState imeAppTarget =
-                createWindow(null, TYPE_BASE_APPLICATION, mDisplayContent, "imeAppTarget");
-        final WindowState appAboveImeTarget =
-                createWindow(null, TYPE_BASE_APPLICATION, mDisplayContent, "appAboveImeTarget");
+        final WindowState appBelowImeTarget = createWindow("appBelowImeTarget");
+        final WindowState imeAppTarget = createWindow("imeAppTarget");
+        final WindowState appAboveImeTarget = createWindow("appAboveImeTarget");
 
         sWm.mInputMethodTarget = imeAppTarget;
         mDisplayContent.assignChildLayers(mTransaction);
 
         // Ime should be above all app windows except for non-fullscreen app window above it and
         // below system windows if it is targeting an app window.
-        assertWindowLayerGreaterThan(mTransaction, mImeWindow, imeAppTarget);
-        assertWindowLayerGreaterThan(mTransaction, mImeWindow, appBelowImeTarget);
-        assertWindowLayerGreaterThan(mTransaction, appAboveImeTarget, mImeWindow);
-        assertWindowLayerGreaterThan(mTransaction, mImeWindow, mChildAppWindowAbove);
-        assertWindowLayerGreaterThan(mTransaction, mImeWindow, mAppWindow);
-        assertWindowLayerGreaterThan(mTransaction, mNavBarWindow, mImeWindow);
-        assertWindowLayerGreaterThan(mTransaction, mStatusBarWindow, mImeWindow);
+        assertWindowHigher(mImeWindow, imeAppTarget);
+        assertWindowHigher(mImeWindow, appBelowImeTarget);
+        assertWindowHigher(appAboveImeTarget, mImeWindow);
+        assertWindowHigher(mImeWindow, mChildAppWindowAbove);
+        assertWindowHigher(mImeWindow, mAppWindow);
+        assertWindowHigher(mNavBarWindow, mImeWindow);
+        assertWindowHigher(mStatusBarWindow, mImeWindow);
 
         // And, IME dialogs should always have an higher layer than the IME.
-        assertWindowLayerGreaterThan(mTransaction, mImeDialogWindow, mImeWindow);
+        assertWindowHigher(mImeDialogWindow, mImeWindow);
     }
 
     @Test
@@ -276,20 +279,20 @@
 
         // The IME target base layer is higher than all window except for the nav bar window, so the
         // IME should be above all windows except for the nav bar.
-        assertWindowLayerGreaterThan(mTransaction, mImeWindow, imeSystemOverlayTarget);
-        assertWindowLayerGreaterThan(mTransaction, mImeWindow, mChildAppWindowAbove);
-        assertWindowLayerGreaterThan(mTransaction, mImeWindow, mAppWindow);
-        assertWindowLayerGreaterThan(mTransaction, mImeWindow, mDockedDividerWindow);
+        assertWindowHigher(mImeWindow, imeSystemOverlayTarget);
+        assertWindowHigher(mImeWindow, mChildAppWindowAbove);
+        assertWindowHigher(mImeWindow, mAppWindow);
+        assertWindowHigher(mImeWindow, mDockedDividerWindow);
 
         // The IME has a higher base layer than the status bar so we may expect it to go
         // above the status bar once they are both in the Non-App layer, as past versions of this
         // test enforced. However this seems like the wrong behavior unless the status bar is the
         // IME target.
-        assertWindowLayerGreaterThan(mTransaction, mNavBarWindow, mImeWindow);
-        assertWindowLayerGreaterThan(mTransaction, mStatusBarWindow, mImeWindow);
+        assertWindowHigher(mNavBarWindow, mImeWindow);
+        assertWindowHigher(mStatusBarWindow, mImeWindow);
 
         // And, IME dialogs should always have an higher layer than the IME.
-        assertWindowLayerGreaterThan(mTransaction, mImeDialogWindow, mImeWindow);
+        assertWindowHigher(mImeDialogWindow, mImeWindow);
     }
 
     @Test
@@ -297,17 +300,18 @@
         sWm.mInputMethodTarget = mStatusBarWindow;
         mDisplayContent.assignChildLayers(mTransaction);
 
-        assertWindowLayerGreaterThan(mTransaction, mImeWindow, mChildAppWindowAbove);
-        assertWindowLayerGreaterThan(mTransaction, mImeWindow, mAppWindow);
-        assertWindowLayerGreaterThan(mTransaction, mImeWindow, mDockedDividerWindow);
-        assertWindowLayerGreaterThan(mTransaction, mImeWindow, mStatusBarWindow);
+        assertWindowHigher(mImeWindow, mChildAppWindowAbove);
+        assertWindowHigher(mImeWindow, mAppWindow);
+        assertWindowHigher(mImeWindow, mDockedDividerWindow);
+        assertWindowHigher(mImeWindow, mStatusBarWindow);
 
         // And, IME dialogs should always have an higher layer than the IME.
-        assertWindowLayerGreaterThan(mTransaction, mImeDialogWindow, mImeWindow);
+        assertWindowHigher(mImeDialogWindow, mImeWindow);
     }
 
     @Test
     public void testStackLayers() throws Exception {
+        final WindowState anyWindow1 = createWindow("anyWindow");
         final WindowState pinnedStackWindow = createWindowOnStack(null, WINDOWING_MODE_PINNED,
                 ACTIVITY_TYPE_STANDARD, TYPE_BASE_APPLICATION, mDisplayContent,
                 "pinnedStackWindow");
@@ -317,12 +321,22 @@
         final WindowState assistantStackWindow = createWindowOnStack(null, WINDOWING_MODE_FULLSCREEN,
                 ACTIVITY_TYPE_ASSISTANT, TYPE_BASE_APPLICATION,
                 mDisplayContent, "assistantStackWindow");
+        final WindowState homeActivityWindow = createWindowOnStack(null, WINDOWING_MODE_FULLSCREEN,
+                ACTIVITY_TYPE_HOME, TYPE_BASE_APPLICATION,
+                mDisplayContent, "homeActivityWindow");
+        final WindowState anyWindow2 = createWindow("anyWindow2");
 
         mDisplayContent.assignChildLayers(mTransaction);
 
-        assertWindowLayerGreaterThan(mTransaction, dockedStackWindow, mAppWindow);
-        assertWindowLayerGreaterThan(mTransaction, assistantStackWindow, dockedStackWindow);
-        assertWindowLayerGreaterThan(mTransaction, pinnedStackWindow, assistantStackWindow);
+        assertWindowHigher(dockedStackWindow, homeActivityWindow);
+        assertWindowHigher(assistantStackWindow, homeActivityWindow);
+        assertWindowHigher(pinnedStackWindow, homeActivityWindow);
+        assertWindowHigher(anyWindow1, homeActivityWindow);
+        assertWindowHigher(anyWindow2, homeActivityWindow);
+        assertWindowHigher(pinnedStackWindow, anyWindow1);
+        assertWindowHigher(pinnedStackWindow, anyWindow2);
+        assertWindowHigher(pinnedStackWindow, dockedStackWindow);
+        assertWindowHigher(pinnedStackWindow, assistantStackWindow);
     }
 
     @Test
@@ -337,9 +351,9 @@
 
         // Ime should be above all app windows and below system windows if it is targeting an app
         // window.
-        assertWindowLayerGreaterThan(mTransaction, navBarPanel, mNavBarWindow);
-        assertWindowLayerGreaterThan(mTransaction, statusBarPanel, mStatusBarWindow);
-        assertWindowLayerGreaterThan(mTransaction, statusBarSubPanel, statusBarPanel);
+        assertWindowHigher(navBarPanel, mNavBarWindow);
+        assertWindowHigher(statusBarPanel, mStatusBarWindow);
+        assertWindowHigher(statusBarSubPanel, statusBarPanel);
     }
 
     @Test
@@ -347,8 +361,7 @@
         // TODO(b/70040778): We should aim to eliminate the last user of TYPE_APPLICATION_MEDIA
         // then we can drop all negative layering on the windowing side.
 
-        final WindowState anyWindow =
-                createWindow(null, TYPE_BASE_APPLICATION, mDisplayContent, "anyWindow");
+        final WindowState anyWindow = createWindow("anyWindow");
         final WindowState child = createWindow(anyWindow, TYPE_APPLICATION_MEDIA, mDisplayContent,
                 "TypeApplicationMediaChild");
         final WindowState mediaOverlayChild = createWindow(anyWindow, TYPE_APPLICATION_MEDIA_OVERLAY,
@@ -356,7 +369,29 @@
 
         mDisplayContent.assignChildLayers(mTransaction);
 
-        assertWindowLayerGreaterThan(mTransaction, anyWindow, mediaOverlayChild);
-        assertWindowLayerGreaterThan(mTransaction, mediaOverlayChild, child);
+        assertWindowHigher(anyWindow, mediaOverlayChild);
+        assertWindowHigher(mediaOverlayChild, child);
+    }
+
+    @Test
+    public void testDockedDividerPosition() throws Exception {
+        final WindowState pinnedStackWindow = createWindowOnStack(null, WINDOWING_MODE_PINNED,
+                ACTIVITY_TYPE_STANDARD, TYPE_BASE_APPLICATION, mDisplayContent,
+                "pinnedStackWindow");
+        final WindowState splitScreenWindow = createWindowOnStack(null,
+                WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, ACTIVITY_TYPE_STANDARD, TYPE_BASE_APPLICATION,
+                mDisplayContent, "splitScreenWindow");
+        final WindowState splitScreenSecondaryWindow = createWindowOnStack(null,
+                WINDOWING_MODE_SPLIT_SCREEN_SECONDARY, ACTIVITY_TYPE_STANDARD,
+                TYPE_BASE_APPLICATION, mDisplayContent, "splitScreenSecondaryWindow");
+        final WindowState assistantStackWindow = createWindowOnStack(null, WINDOWING_MODE_FULLSCREEN,
+                ACTIVITY_TYPE_ASSISTANT, TYPE_BASE_APPLICATION,
+                mDisplayContent, "assistantStackWindow");
+
+        mDisplayContent.assignChildLayers(mTransaction);
+
+        assertWindowHigher(mDockedDividerWindow, splitScreenWindow);
+        assertWindowHigher(mDockedDividerWindow, splitScreenSecondaryWindow);
+        assertWindowHigher(pinnedStackWindow, mDockedDividerWindow);
     }
 }
diff --git a/services/tests/uiservicestests/AndroidManifest.xml b/services/tests/uiservicestests/AndroidManifest.xml
index f022dcf..3475572 100644
--- a/services/tests/uiservicestests/AndroidManifest.xml
+++ b/services/tests/uiservicestests/AndroidManifest.xml
@@ -25,6 +25,7 @@
     <uses-permission android:name="android.permission.ACCESS_NOTIFICATIONS" />
     <uses-permission android:name="android.permission.READ_CONTACTS" />
     <uses-permission android:name="android.permission.STATUS_BAR_SERVICE" />
+    <uses-permission android:name="android.permission.ACCESS_VOICE_INTERACTION_SERVICE" />
 
     <application>
         <uses-library android:name="android.test.runner" />
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
index ad3fecf..e0bebee 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -73,6 +73,7 @@
 import android.service.notification.Adjustment;
 import android.service.notification.NotificationListenerService;
 import android.service.notification.NotificationStats;
+import android.service.notification.NotifyingApp;
 import android.service.notification.StatusBarNotification;
 import android.test.suitebuilder.annotation.SmallTest;
 import android.testing.AndroidTestingRunner;
@@ -105,8 +106,10 @@
 import java.io.FileOutputStream;
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
+import java.util.Set;
 
 @SmallTest
 @RunWith(AndroidTestingRunner.class)
@@ -253,10 +256,19 @@
         mFile.delete();
     }
 
-    public void waitForIdle() throws Exception {
+    public void waitForIdle() {
         mTestableLooper.processAllMessages();
     }
 
+    private StatusBarNotification generateSbn(String pkg, int uid, long postTime, int userId) {
+        Notification.Builder nb = new Notification.Builder(mContext, "a")
+                .setContentTitle("foo")
+                .setSmallIcon(android.R.drawable.sym_def_app_icon);
+        StatusBarNotification sbn = new StatusBarNotification(pkg, pkg, uid, "tag", uid, 0,
+                nb.build(), new UserHandle(userId), null, postTime);
+        return sbn;
+    }
+
     private NotificationRecord generateNotificationRecord(NotificationChannel channel, int id,
             String groupKey, boolean isSummary) {
         Notification.Builder nb = new Notification.Builder(mContext, channel.getId())
@@ -2291,4 +2303,102 @@
 
         verify(handler, timeout(300).times(1)).scheduleSendRankingUpdate();
     }
+
+    @Test
+    public void testRecents() throws Exception {
+        Set<NotifyingApp> expected = new HashSet<>();
+
+        final NotificationRecord oldest = new NotificationRecord(mContext,
+                generateSbn("p", 1000, 9, 0), mTestNotificationChannel);
+        mService.logRecentLocked(oldest);
+        for (int i = 1; i <= 5; i++) {
+            NotificationRecord r = new NotificationRecord(mContext,
+                    generateSbn("p" + i, i, i*100, 0), mTestNotificationChannel);
+            expected.add(new NotifyingApp()
+                    .setPackage(r.sbn.getPackageName())
+                    .setUid(r.sbn.getUid())
+                    .setLastNotified(r.sbn.getPostTime()));
+            mService.logRecentLocked(r);
+        }
+
+        List<NotifyingApp> apps = mBinderService.getRecentNotifyingAppsForUser(0).getList();
+        assertTrue(apps.size() == 5);
+        for (NotifyingApp actual : apps) {
+            assertTrue("got unexpected result: " + actual, expected.contains(actual));
+        }
+    }
+
+    @Test
+    public void testRecentsNoDuplicatePackages() throws Exception {
+        final NotificationRecord p1 = new NotificationRecord(mContext, generateSbn("p", 1, 1000, 0),
+                mTestNotificationChannel);
+        final NotificationRecord p2 = new NotificationRecord(mContext, generateSbn("p", 1, 2000, 0),
+                mTestNotificationChannel);
+
+        mService.logRecentLocked(p1);
+        mService.logRecentLocked(p2);
+
+        List<NotifyingApp> apps = mBinderService.getRecentNotifyingAppsForUser(0).getList();
+        assertTrue(apps.size() == 1);
+        NotifyingApp expected = new NotifyingApp().setPackage("p").setUid(1).setLastNotified(2000);
+        assertEquals(expected, apps.get(0));
+    }
+
+    @Test
+    public void testRecentsWithDuplicatePackage() throws Exception {
+        Set<NotifyingApp> expected = new HashSet<>();
+
+        final NotificationRecord oldest = new NotificationRecord(mContext,
+                generateSbn("p", 1000, 9, 0), mTestNotificationChannel);
+        mService.logRecentLocked(oldest);
+        for (int i = 1; i <= 5; i++) {
+            NotificationRecord r = new NotificationRecord(mContext,
+                    generateSbn("p" + i, i, i*100, 0), mTestNotificationChannel);
+            expected.add(new NotifyingApp()
+                    .setPackage(r.sbn.getPackageName())
+                    .setUid(r.sbn.getUid())
+                    .setLastNotified(r.sbn.getPostTime()));
+            mService.logRecentLocked(r);
+        }
+        NotificationRecord r = new NotificationRecord(mContext,
+                generateSbn("p" + 3, 3, 300000, 0), mTestNotificationChannel);
+        expected.remove(new NotifyingApp()
+                .setPackage(r.sbn.getPackageName())
+                .setUid(3)
+                .setLastNotified(300));
+        NotifyingApp newest = new NotifyingApp()
+                .setPackage(r.sbn.getPackageName())
+                .setUid(r.sbn.getUid())
+                .setLastNotified(r.sbn.getPostTime());
+        expected.add(newest);
+        mService.logRecentLocked(r);
+
+        List<NotifyingApp> apps = mBinderService.getRecentNotifyingAppsForUser(0).getList();
+        assertTrue(apps.size() == 5);
+        for (NotifyingApp actual : apps) {
+            assertTrue("got unexpected result: " + actual, expected.contains(actual));
+        }
+        assertEquals(newest, apps.get(0));
+    }
+
+    @Test
+    public void testRecentsMultiuser() throws Exception {
+        final NotificationRecord user1 = new NotificationRecord(mContext,
+                generateSbn("p", 1000, 9, 1), mTestNotificationChannel);
+        mService.logRecentLocked(user1);
+
+        final NotificationRecord user2 = new NotificationRecord(mContext,
+                generateSbn("p2", 100000, 9999, 2), mTestNotificationChannel);
+        mService.logRecentLocked(user2);
+
+        assertEquals(0, mBinderService.getRecentNotifyingAppsForUser(0).getList().size());
+        assertEquals(1, mBinderService.getRecentNotifyingAppsForUser(1).getList().size());
+        assertEquals(1, mBinderService.getRecentNotifyingAppsForUser(2).getList().size());
+
+        assertTrue(mBinderService.getRecentNotifyingAppsForUser(2).getList().contains(
+                new NotifyingApp()
+                        .setPackage(user2.sbn.getPackageName())
+                        .setUid(user2.sbn.getUid())
+                        .setLastNotified(user2.sbn.getPostTime())));
+    }
 }
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationStatsTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationStatsTest.java
index 4f153ee..0a630f4 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationStatsTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationStatsTest.java
@@ -1,3 +1,18 @@
+/**
+ * Copyright (c) 2017, 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.notification;
 
 import static android.service.notification.NotificationStats.DISMISSAL_PEEK;
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotifyingAppTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotifyingAppTest.java
new file mode 100644
index 0000000..fbb8c33
--- /dev/null
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotifyingAppTest.java
@@ -0,0 +1,94 @@
+/**
+ * Copyright (c) 2018, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.server.notification;
+
+import static junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.assertTrue;
+
+import android.os.Parcel;
+import android.service.notification.NotifyingApp;
+import android.support.test.runner.AndroidJUnit4;
+import android.test.suitebuilder.annotation.SmallTest;
+
+import com.android.server.UiServiceTestCase;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class NotifyingAppTest extends UiServiceTestCase {
+
+    @Test
+    public void testConstructor() {
+        NotifyingApp na = new NotifyingApp();
+        assertEquals(0, na.getUid());
+        assertEquals(0, na.getLastNotified());
+        assertEquals(null, na.getPackage());
+    }
+
+    @Test
+    public void testPackage() {
+        NotifyingApp na = new NotifyingApp();
+        na.setPackage("test");
+        assertEquals("test", na.getPackage());
+    }
+
+    @Test
+    public void testUid() {
+        NotifyingApp na = new NotifyingApp();
+        na.setUid(90);
+        assertEquals(90, na.getUid());
+    }
+
+    @Test
+    public void testLastNotified() {
+        NotifyingApp na = new NotifyingApp();
+        na.setLastNotified((long) 8000);
+        assertEquals((long) 8000, na.getLastNotified());
+    }
+
+    @Test
+    public void testWriteToParcel() {
+        NotifyingApp na = new NotifyingApp();
+        na.setPackage("package");
+        na.setUid(200);
+        na.setLastNotified(4000);
+
+        Parcel parcel = Parcel.obtain();
+        na.writeToParcel(parcel, 0);
+        parcel.setDataPosition(0);
+        NotifyingApp na1 = NotifyingApp.CREATOR.createFromParcel(parcel);
+        assertEquals(na.getLastNotified(), na1.getLastNotified());
+        assertEquals(na.getPackage(), na1.getPackage());
+        assertEquals(na.getUid(), na1.getUid());
+    }
+
+    @Test
+    public void testCompareTo() {
+        NotifyingApp na1 = new NotifyingApp();
+        na1.setPackage("pkg1");
+        na1.setUid(1000);
+        na1.setLastNotified(6);
+
+        NotifyingApp na2 = new NotifyingApp();
+        na2.setPackage("a");
+        na2.setUid(999);
+        na2.setLastNotified(1);
+
+        assertTrue(na1.compareTo(na2) < 0);
+    }
+}
diff --git a/services/tests/uiservicestests/src/com/android/server/slice/PinnedSliceStateTest.java b/services/tests/uiservicestests/src/com/android/server/slice/PinnedSliceStateTest.java
index ce328c2..d3bb804 100644
--- a/services/tests/uiservicestests/src/com/android/server/slice/PinnedSliceStateTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/slice/PinnedSliceStateTest.java
@@ -6,6 +6,7 @@
 import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
 import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.anyString;
 import static org.mockito.ArgumentMatchers.argThat;
 import static org.mockito.ArgumentMatchers.eq;
@@ -21,8 +22,11 @@
 import android.content.ContentProvider;
 import android.content.IContentProvider;
 import android.net.Uri;
+import android.os.Binder;
 import android.os.Bundle;
 import android.os.Handler;
+import android.os.IBinder;
+import android.os.IBinder.DeathRecipient;
 import android.os.RemoteException;
 import android.support.test.filters.SmallTest;
 import android.testing.AndroidTestingRunner;
@@ -34,6 +38,7 @@
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
 
 @SmallTest
 @RunWith(AndroidTestingRunner.class)
@@ -147,9 +152,10 @@
     @Test
     public void testListenerPin() {
         ISliceListener listener = mock(ISliceListener.class);
+        when(listener.asBinder()).thenReturn(new Binder());
         assertFalse(mPinnedSliceManager.isPinned());
 
-        mPinnedSliceManager.addSliceListener(listener, FIRST_SPECS);
+        mPinnedSliceManager.addSliceListener(listener, mContext.getPackageName(), FIRST_SPECS);
         assertTrue(mPinnedSliceManager.isPinned());
 
         assertTrue(mPinnedSliceManager.removeSliceListener(listener));
@@ -159,12 +165,16 @@
     @Test
     public void testMultiListenerPin() {
         ISliceListener listener = mock(ISliceListener.class);
+        Binder value = new Binder();
+        when(listener.asBinder()).thenReturn(value);
         ISliceListener listener2 = mock(ISliceListener.class);
+        Binder value2 = new Binder();
+        when(listener2.asBinder()).thenReturn(value2);
         assertFalse(mPinnedSliceManager.isPinned());
 
-        mPinnedSliceManager.addSliceListener(listener, FIRST_SPECS);
+        mPinnedSliceManager.addSliceListener(listener, mContext.getPackageName(), FIRST_SPECS);
         assertTrue(mPinnedSliceManager.isPinned());
-        mPinnedSliceManager.addSliceListener(listener2, FIRST_SPECS);
+        mPinnedSliceManager.addSliceListener(listener2, mContext.getPackageName(), FIRST_SPECS);
 
         assertFalse(mPinnedSliceManager.removeSliceListener(listener));
         assertTrue(mPinnedSliceManager.removeSliceListener(listener2));
@@ -172,11 +182,33 @@
     }
 
     @Test
-    public void testPkgListenerPin() {
+    public void testListenerDeath() throws RemoteException {
         ISliceListener listener = mock(ISliceListener.class);
+        IBinder binder = mock(IBinder.class);
+        when(binder.isBinderAlive()).thenReturn(true);
+        when(listener.asBinder()).thenReturn(binder);
         assertFalse(mPinnedSliceManager.isPinned());
 
-        mPinnedSliceManager.addSliceListener(listener, FIRST_SPECS);
+        mPinnedSliceManager.addSliceListener(listener, mContext.getPackageName(), FIRST_SPECS);
+        assertTrue(mPinnedSliceManager.isPinned());
+
+        ArgumentCaptor<DeathRecipient> arg = ArgumentCaptor.forClass(DeathRecipient.class);
+        verify(binder).linkToDeath(arg.capture(), anyInt());
+
+        when(binder.isBinderAlive()).thenReturn(false);
+        arg.getValue().binderDied();
+
+        verify(mSliceService).removePinnedSlice(eq(TEST_URI));
+        assertFalse(mPinnedSliceManager.isPinned());
+    }
+
+    @Test
+    public void testPkgListenerPin() {
+        ISliceListener listener = mock(ISliceListener.class);
+        when(listener.asBinder()).thenReturn(new Binder());
+        assertFalse(mPinnedSliceManager.isPinned());
+
+        mPinnedSliceManager.addSliceListener(listener, mContext.getPackageName(), FIRST_SPECS);
         assertTrue(mPinnedSliceManager.isPinned());
         mPinnedSliceManager.pin("pkg", FIRST_SPECS);
 
@@ -191,6 +223,7 @@
         clearInvocations(mIContentProvider);
 
         ISliceListener listener = mock(ISliceListener.class);
+        when(listener.asBinder()).thenReturn(new Binder());
         Slice s = new Slice.Builder(TEST_URI).build();
         Bundle b = new Bundle();
         b.putParcelable(SliceProvider.EXTRA_SLICE, s);
@@ -199,7 +232,7 @@
 
         assertFalse(mPinnedSliceManager.isPinned());
 
-        mPinnedSliceManager.addSliceListener(listener, FIRST_SPECS);
+        mPinnedSliceManager.addSliceListener(listener, mContext.getPackageName(), FIRST_SPECS);
 
         mPinnedSliceManager.onChange();
         TestableLooper.get(this).processAllMessages();
diff --git a/services/usage/java/com/android/server/usage/AppStandbyController.java b/services/usage/java/com/android/server/usage/AppStandbyController.java
index ff3d586..6782188 100644
--- a/services/usage/java/com/android/server/usage/AppStandbyController.java
+++ b/services/usage/java/com/android/server/usage/AppStandbyController.java
@@ -78,6 +78,7 @@
 import com.android.internal.app.IBatteryStats;
 import com.android.internal.os.SomeArgs;
 import com.android.internal.util.ArrayUtils;
+import com.android.internal.util.ConcurrentUtils;
 import com.android.internal.util.IndentingPrintWriter;
 import com.android.server.LocalServices;
 
@@ -90,6 +91,7 @@
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
+import java.util.concurrent.CountDownLatch;
 
 /**
  * Manages the standby state of an app, listening to various events.
@@ -128,6 +130,11 @@
     // Expiration time for predicted bucket
     private static final long PREDICTION_TIMEOUT = 12 * ONE_HOUR;
 
+    /**
+     * Indicates the maximum wait time for admin data to be available;
+     */
+    private static final long WAIT_FOR_ADMIN_DATA_TIMEOUT_MS = 10_000;
+
     // To name the lock for stack traces
     static class Lock {}
 
@@ -153,6 +160,8 @@
     @GuardedBy("mActiveAdminApps")
     private final SparseArray<Set<String>> mActiveAdminApps = new SparseArray<>();
 
+    private final CountDownLatch mAdminDataAvailableLatch = new CountDownLatch(1);
+
     // Messages for the handler
     static final int MSG_INFORM_LISTENERS = 3;
     static final int MSG_FORCE_IDLE_STATE = 4;
@@ -895,6 +904,20 @@
         }
     }
 
+    public void onAdminDataAvailable() {
+        mAdminDataAvailableLatch.countDown();
+    }
+
+    /**
+     * This will only ever be called once - during device boot.
+     */
+    private void waitForAdminData() {
+        if (mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_DEVICE_ADMIN)) {
+            ConcurrentUtils.waitForCountDownNoInterrupt(mAdminDataAvailableLatch,
+                    WAIT_FOR_ADMIN_DATA_TIMEOUT_MS, "Wait for admin data");
+        }
+    }
+
     Set<String> getActiveAdminAppsForTest(int userId) {
         synchronized (mActiveAdminApps) {
             return mActiveAdminApps.get(userId);
@@ -1224,6 +1247,7 @@
 
                 case MSG_ONE_TIME_CHECK_IDLE_STATES:
                     mHandler.removeMessages(MSG_ONE_TIME_CHECK_IDLE_STATES);
+                    waitForAdminData();
                     checkIdleStates(UserHandle.USER_ALL);
                     break;
 
diff --git a/services/usage/java/com/android/server/usage/UsageStatsService.java b/services/usage/java/com/android/server/usage/UsageStatsService.java
index 78cc81f..979feaa 100644
--- a/services/usage/java/com/android/server/usage/UsageStatsService.java
+++ b/services/usage/java/com/android/server/usage/UsageStatsService.java
@@ -1030,5 +1030,10 @@
         public void setActiveAdminApps(Set<String> packageNames, int userId) {
             mAppStandby.setActiveAdminApps(packageNames, userId);
         }
+
+        @Override
+        public void onAdminDataAvailable() {
+            mAppStandby.onAdminDataAvailable();
+        }
     }
 }
diff --git a/telecomm/java/android/telecom/Call.java b/telecomm/java/android/telecom/Call.java
index 8c7d6b3..d17bdc8 100644
--- a/telecomm/java/android/telecom/Call.java
+++ b/telecomm/java/android/telecom/Call.java
@@ -419,7 +419,6 @@
         /**
          * Indicates the call used Assisted Dialing.
          * See also {@link Connection#PROPERTY_ASSISTED_DIALING_USED}
-         * @hide
          */
         public static final int PROPERTY_ASSISTED_DIALING_USED = 0x00000200;
 
diff --git a/telecomm/java/android/telecom/Connection.java b/telecomm/java/android/telecom/Connection.java
index aaef8d3..7522443 100644
--- a/telecomm/java/android/telecom/Connection.java
+++ b/telecomm/java/android/telecom/Connection.java
@@ -35,6 +35,7 @@
 import android.os.Looper;
 import android.os.Message;
 import android.os.ParcelFileDescriptor;
+import android.os.Parcelable;
 import android.os.RemoteException;
 import android.os.SystemClock;
 import android.util.ArraySet;
@@ -401,7 +402,6 @@
 
     /**
      * Set by the framework to indicate that a connection is using assisted dialing.
-     * @hide
      */
     public static final int PROPERTY_ASSISTED_DIALING_USED = 1 << 9;
 
@@ -2538,6 +2538,19 @@
     }
 
     /**
+     * Adds a parcelable extra to this {@code Connection}.
+     *
+     * @param key The extra key.
+     * @param value The value.
+     * @hide
+     */
+    public final void putExtra(@NonNull String key, @NonNull Parcelable value) {
+        Bundle newExtras = new Bundle();
+        newExtras.putParcelable(key, value);
+        putExtras(newExtras);
+    }
+
+    /**
      * Removes extras from this {@code Connection}.
      *
      * @param keys The keys of the extras to remove.
diff --git a/telecomm/java/android/telecom/TelecomManager.java b/telecomm/java/android/telecom/TelecomManager.java
index d292db3..91d5da3 100644
--- a/telecomm/java/android/telecom/TelecomManager.java
+++ b/telecomm/java/android/telecom/TelecomManager.java
@@ -602,12 +602,17 @@
     /**
      * The boolean indicated by this extra controls whether or not a call is eligible to undergo
      * assisted dialing. This extra is stored under {@link #EXTRA_OUTGOING_CALL_EXTRAS}.
-     * @hide
      */
     public static final String EXTRA_USE_ASSISTED_DIALING =
             "android.telecom.extra.USE_ASSISTED_DIALING";
 
     /**
+     * The bundle indicated by this extra store information related to the assisted dialing action.
+     */
+    public static final String EXTRA_ASSISTED_DIALING_TRANSFORMATION_INFO =
+            "android.telecom.extra.ASSISTED_DIALING_TRANSFORMATION_INFO";
+
+    /**
      * The following 4 constants define how properties such as phone numbers and names are
      * displayed to the user.
      */
diff --git a/telecomm/java/android/telecom/TransformationInfo.java b/telecomm/java/android/telecom/TransformationInfo.java
new file mode 100755
index 0000000..3e848c6
--- /dev/null
+++ b/telecomm/java/android/telecom/TransformationInfo.java
@@ -0,0 +1,127 @@
+/*
+ * Copyright (C) 2017 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.telecom;
+
+import android.os.Parcelable;
+import android.os.Parcel;
+
+/**
+ * A container class to hold information related to the Assisted Dialing operation. All member
+ * variables must be set when constructing a new instance of this class.
+ */
+public final class TransformationInfo implements Parcelable {
+    private String mOriginalNumber;
+    private String mTransformedNumber;
+    private String mUserHomeCountryCode;
+    private String mUserRoamingCountryCode;
+    private int mTransformedNumberCountryCallingCode;
+
+    public TransformationInfo(String originalNumber,
+                              String transformedNumber,
+                              String userHomeCountryCode,
+                              String userRoamingCountryCode,
+                              int transformedNumberCountryCallingCode) {
+        String missing = "";
+        if (originalNumber == null) {
+            missing += " mOriginalNumber";
+        }
+        if (transformedNumber == null) {
+            missing += " mTransformedNumber";
+        }
+        if (userHomeCountryCode == null) {
+            missing += " mUserHomeCountryCode";
+        }
+        if (userRoamingCountryCode == null) {
+            missing += " mUserRoamingCountryCode";
+        }
+
+        if (!missing.isEmpty()) {
+            throw new IllegalStateException("Missing required properties:" + missing);
+        }
+        this.mOriginalNumber = originalNumber;
+        this.mTransformedNumber = transformedNumber;
+        this.mUserHomeCountryCode = userHomeCountryCode;
+        this.mUserRoamingCountryCode = userRoamingCountryCode;
+        this.mTransformedNumberCountryCallingCode = transformedNumberCountryCallingCode;
+    }
+
+    public int describeContents() {
+        return 0;
+    }
+
+    public void writeToParcel(Parcel out, int flags) {
+        out.writeString(mOriginalNumber);
+        out.writeString(mTransformedNumber);
+        out.writeString(mUserHomeCountryCode);
+        out.writeString(mUserRoamingCountryCode);
+        out.writeInt(mTransformedNumberCountryCallingCode);
+    }
+
+    public static final Parcelable.Creator<TransformationInfo> CREATOR
+            = new Parcelable.Creator<TransformationInfo>() {
+        public TransformationInfo createFromParcel(Parcel in) {
+            return new TransformationInfo(in);
+        }
+
+        public TransformationInfo[] newArray(int size) {
+            return new TransformationInfo[size];
+        }
+    };
+
+    private TransformationInfo(Parcel in) {
+        mOriginalNumber = in.readString();
+        mTransformedNumber = in.readString();
+        mUserHomeCountryCode = in.readString();
+        mUserRoamingCountryCode = in.readString();
+        mTransformedNumberCountryCallingCode = in.readInt();
+    }
+
+    /**
+     * The original number that underwent Assisted Dialing.
+     */
+    public String getOriginalNumber() {
+        return mOriginalNumber;
+    }
+
+    /**
+     * The number after it underwent Assisted Dialing.
+     */
+    public String getTransformedNumber() {
+        return mTransformedNumber;
+    }
+
+    /**
+     * The user's home country code that was used when attempting to transform the number.
+     */
+    public String getUserHomeCountryCode() {
+        return mUserHomeCountryCode;
+    }
+
+    /**
+     * The users's roaming country code that was used when attempting to transform the number.
+     */
+    public String getUserRoamingCountryCode() {
+        return mUserRoamingCountryCode;
+    }
+
+    /**
+     * The country calling code that was used in the transformation.
+     */
+    public int getTransformedNumberCountryCallingCode() {
+        return mTransformedNumberCountryCallingCode;
+    }
+}
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index ce0b551..649d478 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -1644,6 +1644,13 @@
             "roaming_operator_string_array";
 
     /**
+     * Controls whether Assisted Dialing is enabled and the preference is shown. This feature
+     * transforms numbers when the user is roaming.
+     */
+    public static final String KEY_ASSISTED_DIALING_ENABLED_BOOL =
+            "assisted_dialing_enabled_bool";
+
+    /**
      * URL from which the proto containing the public key of the Carrier used for
      * IMSI encryption will be downloaded.
      * @hide
@@ -2040,6 +2047,7 @@
                 false);
         sDefaults.putStringArray(KEY_NON_ROAMING_OPERATOR_STRING_ARRAY, null);
         sDefaults.putStringArray(KEY_ROAMING_OPERATOR_STRING_ARRAY, null);
+        sDefaults.putBoolean(KEY_ASSISTED_DIALING_ENABLED_BOOL, true);
         sDefaults.putBoolean(KEY_SHOW_IMS_REGISTRATION_STATUS_BOOL, false);
         sDefaults.putBoolean(KEY_RTT_SUPPORTED_BOOL, false);
         sDefaults.putBoolean(KEY_DISABLE_CHARGE_INDICATION_BOOL, false);
diff --git a/telephony/java/android/telephony/SubscriptionInfo.java b/telephony/java/android/telephony/SubscriptionInfo.java
index 4e1c15f..38408fe 100644
--- a/telephony/java/android/telephony/SubscriptionInfo.java
+++ b/telephony/java/android/telephony/SubscriptionInfo.java
@@ -126,14 +126,31 @@
     private UiccAccessRule[] mAccessRules;
 
     /**
+     * The ID of the SIM card. It is the ICCID of the active profile for a UICC card and the EID
+     * for an eUICC card.
+     */
+    private String mCardId;
+
+    /**
+     * @hide
+     */
+    public SubscriptionInfo(int id, String iccId, int simSlotIndex, CharSequence displayName,
+        CharSequence carrierName, int nameSource, int iconTint, String number, int roaming,
+        Bitmap icon, int mcc, int mnc, String countryIso) {
+        this(id, iccId, simSlotIndex, displayName, carrierName, nameSource, iconTint, number,
+            roaming, icon, mcc, mnc, countryIso, false /* isEmbedded */,
+            null /* accessRules */, null /* accessRules */);
+    }
+
+    /**
      * @hide
      */
     public SubscriptionInfo(int id, String iccId, int simSlotIndex, CharSequence displayName,
             CharSequence carrierName, int nameSource, int iconTint, String number, int roaming,
-            Bitmap icon, int mcc, int mnc, String countryIso) {
+            Bitmap icon, int mcc, int mnc, String countryIso,  boolean isEmbedded,
+            @Nullable UiccAccessRule[] accessRules) {
         this(id, iccId, simSlotIndex, displayName, carrierName, nameSource, iconTint, number,
-                roaming, icon, mcc, mnc, countryIso, false /* isEmbedded */,
-                null /* accessRules */);
+                roaming, icon, mcc, mnc, countryIso, isEmbedded, accessRules, null /* cardId */);
     }
 
     /**
@@ -142,7 +159,7 @@
     public SubscriptionInfo(int id, String iccId, int simSlotIndex, CharSequence displayName,
             CharSequence carrierName, int nameSource, int iconTint, String number, int roaming,
             Bitmap icon, int mcc, int mnc, String countryIso, boolean isEmbedded,
-            @Nullable UiccAccessRule[] accessRules) {
+            @Nullable UiccAccessRule[] accessRules, String cardId) {
         this.mId = id;
         this.mIccId = iccId;
         this.mSimSlotIndex = simSlotIndex;
@@ -158,6 +175,7 @@
         this.mCountryIso = countryIso;
         this.mIsEmbedded = isEmbedded;
         this.mAccessRules = accessRules;
+        this.mCardId = cardId;
     }
 
     /**
@@ -387,6 +405,14 @@
         return mAccessRules;
     }
 
+    /**
+     * @return the ID of the SIM card which contains the subscription.
+     * @hide
+     */
+    public String getCardId() {
+        return this.mCardId;
+    }
+
     public static final Parcelable.Creator<SubscriptionInfo> CREATOR = new Parcelable.Creator<SubscriptionInfo>() {
         @Override
         public SubscriptionInfo createFromParcel(Parcel source) {
@@ -405,10 +431,11 @@
             Bitmap iconBitmap = Bitmap.CREATOR.createFromParcel(source);
             boolean isEmbedded = source.readBoolean();
             UiccAccessRule[] accessRules = source.createTypedArray(UiccAccessRule.CREATOR);
+            String cardId = source.readString();
 
             return new SubscriptionInfo(id, iccId, simSlotIndex, displayName, carrierName,
                     nameSource, iconTint, number, dataRoaming, iconBitmap, mcc, mnc, countryIso,
-                    isEmbedded, accessRules);
+                    isEmbedded, accessRules, cardId);
         }
 
         @Override
@@ -434,6 +461,7 @@
         mIconBitmap.writeToParcel(dest, flags);
         dest.writeBoolean(mIsEmbedded);
         dest.writeTypedArray(mAccessRules, flags);
+        dest.writeString(mCardId);
     }
 
     @Override
@@ -459,11 +487,13 @@
     @Override
     public String toString() {
         String iccIdToPrint = givePrintableIccid(mIccId);
+        String cardIdToPrint = givePrintableIccid(mCardId);
         return "{id=" + mId + ", iccId=" + iccIdToPrint + " simSlotIndex=" + mSimSlotIndex
                 + " displayName=" + mDisplayName + " carrierName=" + mCarrierName
                 + " nameSource=" + mNameSource + " iconTint=" + mIconTint
                 + " dataRoaming=" + mDataRoaming + " iconBitmap=" + mIconBitmap + " mcc " + mMcc
                 + " mnc " + mMnc + " isEmbedded " + mIsEmbedded
-                + " accessRules " + Arrays.toString(mAccessRules) + "}";
+                + " accessRules " + Arrays.toString(mAccessRules)
+                + " cardId=" + cardIdToPrint + "}";
     }
 }
diff --git a/telephony/java/android/telephony/SubscriptionManager.java b/telephony/java/android/telephony/SubscriptionManager.java
index 1406093..57f4cf2 100644
--- a/telephony/java/android/telephony/SubscriptionManager.java
+++ b/telephony/java/android/telephony/SubscriptionManager.java
@@ -284,6 +284,14 @@
     public static final String IS_EMBEDDED = "is_embedded";
 
     /**
+     * TelephonyProvider column name for SIM card identifier. For UICC card it is the ICCID of the
+     * current enabled profile on the card, while for eUICC card it is the EID of the card.
+     * <P>Type: TEXT (String)</P>
+     * @hide
+     */
+     public static final String CARD_ID = "card_id";
+
+    /**
      * TelephonyProvider column name for the encoded {@link UiccAccessRule}s from
      * {@link UiccAccessRule#encodeRules}. Only present if {@link #IS_EMBEDDED} is 1.
      * <p>TYPE: BLOB
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index de9e691..17f809d 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -1024,8 +1024,8 @@
 
     /**
      * An int extra used with {@link #ACTION_SUBSCRIPTION_CARRIER_IDENTITY_CHANGED} which indicates
-     * the updated carrier id {@link TelephonyManager#getSubscriptionCarrierId()} of the current
-     * subscription.
+     * the updated carrier id {@link TelephonyManager#getAndroidCarrierIdForSubscription()} of
+     * the current subscription.
      * <p>Will be {@link TelephonyManager#UNKNOWN_CARRIER_ID} if the subscription is unavailable or
      * the carrier cannot be identified.
      */
@@ -6900,14 +6900,19 @@
 
     /**
      * Returns carrier id of the current subscription.
-     * <p>To recognize a carrier (including MVNO) as a first class identity, assign each carrier
-     * with a canonical integer a.k.a carrier id.
+     * <p>To recognize a carrier (including MVNO) as a first-class identity, Android assigns each
+     * carrier with a canonical integer a.k.a. android carrier id. The Android carrier ID is an
+     * Android platform-wide identifier for a carrier. AOSP maintains carrier ID assignments in
+     * <a href="https://android.googlesource.com/platform/packages/providers/TelephonyProvider/+/master/assets/carrier_list.textpb">here</a>
+     *
+     * <p>Apps which have carrier-specific configurations or business logic can use the carrier id
+     * as an Android platform-wide identifier for carriers.
      *
      * @return Carrier id of the current subscription. Return {@link #UNKNOWN_CARRIER_ID} if the
      * subscription is unavailable or the carrier cannot be identified.
      * @throws IllegalStateException if telephony service is unavailable.
      */
-    public int getSubscriptionCarrierId() {
+    public int getAndroidCarrierIdForSubscription() {
         try {
             ITelephony service = getITelephony();
             return service.getSubscriptionCarrierId(getSubId());
@@ -6923,17 +6928,18 @@
 
     /**
      * Returns carrier name of the current subscription.
-     * <p>Carrier name is a user-facing name of carrier id {@link #getSubscriptionCarrierId()},
-     * usually the brand name of the subsidiary (e.g. T-Mobile). Each carrier could configure
-     * multiple {@link #getSimOperatorName() SPN} but should have a single carrier name.
-     * Carrier name is not a canonical identity, use {@link #getSubscriptionCarrierId()} instead.
+     * <p>Carrier name is a user-facing name of carrier id
+     * {@link #getAndroidCarrierIdForSubscription()}, usually the brand name of the subsidiary
+     * (e.g. T-Mobile). Each carrier could configure multiple {@link #getSimOperatorName() SPN} but
+     * should have a single carrier name. Carrier name is not a canonical identity,
+     * use {@link #getAndroidCarrierIdForSubscription()} instead.
      * <p>The returned carrier name is unlocalized.
      *
      * @return Carrier name of the current subscription. Return {@code null} if the subscription is
      * unavailable or the carrier cannot be identified.
      * @throws IllegalStateException if telephony service is unavailable.
      */
-    public String getSubscriptionCarrierName() {
+    public CharSequence getAndroidCarrierNameForSubscription() {
         try {
             ITelephony service = getITelephony();
             return service.getSubscriptionCarrierName(getSubId());
diff --git a/telephony/java/android/telephony/data/ApnSetting.java b/telephony/java/android/telephony/data/ApnSetting.java
index 2ab8d4f..73a05af 100644
--- a/telephony/java/android/telephony/data/ApnSetting.java
+++ b/telephony/java/android/telephony/data/ApnSetting.java
@@ -21,22 +21,23 @@
 import android.content.ContentValues;
 import android.database.Cursor;
 import android.hardware.radio.V1_0.ApnTypes;
-import android.net.NetworkUtils;
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.provider.Telephony;
 import android.telephony.Rlog;
+import android.telephony.ServiceState;
+import android.telephony.TelephonyManager;
 import android.text.TextUtils;
 import android.util.Log;
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
-import java.net.MalformedURLException;
-import java.net.UnknownHostException;
-import java.net.URL;
 import java.net.InetAddress;
-import java.util.Arrays;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.net.UnknownHostException;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.List;
 import java.util.Objects;
 
@@ -67,8 +68,8 @@
     private final int mMtu;
 
     private final boolean mCarrierEnabled;
-    private final int mBearer;
-    private final int mBearerBitmask;
+
+    private final int mNetworkTypeBitmask;
 
     private final int mProfileId;
 
@@ -103,34 +104,6 @@
     }
 
     /**
-     * Radio Access Technology info.
-     * To check what values can hold, refer to ServiceState.java.
-     * This should be spread to other technologies,
-     * but currently only used for LTE(14) and EHRPD(13).
-     *
-     * @return the bearer info of the APN
-     * @hide
-     */
-    public int getBearer() {
-        return mBearer;
-    }
-
-    /**
-     * Returns the radio access technology bitmask for this APN.
-     *
-     * To check what values can hold, refer to ServiceState.java. This is a bitmask of radio
-     * technologies in ServiceState.
-     * This should be spread to other technologies,
-     * but currently only used for LTE(14) and EHRPD(13).
-     *
-     * @return the radio access technology bitmask
-     * @hide
-     */
-    public int getBearerBitmask() {
-        return mBearerBitmask;
-    }
-
-    /**
      * Returns the profile id to which the APN saved in modem.
      *
      * @return the profile id of the APN
@@ -411,6 +384,20 @@
         return mCarrierEnabled;
     }
 
+    /**
+     * Returns a bitmask describing the Radio Technologies(Network Types) which this APN may use.
+     *
+     * NetworkType bitmask is calculated from NETWORK_TYPE defined in {@link TelephonyManager}.
+     *
+     * Examples of Network Types include {@link TelephonyManager#NETWORK_TYPE_UNKNOWN},
+     * {@link TelephonyManager#NETWORK_TYPE_GPRS}, {@link TelephonyManager#NETWORK_TYPE_EDGE}.
+     *
+     * @return a bitmask describing the Radio Technologies(Network Types)
+     */
+    public int getNetworkTypeBitmask() {
+        return mNetworkTypeBitmask;
+    }
+
     /** @hide */
     @StringDef({
             MVNO_TYPE_SPN,
@@ -452,8 +439,7 @@
         this.mRoamingProtocol = builder.mRoamingProtocol;
         this.mMtu = builder.mMtu;
         this.mCarrierEnabled = builder.mCarrierEnabled;
-        this.mBearer = builder.mBearer;
-        this.mBearerBitmask = builder.mBearerBitmask;
+        this.mNetworkTypeBitmask = builder.mNetworkTypeBitmask;
         this.mProfileId = builder.mProfileId;
         this.mModemCognitive = builder.mModemCognitive;
         this.mMaxConns = builder.mMaxConns;
@@ -467,8 +453,8 @@
     public static ApnSetting makeApnSetting(int id, String operatorNumeric, String entryName,
             String apnName, InetAddress proxy, int port, URL mmsc, InetAddress mmsProxy,
             int mmsPort, String user, String password, int authType, List<String> types,
-            String protocol, String roamingProtocol, boolean carrierEnabled, int bearer,
-            int bearerBitmask, int profileId, boolean modemCognitive, int maxConns,
+            String protocol, String roamingProtocol, boolean carrierEnabled,
+            int networkTypeBitmask, int profileId, boolean modemCognitive, int maxConns,
             int waitTime, int maxConnsTime, int mtu, String mvnoType, String mvnoMatchData) {
         return new Builder()
                 .setId(id)
@@ -487,8 +473,7 @@
                 .setProtocol(protocol)
                 .setRoamingProtocol(roamingProtocol)
                 .setCarrierEnabled(carrierEnabled)
-                .setBearer(bearer)
-                .setBearerBitmask(bearerBitmask)
+                .setNetworkTypeBitmask(networkTypeBitmask)
                 .setProfileId(profileId)
                 .setModemCognitive(modemCognitive)
                 .setMaxConns(maxConns)
@@ -504,6 +489,14 @@
     public static ApnSetting makeApnSetting(Cursor cursor) {
         String[] types = parseTypes(
                 cursor.getString(cursor.getColumnIndexOrThrow(Telephony.Carriers.TYPE)));
+        int networkTypeBitmask = cursor.getInt(
+                cursor.getColumnIndexOrThrow(Telephony.Carriers.NETWORK_TYPE_BITMASK));
+        if (networkTypeBitmask == 0) {
+            final int bearerBitmask = cursor.getInt(cursor.getColumnIndexOrThrow(
+                    Telephony.Carriers.BEARER_BITMASK));
+            networkTypeBitmask =
+                    ServiceState.convertBearerBitmaskToNetworkTypeBitmask(bearerBitmask);
+        }
 
         return makeApnSetting(
                 cursor.getInt(cursor.getColumnIndexOrThrow(Telephony.Carriers._ID)),
@@ -529,9 +522,7 @@
                         Telephony.Carriers.ROAMING_PROTOCOL)),
                 cursor.getInt(cursor.getColumnIndexOrThrow(
                         Telephony.Carriers.CARRIER_ENABLED)) == 1,
-                cursor.getInt(cursor.getColumnIndexOrThrow(Telephony.Carriers.BEARER)),
-                cursor.getInt(cursor.getColumnIndexOrThrow(
-                        Telephony.Carriers.BEARER_BITMASK)),
+                networkTypeBitmask,
                 cursor.getInt(cursor.getColumnIndexOrThrow(Telephony.Carriers.PROFILE_ID)),
                 cursor.getInt(cursor.getColumnIndexOrThrow(
                         Telephony.Carriers.MODEM_COGNITIVE)) == 1,
@@ -551,7 +542,7 @@
         return makeApnSetting(apn.mId, apn.mOperatorNumeric, apn.mEntryName, apn.mApnName,
                 apn.mProxy, apn.mPort, apn.mMmsc, apn.mMmsProxy, apn.mMmsPort, apn.mUser,
                 apn.mPassword, apn.mAuthType, apn.mTypes, apn.mProtocol, apn.mRoamingProtocol,
-                apn.mCarrierEnabled, apn.mBearer, apn.mBearerBitmask, apn.mProfileId,
+                apn.mCarrierEnabled, apn.mNetworkTypeBitmask, apn.mProfileId,
                 apn.mModemCognitive, apn.mMaxConns, apn.mWaitTime, apn.mMaxConnsTime, apn.mMtu,
                 apn.mMvnoType, apn.mMvnoMatchData);
     }
@@ -559,7 +550,7 @@
     /** @hide */
     public String toString() {
         StringBuilder sb = new StringBuilder();
-        sb.append("[ApnSettingV3] ")
+        sb.append("[ApnSettingV4] ")
                 .append(mEntryName)
                 .append(", ").append(mId)
                 .append(", ").append(mOperatorNumeric)
@@ -579,8 +570,6 @@
         sb.append(", ").append(mProtocol);
         sb.append(", ").append(mRoamingProtocol);
         sb.append(", ").append(mCarrierEnabled);
-        sb.append(", ").append(mBearer);
-        sb.append(", ").append(mBearerBitmask);
         sb.append(", ").append(mProfileId);
         sb.append(", ").append(mModemCognitive);
         sb.append(", ").append(mMaxConns);
@@ -590,6 +579,7 @@
         sb.append(", ").append(mMvnoType);
         sb.append(", ").append(mMvnoMatchData);
         sb.append(", ").append(mPermanentFailed);
+        sb.append(", ").append(mNetworkTypeBitmask);
         return sb.toString();
     }
 
@@ -678,8 +668,6 @@
                 && Objects.equals(mProtocol, other.mProtocol)
                 && Objects.equals(mRoamingProtocol, other.mRoamingProtocol)
                 && Objects.equals(mCarrierEnabled, other.mCarrierEnabled)
-                && Objects.equals(mBearer, other.mBearer)
-                && Objects.equals(mBearerBitmask, other.mBearerBitmask)
                 && Objects.equals(mProfileId, other.mProfileId)
                 && Objects.equals(mModemCognitive, other.mModemCognitive)
                 && Objects.equals(mMaxConns, other.mMaxConns)
@@ -687,13 +675,14 @@
                 && Objects.equals(mMaxConnsTime, other.mMaxConnsTime)
                 && Objects.equals(mMtu, other.mMtu)
                 && Objects.equals(mMvnoType, other.mMvnoType)
-                && Objects.equals(mMvnoMatchData, other.mMvnoMatchData);
+                && Objects.equals(mMvnoMatchData, other.mMvnoMatchData)
+                && Objects.equals(mNetworkTypeBitmask, other.mNetworkTypeBitmask);
     }
 
     /**
      * Compare two APN settings
      *
-     * Note: This method does not compare 'id', 'bearer', 'bearerBitmask'. We only use this for
+     * Note: This method does not compare 'mId', 'mNetworkTypeBitmask'. We only use this for
      * determining if tearing a data call is needed when conditions change. See
      * cleanUpConnectionsOnUpdatedApns in DcTracker.
      *
@@ -752,13 +741,13 @@
                 && xorEquals(this.mProtocol, other.mProtocol)
                 && xorEquals(this.mRoamingProtocol, other.mRoamingProtocol)
                 && Objects.equals(this.mCarrierEnabled, other.mCarrierEnabled)
-                && Objects.equals(this.mBearerBitmask, other.mBearerBitmask)
                 && Objects.equals(this.mProfileId, other.mProfileId)
                 && Objects.equals(this.mMvnoType, other.mMvnoType)
                 && Objects.equals(this.mMvnoMatchData, other.mMvnoMatchData)
                 && xorEqualsURL(this.mMmsc, other.mMmsc)
                 && xorEqualsInetAddress(this.mMmsProxy, other.mMmsProxy)
-                && xorEqualsPort(this.mMmsPort, other.mMmsPort));
+                && xorEqualsPort(this.mMmsPort, other.mMmsPort))
+                && Objects.equals(this.mNetworkTypeBitmask, other.mNetworkTypeBitmask);
     }
 
     // Equal or one is not specified.
@@ -808,53 +797,33 @@
         return TextUtils.join(",", types);
     }
 
+    private String nullToEmpty(String stringValue) {
+        return stringValue == null ? "" : stringValue;
+    }
+
     /** @hide */
     // Called by DPM.
     public ContentValues toContentValues() {
         ContentValues apnValue = new ContentValues();
-        if (mOperatorNumeric != null) {
-            apnValue.put(Telephony.Carriers.NUMERIC, mOperatorNumeric);
-        }
-        if (mEntryName != null) {
-            apnValue.put(Telephony.Carriers.NAME, mEntryName);
-        }
-        if (mApnName != null) {
-            apnValue.put(Telephony.Carriers.APN, mApnName);
-        }
-        if (mProxy != null) {
-            apnValue.put(Telephony.Carriers.PROXY, inetAddressToString(mProxy));
-        }
+        apnValue.put(Telephony.Carriers.NUMERIC, nullToEmpty(mOperatorNumeric));
+        apnValue.put(Telephony.Carriers.NAME, nullToEmpty(mEntryName));
+        apnValue.put(Telephony.Carriers.APN, nullToEmpty(mApnName));
+        apnValue.put(Telephony.Carriers.PROXY, mProxy == null ? "" : inetAddressToString(mProxy));
         apnValue.put(Telephony.Carriers.PORT, portToString(mPort));
-        if (mMmsc != null) {
-            apnValue.put(Telephony.Carriers.MMSC, URLToString(mMmsc));
-        }
+        apnValue.put(Telephony.Carriers.MMSC, mMmsc == null ? "" : URLToString(mMmsc));
         apnValue.put(Telephony.Carriers.MMSPORT, portToString(mMmsPort));
-        if (mMmsProxy != null) {
-            apnValue.put(Telephony.Carriers.MMSPROXY, inetAddressToString(mMmsProxy));
-        }
-        if (mUser != null) {
-            apnValue.put(Telephony.Carriers.USER, mUser);
-        }
-        if (mPassword != null) {
-            apnValue.put(Telephony.Carriers.PASSWORD, mPassword);
-        }
+        apnValue.put(Telephony.Carriers.MMSPROXY, mMmsProxy == null
+                ? "" : inetAddressToString(mMmsProxy));
+        apnValue.put(Telephony.Carriers.USER, nullToEmpty(mUser));
+        apnValue.put(Telephony.Carriers.PASSWORD, nullToEmpty(mPassword));
         apnValue.put(Telephony.Carriers.AUTH_TYPE, mAuthType);
         String apnType = deParseTypes(mTypes);
-        if (apnType != null) {
-            apnValue.put(Telephony.Carriers.TYPE, apnType);
-        }
-        if (mProtocol != null) {
-            apnValue.put(Telephony.Carriers.PROTOCOL, mProtocol);
-        }
-        if (mRoamingProtocol != null) {
-            apnValue.put(Telephony.Carriers.ROAMING_PROTOCOL, mRoamingProtocol);
-        }
+        apnValue.put(Telephony.Carriers.TYPE, nullToEmpty(apnType));
+        apnValue.put(Telephony.Carriers.PROTOCOL, nullToEmpty(mProtocol));
+        apnValue.put(Telephony.Carriers.ROAMING_PROTOCOL, nullToEmpty(mRoamingProtocol));
         apnValue.put(Telephony.Carriers.CARRIER_ENABLED, mCarrierEnabled);
-        // networkTypeBit.
-        apnValue.put(Telephony.Carriers.BEARER_BITMASK, mBearerBitmask);
-        if (mMvnoType != null) {
-            apnValue.put(Telephony.Carriers.MVNO_TYPE, mMvnoType);
-        }
+        apnValue.put(Telephony.Carriers.MVNO_TYPE, nullToEmpty(mMvnoType));
+        apnValue.put(Telephony.Carriers.NETWORK_TYPE_BITMASK, mNetworkTypeBitmask);
 
         return apnValue;
     }
@@ -905,8 +874,16 @@
         if (inetAddress == null) {
             return null;
         }
-        return TextUtils.isEmpty(inetAddress.getHostName())
-                ? inetAddress.getHostAddress() : inetAddress.getHostName();
+        final String inetAddressString = inetAddress.toString();
+        if (TextUtils.isEmpty(inetAddressString)) {
+            return null;
+        }
+        final String hostName = inetAddressString.substring(0, inetAddressString.indexOf("/"));
+        final String address = inetAddressString.substring(inetAddressString.indexOf("/") + 1);
+        if (TextUtils.isEmpty(hostName) && TextUtils.isEmpty(address)) {
+            return null;
+        }
+        return TextUtils.isEmpty(hostName) ? address : hostName;
     }
 
     private static int portFromString(String strPort) {
@@ -952,16 +929,33 @@
         dest.writeString(mRoamingProtocol);
         dest.writeInt(mCarrierEnabled ? 1: 0);
         dest.writeString(mMvnoType);
+        dest.writeInt(mNetworkTypeBitmask);
     }
 
     private static ApnSetting readFromParcel(Parcel in) {
-        return makeApnSetting(in.readInt(), in.readString(), in.readString(), in.readString(),
-                (InetAddress)in.readValue(InetAddress.class.getClassLoader()),
-                in.readInt(), (URL)in.readValue(URL.class.getClassLoader()),
-                (InetAddress)in.readValue(InetAddress.class.getClassLoader()),
-                in.readInt(), in.readString(), in.readString(), in.readInt(),
-                Arrays.asList(in.readStringArray()), in.readString(), in.readString(),
-                in.readInt() > 0, 0, 0, 0, false, 0, 0, 0, 0, in.readString(), null);
+        final int id = in.readInt();
+        final String operatorNumeric = in.readString();
+        final String entryName = in.readString();
+        final String apnName = in.readString();
+        final InetAddress proxy = (InetAddress)in.readValue(InetAddress.class.getClassLoader());
+        final int port = in.readInt();
+        final URL mmsc = (URL)in.readValue(URL.class.getClassLoader());
+        final InetAddress mmsProxy = (InetAddress)in.readValue(InetAddress.class.getClassLoader());
+        final int mmsPort = in.readInt();
+        final String user = in.readString();
+        final String password = in.readString();
+        final int authType = in.readInt();
+        final List<String> types = Arrays.asList(in.readStringArray());
+        final String protocol = in.readString();
+        final String roamingProtocol = in.readString();
+        final boolean carrierEnabled = in.readInt() > 0;
+        final String mvnoType = in.readString();
+        final int networkTypeBitmask = in.readInt();
+
+        return makeApnSetting(id, operatorNumeric, entryName, apnName,
+                proxy, port, mmsc, mmsProxy, mmsPort, user, password, authType, types, protocol,
+                roamingProtocol, carrierEnabled, networkTypeBitmask, 0, false,
+                0, 0, 0, 0, mvnoType, null);
     }
 
     public static final Parcelable.Creator<ApnSetting> CREATOR =
@@ -1061,9 +1055,8 @@
         private String mProtocol;
         private String mRoamingProtocol;
         private int mMtu;
+        private int mNetworkTypeBitmask;
         private boolean mCarrierEnabled;
-        private int mBearer;
-        private int mBearerBitmask;
         private int mProfileId;
         private boolean mModemCognitive;
         private int mMaxConns;
@@ -1078,6 +1071,16 @@
         public Builder() {}
 
         /**
+         * Sets the unique database id for this entry.
+         *
+         * @param id the unique database id to set for this entry
+         */
+        private Builder setId(int id) {
+            this.mId = id;
+            return this;
+        }
+
+        /**
          * Set the MTU size of the mobile interface to which the APN connected.
          *
          * @param mtu the MTU size to set for the APN
@@ -1089,28 +1092,6 @@
         }
 
         /**
-         * Sets bearer info.
-         *
-         * @param bearer the bearer info to set for the APN
-         * @hide
-         */
-        public Builder setBearer(int bearer) {
-            this.mBearer = bearer;
-            return this;
-        }
-
-        /**
-         * Sets the radio access technology bitmask for this APN.
-         *
-         * @param bearerBitmask the radio access technology bitmask to set for this APN
-         * @hide
-         */
-        public Builder setBearerBitmask(int bearerBitmask) {
-            this.mBearerBitmask = bearerBitmask;
-            return this;
-        }
-
-        /**
          * Sets the profile id to which the APN saved in modem.
          *
          * @param profileId the profile id to set for the APN
@@ -1298,16 +1279,6 @@
         }
 
         /**
-         * Sets the unique database id for this entry.
-         *
-         * @param id the unique database id to set for this entry
-         */
-        public Builder setId(int id) {
-            this.mId = id;
-            return this;
-        }
-
-        /**
          * Set the numeric operator ID for the APN.
          *
          * @param operatorNumeric the numeric operator ID to set for this entry
@@ -1341,7 +1312,7 @@
         }
 
         /**
-         * Sets the current status of APN.
+         * Sets the current status for this APN.
          *
          * @param carrierEnabled the current status to set for this APN
          */
@@ -1351,6 +1322,16 @@
         }
 
         /**
+         * Sets Radio Technology (Network Type) info for this APN.
+         *
+         * @param networkTypeBitmask the Radio Technology (Network Type) info
+         */
+        public Builder setNetworkTypeBitmask(int networkTypeBitmask) {
+            this.mNetworkTypeBitmask = networkTypeBitmask;
+            return this;
+        }
+
+        /**
          * Sets the MVNO match type for this APN.
          *
          * Example of possible values: {@link #MVNO_TYPE_SPN}, {@link #MVNO_TYPE_IMSI}.
diff --git a/telephony/java/android/telephony/euicc/EuiccManager.java b/telephony/java/android/telephony/euicc/EuiccManager.java
index 2534327..d146707 100644
--- a/telephony/java/android/telephony/euicc/EuiccManager.java
+++ b/telephony/java/android/telephony/euicc/EuiccManager.java
@@ -71,8 +71,18 @@
      * TODO(b/35851809): Make this a SystemApi.
      */
     @SdkConstant(SdkConstant.SdkConstantType.BROADCAST_INTENT_ACTION)
-    public static final String ACTION_OTA_STATUS_CHANGED
-            = "android.telephony.euicc.action.OTA_STATUS_CHANGED";
+    public static final String ACTION_OTA_STATUS_CHANGED =
+            "android.telephony.euicc.action.OTA_STATUS_CHANGED";
+
+    /**
+     * Broadcast Action: The action sent to carrier app so it knows the carrier setup is not
+     * completed.
+     *
+     * TODO(b/35851809): Make this a public API.
+     */
+    @SdkConstant(SdkConstant.SdkConstantType.BROADCAST_INTENT_ACTION)
+    public static final String ACTION_NOTIFY_CARRIER_SETUP =
+            "android.telephony.euicc.action.NOTIFY_CARRIER_SETUP";
 
     /**
      * Intent action to provision an embedded subscription.
diff --git a/tests/net/Android.mk b/tests/net/Android.mk
index 1bd1af5..994f3cc 100644
--- a/tests/net/Android.mk
+++ b/tests/net/Android.mk
@@ -31,32 +31,34 @@
 LOCAL_CERTIFICATE := platform
 
 # These are not normally accessible from apps so they must be explicitly included.
-LOCAL_JNI_SHARED_LIBRARIES := libframeworksnettestsjni \
+LOCAL_JNI_SHARED_LIBRARIES := \
+    android.hidl.token@1.0 \
     libbacktrace \
     libbase \
     libbinder \
     libc++ \
+    libcrypto \
     libcutils \
+    libframeworksnettestsjni \
+    libhidl-gen-utils \
+    libhidlbase \
+    libhidltransport \
+    libhwbinder \
     liblog \
     liblzma \
     libnativehelper \
     libnetdaidl \
-    libui \
-    libunwind \
-    libutils \
-    libvndksupport \
-    libcrypto \
-    libhidl-gen-utils \
-    libhidlbase \
-    libhidltransport \
     libpackagelistparser \
     libpcre2 \
     libselinux \
-    libtinyxml2 \
+    libui \
+    libunwind \
+    libutils \
     libvintf \
-    libhwbinder \
+    libvndksupport \
+    libtinyxml2 \
     libunwindstack \
-    android.hidl.token@1.0
+    libutilscallstack
 
 LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk
 
diff --git a/tests/net/java/com/android/server/IpSecServiceParameterizedTest.java b/tests/net/java/com/android/server/IpSecServiceParameterizedTest.java
index 4fbb228..d9d4eeb 100644
--- a/tests/net/java/com/android/server/IpSecServiceParameterizedTest.java
+++ b/tests/net/java/com/android/server/IpSecServiceParameterizedTest.java
@@ -204,13 +204,13 @@
     }
 
     @Test
-    public void testCreateTransportModeTransform() throws Exception {
+    public void testCreateTransform() throws Exception {
         IpSecConfig ipSecConfig = new IpSecConfig();
         addDefaultSpisAndRemoteAddrToIpSecConfig(ipSecConfig);
         addAuthAndCryptToIpSecConfig(ipSecConfig);
 
         IpSecTransformResponse createTransformResp =
-                mIpSecService.createTransportModeTransform(ipSecConfig, new Binder());
+                mIpSecService.createTransform(ipSecConfig, new Binder());
         assertEquals(IpSecManager.Status.OK, createTransformResp.status);
 
         verify(mMockNetd)
@@ -236,14 +236,14 @@
     }
 
     @Test
-    public void testCreateTransportModeTransformAead() throws Exception {
+    public void testCreateTransformAead() throws Exception {
         IpSecConfig ipSecConfig = new IpSecConfig();
         addDefaultSpisAndRemoteAddrToIpSecConfig(ipSecConfig);
 
         ipSecConfig.setAuthenticatedEncryption(AEAD_ALGO);
 
         IpSecTransformResponse createTransformResp =
-                mIpSecService.createTransportModeTransform(ipSecConfig, new Binder());
+                mIpSecService.createTransform(ipSecConfig, new Binder());
         assertEquals(IpSecManager.Status.OK, createTransformResp.status);
 
         verify(mMockNetd)
@@ -269,14 +269,14 @@
     }
 
     @Test
-    public void testDeleteTransportModeTransform() throws Exception {
+    public void testDeleteTransform() throws Exception {
         IpSecConfig ipSecConfig = new IpSecConfig();
         addDefaultSpisAndRemoteAddrToIpSecConfig(ipSecConfig);
         addAuthAndCryptToIpSecConfig(ipSecConfig);
 
         IpSecTransformResponse createTransformResp =
-                mIpSecService.createTransportModeTransform(ipSecConfig, new Binder());
-        mIpSecService.deleteTransportModeTransform(createTransformResp.resourceId);
+                mIpSecService.createTransform(ipSecConfig, new Binder());
+        mIpSecService.deleteTransform(createTransformResp.resourceId);
 
         verify(mMockNetd)
                 .ipSecDeleteSecurityAssociation(
@@ -302,7 +302,7 @@
         addAuthAndCryptToIpSecConfig(ipSecConfig);
 
         IpSecTransformResponse createTransformResp =
-                mIpSecService.createTransportModeTransform(ipSecConfig, new Binder());
+                mIpSecService.createTransform(ipSecConfig, new Binder());
 
         IpSecService.UserRecord userRecord =
                 mIpSecService.mUserResourceTracker.getUserRecord(Os.getuid());
@@ -334,7 +334,7 @@
         addAuthAndCryptToIpSecConfig(ipSecConfig);
 
         IpSecTransformResponse createTransformResp =
-                mIpSecService.createTransportModeTransform(ipSecConfig, new Binder());
+                mIpSecService.createTransform(ipSecConfig, new Binder());
         ParcelFileDescriptor pfd = ParcelFileDescriptor.fromSocket(new Socket());
 
         int resourceId = createTransformResp.resourceId;
diff --git a/tests/net/java/com/android/server/IpSecServiceTest.java b/tests/net/java/com/android/server/IpSecServiceTest.java
index 3eba881..a375b60 100644
--- a/tests/net/java/com/android/server/IpSecServiceTest.java
+++ b/tests/net/java/com/android/server/IpSecServiceTest.java
@@ -166,6 +166,7 @@
         mIpSecService.closeUdpEncapsulationSocket(udpEncapResp.resourceId);
         udpEncapResp.fileDescriptor.close();
 
+        // Verify quota and RefcountedResource objects cleaned up
         IpSecService.UserRecord userRecord =
                 mIpSecService.mUserResourceTracker.getUserRecord(Os.getuid());
         assertEquals(0, userRecord.mSocketQuotaTracker.mCurrent);
@@ -179,10 +180,8 @@
 
     @Test
     public void testUdpEncapsulationSocketBinderDeath() throws Exception {
-        int localport = findUnusedPort();
-
         IpSecUdpEncapResponse udpEncapResp =
-                mIpSecService.openUdpEncapsulationSocket(localport, new Binder());
+                mIpSecService.openUdpEncapsulationSocket(0, new Binder());
 
         IpSecService.UserRecord userRecord =
                 mIpSecService.mUserResourceTracker.getUserRecord(Os.getuid());
@@ -192,6 +191,7 @@
 
         refcountedRecord.binderDied();
 
+        // Verify quota and RefcountedResource objects cleaned up
         assertEquals(0, userRecord.mSocketQuotaTracker.mCurrent);
         try {
             userRecord.mEncapSocketRecords.getRefcountedResourceOrThrow(udpEncapResp.resourceId);
@@ -412,9 +412,9 @@
     }
 
     @Test
-    public void testDeleteInvalidTransportModeTransform() throws Exception {
+    public void testDeleteInvalidTransform() throws Exception {
         try {
-            mIpSecService.deleteTransportModeTransform(1);
+            mIpSecService.deleteTransform(1);
             fail("IllegalArgumentException not thrown");
         } catch (IllegalArgumentException e) {
         }
diff --git a/tests/net/java/com/android/server/connectivity/IpConnectivityMetricsTest.java b/tests/net/java/com/android/server/connectivity/IpConnectivityMetricsTest.java
index 10d6deb..9f2cb92 100644
--- a/tests/net/java/com/android/server/connectivity/IpConnectivityMetricsTest.java
+++ b/tests/net/java/com/android/server/connectivity/IpConnectivityMetricsTest.java
@@ -66,6 +66,7 @@
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 import org.junit.Before;
+import org.junit.Ignore;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
@@ -174,6 +175,7 @@
     }
 
     @Test
+    @Ignore
     public void testDefaultNetworkEvents() throws Exception {
         final long cell = BitUtils.packBits(new int[]{NetworkCapabilities.TRANSPORT_CELLULAR});
         final long wifi = BitUtils.packBits(new int[]{NetworkCapabilities.TRANSPORT_WIFI});
@@ -292,6 +294,7 @@
     }
 
     @Test
+    @Ignore
     public void testEndToEndLogging() throws Exception {
         // TODO: instead of comparing textpb to textpb, parse textpb and compare proto to proto.
         IpConnectivityLog logger = new IpConnectivityLog(mService.impl);
diff --git a/tests/permission/src/com/android/framework/permission/tests/ServiceManagerPermissionTests.java b/tests/permission/src/com/android/framework/permission/tests/ServiceManagerPermissionTests.java
index 4098b98..0504c79 100644
--- a/tests/permission/src/com/android/framework/permission/tests/ServiceManagerPermissionTests.java
+++ b/tests/permission/src/com/android/framework/permission/tests/ServiceManagerPermissionTests.java
@@ -62,6 +62,11 @@
                 public boolean isRuntimePermission(String permission) {
                     return false;
                 }
+
+                @Override
+                public int getPackageUid(String packageName, int flags) {
+                    return -1;
+                }
             };
             ServiceManagerNative.asInterface(BinderInternal.getContextObject())
                     .setPermissionController(pc);
diff --git a/tools/aapt2/link/ManifestFixer.cpp b/tools/aapt2/link/ManifestFixer.cpp
index ccc3470..713db5b 100644
--- a/tools/aapt2/link/ManifestFixer.cpp
+++ b/tools/aapt2/link/ManifestFixer.cpp
@@ -319,6 +319,7 @@
   manifest_action["original-package"];
   manifest_action["overlay"];
   manifest_action["protected-broadcast"];
+  manifest_action["adopt-permissions"];
   manifest_action["uses-permission"];
   manifest_action["uses-permission-sdk-23"];
   manifest_action["permission"];
diff --git a/wifi/java/android/net/wifi/WifiActivityEnergyInfo.java b/wifi/java/android/net/wifi/WifiActivityEnergyInfo.java
index 29bf02c..03c9fbe 100644
--- a/wifi/java/android/net/wifi/WifiActivityEnergyInfo.java
+++ b/wifi/java/android/net/wifi/WifiActivityEnergyInfo.java
@@ -56,6 +56,11 @@
     /**
      * @hide
      */
+    public long mControllerScanTimeMs;
+
+    /**
+     * @hide
+     */
     public long mControllerIdleTimeMs;
 
     /**
@@ -69,13 +74,14 @@
     public static final int STACK_STATE_STATE_IDLE = 3;
 
     public WifiActivityEnergyInfo(long timestamp, int stackState,
-                                  long txTime, long[] txTimePerLevel, long rxTime, long idleTime,
-                                  long energyUsed) {
+                                  long txTime, long[] txTimePerLevel, long rxTime, long scanTime,
+                                  long idleTime, long energyUsed) {
         mTimestamp = timestamp;
         mStackState = stackState;
         mControllerTxTimeMs = txTime;
         mControllerTxTimePerLevelMs = txTimePerLevel;
         mControllerRxTimeMs = rxTime;
+        mControllerScanTimeMs = scanTime;
         mControllerIdleTimeMs = idleTime;
         mControllerEnergyUsed = energyUsed;
     }
@@ -88,6 +94,7 @@
             + " mControllerTxTimeMs=" + mControllerTxTimeMs
             + " mControllerTxTimePerLevelMs=" + Arrays.toString(mControllerTxTimePerLevelMs)
             + " mControllerRxTimeMs=" + mControllerRxTimeMs
+            + " mControllerScanTimeMs=" + mControllerScanTimeMs
             + " mControllerIdleTimeMs=" + mControllerIdleTimeMs
             + " mControllerEnergyUsed=" + mControllerEnergyUsed
             + " }";
@@ -101,10 +108,11 @@
             long txTime = in.readLong();
             long[] txTimePerLevel = in.createLongArray();
             long rxTime = in.readLong();
+            long scanTime = in.readLong();
             long idleTime = in.readLong();
             long energyUsed = in.readLong();
             return new WifiActivityEnergyInfo(timestamp, stackState,
-                    txTime, txTimePerLevel, rxTime, idleTime, energyUsed);
+                    txTime, txTimePerLevel, rxTime, scanTime, idleTime, energyUsed);
         }
         public WifiActivityEnergyInfo[] newArray(int size) {
             return new WifiActivityEnergyInfo[size];
@@ -117,6 +125,7 @@
         out.writeLong(mControllerTxTimeMs);
         out.writeLongArray(mControllerTxTimePerLevelMs);
         out.writeLong(mControllerRxTimeMs);
+        out.writeLong(mControllerScanTimeMs);
         out.writeLong(mControllerIdleTimeMs);
         out.writeLong(mControllerEnergyUsed);
     }
@@ -157,6 +166,13 @@
     }
 
     /**
+     * @return scan time in ms
+     */
+    public long getControllerScanTimeMillis() {
+        return mControllerScanTimeMs;
+    }
+
+    /**
      * @return idle time in ms
      */
     public long getControllerIdleTimeMillis() {
@@ -183,6 +199,7 @@
     public boolean isValid() {
         return ((mControllerTxTimeMs >=0) &&
                 (mControllerRxTimeMs >=0) &&
+                (mControllerScanTimeMs >=0) &&
                 (mControllerIdleTimeMs >=0));
     }
-}
+}
\ No newline at end of file
diff --git a/wifi/java/android/net/wifi/WifiConfiguration.java b/wifi/java/android/net/wifi/WifiConfiguration.java
index 6438631..2c60118 100644
--- a/wifi/java/android/net/wifi/WifiConfiguration.java
+++ b/wifi/java/android/net/wifi/WifiConfiguration.java
@@ -267,8 +267,15 @@
     public static final int AP_BAND_5GHZ = 1;
 
     /**
+     * Device is allowed to choose the optimal band (2Ghz or 5Ghz) based on device capability,
+     * operating country code and current radio conditions.
+     * @hide
+     */
+    public static final int AP_BAND_ANY = -1;
+
+    /**
      * The band which AP resides on
-     * 0-2G  1-5G
+     * -1:Any 0:2G 1:5G
      * By default, 2G is chosen
      * @hide
      */
diff --git a/wifi/java/android/net/wifi/WifiManager.java b/wifi/java/android/net/wifi/WifiManager.java
index 99080d6..e4b510d 100644
--- a/wifi/java/android/net/wifi/WifiManager.java
+++ b/wifi/java/android/net/wifi/WifiManager.java
@@ -1625,6 +1625,7 @@
      *
      * @return hex-string encoded configuration token or null if there is no current network
      * @hide
+     * @deprecated This API is deprecated
      */
     public String getCurrentNetworkWpsNfcConfigurationToken() {
         try {
@@ -2210,20 +2211,34 @@
     /** @hide */
     public static final int SAVE_NETWORK_SUCCEEDED          = BASE + 9;
 
-    /** @hide */
+    /** @hide
+     * @deprecated This is deprecated
+     */
     public static final int START_WPS                       = BASE + 10;
-    /** @hide */
+    /** @hide
+     * @deprecated This is deprecated
+     */
     public static final int START_WPS_SUCCEEDED             = BASE + 11;
-    /** @hide */
+    /** @hide
+     * @deprecated This is deprecated
+     */
     public static final int WPS_FAILED                      = BASE + 12;
-    /** @hide */
+    /** @hide
+     * @deprecated This is deprecated
+     */
     public static final int WPS_COMPLETED                   = BASE + 13;
 
-    /** @hide */
+    /** @hide
+     * @deprecated This is deprecated
+     */
     public static final int CANCEL_WPS                      = BASE + 14;
-    /** @hide */
+    /** @hide
+     * @deprecated This is deprecated
+     */
     public static final int CANCEL_WPS_FAILED               = BASE + 15;
-    /** @hide */
+    /** @hide
+     * @deprecated This is deprecated
+     */
     public static final int CANCEL_WPS_SUCCEDED             = BASE + 16;
 
     /** @hide */
@@ -2263,15 +2278,25 @@
     public static final int BUSY                        = 2;
 
     /* WPS specific errors */
-    /** WPS overlap detected */
+    /** WPS overlap detected
+     * @deprecated This is deprecated
+     */
     public static final int WPS_OVERLAP_ERROR           = 3;
-    /** WEP on WPS is prohibited */
+    /** WEP on WPS is prohibited
+     * @deprecated This is deprecated
+     */
     public static final int WPS_WEP_PROHIBITED          = 4;
-    /** TKIP only prohibited */
+    /** TKIP only prohibited
+     * @deprecated This is deprecated
+     */
     public static final int WPS_TKIP_ONLY_PROHIBITED    = 5;
-    /** Authentication failure on WPS */
+    /** Authentication failure on WPS
+     * @deprecated This is deprecated
+     */
     public static final int WPS_AUTH_FAILURE            = 6;
-    /** WPS timed out */
+    /** WPS timed out
+     * @deprecated This is deprecated
+     */
     public static final int WPS_TIMED_OUT               = 7;
 
     /**
@@ -2312,12 +2337,19 @@
         public void onFailure(int reason);
     }
 
-    /** Interface for callback invocation on a start WPS action */
+    /** Interface for callback invocation on a start WPS action
+     * @deprecated This is deprecated
+     */
     public static abstract class WpsCallback {
-        /** WPS start succeeded */
+
+        /** WPS start succeeded
+         * @deprecated This API is deprecated
+         */
         public abstract void onStarted(String pin);
 
-        /** WPS operation completed successfully */
+        /** WPS operation completed successfully
+         * @deprecated This API is deprecated
+         */
         public abstract void onSucceeded();
 
         /**
@@ -2326,6 +2358,7 @@
          * {@link #WPS_TKIP_ONLY_PROHIBITED}, {@link #WPS_OVERLAP_ERROR},
          * {@link #WPS_WEP_PROHIBITED}, {@link #WPS_TIMED_OUT} or {@link #WPS_AUTH_FAILURE}
          * and some generic errors.
+         * @deprecated This API is deprecated
          */
         public abstract void onFailed(int reason);
     }
@@ -3032,6 +3065,7 @@
      * @param listener for callbacks on success or failure. Can be null.
      * @throws IllegalStateException if the WifiManager instance needs to be
      * initialized again
+     * @deprecated This API is deprecated
      */
     public void startWps(WpsInfo config, WpsCallback listener) {
         if (config == null) throw new IllegalArgumentException("config cannot be null");
@@ -3044,6 +3078,7 @@
      * @param listener for callbacks on success or failure. Can be null.
      * @throws IllegalStateException if the WifiManager instance needs to be
      * initialized again
+     * @deprecated This API is deprecated
      */
     public void cancelWps(WpsCallback listener) {
         getChannel().sendMessage(CANCEL_WPS, 0, putListener(listener));
diff --git a/wifi/java/android/net/wifi/rtt/LocationCivic.java b/wifi/java/android/net/wifi/rtt/LocationCivic.java
new file mode 100644
index 0000000..610edb6
--- /dev/null
+++ b/wifi/java/android/net/wifi/rtt/LocationCivic.java
@@ -0,0 +1,118 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.wifi.rtt;
+
+import android.annotation.Nullable;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.util.Arrays;
+import java.util.Objects;
+
+/**
+ * Location Civic Report (LCR).
+ * <p>
+ * The information matches the IEEE 802.11-2016 LCR report.
+ * <p>
+ * Note: depending on the mechanism by which this information is returned (i.e. the API which
+ * returns an instance of this class) it is possibly Self Reported (by the peer). In such a case
+ * the information is NOT validated - use with caution. Consider validating it with other sources
+ * of information before using it.
+ */
+public final class LocationCivic implements Parcelable {
+    private final byte[] mData;
+
+    /**
+     * Parse the raw LCR information element (byte array) and extract the LocationCivic structure.
+     *
+     * Note: any parsing errors or invalid/unexpected errors will result in a null being returned.
+     *
+     * @hide
+     */
+    @Nullable
+    public static LocationCivic parseInformationElement(byte id, byte[] data) {
+        // TODO
+        return null;
+    }
+
+    /** @hide */
+    public LocationCivic(byte[] data) {
+        mData = data;
+    }
+
+    /**
+     * Return the Location Civic data reported by the peer.
+     *
+     * @return An arbitrary location information.
+     */
+    public byte[] getData() {
+        return mData;
+    }
+
+    /** @hide */
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    /** @hide */
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeByteArray(mData);
+    }
+
+    public static final Parcelable.Creator<LocationCivic> CREATOR =
+            new Parcelable.Creator<LocationCivic>() {
+                @Override
+                public LocationCivic[] newArray(int size) {
+                    return new LocationCivic[size];
+                }
+
+                @Override
+                public LocationCivic createFromParcel(Parcel in) {
+                    byte[] data = in.createByteArray();
+
+                    return new LocationCivic(data);
+                }
+            };
+
+    /** @hide */
+    @Override
+    public String toString() {
+        return new StringBuilder("LCR: data=").append(Arrays.toString(mData)).toString();
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) {
+            return true;
+        }
+
+        if (!(o instanceof LocationCivic)) {
+            return false;
+        }
+
+        LocationCivic lhs = (LocationCivic) o;
+
+        return Arrays.equals(mData, lhs.mData);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(mData);
+    }
+}
diff --git a/wifi/java/android/net/wifi/rtt/LocationConfigurationInformation.java b/wifi/java/android/net/wifi/rtt/LocationConfigurationInformation.java
new file mode 100644
index 0000000..8aba56a
--- /dev/null
+++ b/wifi/java/android/net/wifi/rtt/LocationConfigurationInformation.java
@@ -0,0 +1,272 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.wifi.rtt;
+
+import android.annotation.IntDef;
+import android.annotation.Nullable;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.Objects;
+
+/**
+ * The Device Location Configuration Information (LCI) specifies the location information of a peer
+ * device (e.g. an Access Point).
+ * <p>
+ * The information matches the IEEE 802.11-2016 LCI report (Location configuration information
+ * report).
+ * <p>
+ * Note: depending on the mechanism by which this information is returned (i.e. the API which
+ * returns an instance of this class) it is possibly Self Reported (by the peer). In such a case
+ * the information is NOT validated - use with caution. Consider validating it with other sources
+ * of information before using it.
+ */
+public final class LocationConfigurationInformation implements Parcelable {
+    /** @hide */
+    @IntDef({
+            ALTITUDE_UNKNOWN, ALTITUDE_IN_METERS, ALTITUDE_IN_FLOORS })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface AltitudeTypes {
+    }
+
+    /**
+     * Define an Altitude Type returned by {@link #getAltitudeType()}. Indicates that the location
+     * does not specify an altitude or altitude uncertainty. The corresponding methods,
+     * {@link #getAltitude()} and {@link #getAltitudeUncertainty()} are not valid and will throw
+     * an exception.
+     */
+    public static final int ALTITUDE_UNKNOWN = 0;
+
+    /**
+     * Define an Altitude Type returned by {@link #getAltitudeType()}. Indicates that the location
+     * specifies the altitude and altitude uncertainty in meters. The corresponding methods,
+     * {@link #getAltitude()} and {@link #getAltitudeUncertainty()} return a valid value in meters.
+     */
+    public static final int ALTITUDE_IN_METERS = 1;
+
+    /**
+     * Define an Altitude Type returned by {@link #getAltitudeType()}. Indicates that the
+     * location specifies the altitude in floors, and does not specify an altitude uncertainty.
+     * The {@link #getAltitude()} method returns valid value in floors, and the
+     * {@link #getAltitudeUncertainty()} method is not valid and will throw an exception.
+     */
+    public static final int ALTITUDE_IN_FLOORS = 2;
+
+    private final double mLatitude;
+    private final double mLatitudeUncertainty;
+    private final double mLongitude;
+    private final double mLongitudeUncertainty;
+    private final int mAltitudeType;
+    private final double mAltitude;
+    private final double mAltitudeUncertainty;
+
+    /**
+     * Parse the raw LCI information element (byte array) and extract the
+     * LocationConfigurationInformation structure.
+     *
+     * Note: any parsing errors or invalid/unexpected errors will result in a null being returned.
+     *
+     * @hide
+     */
+    @Nullable
+    public static LocationConfigurationInformation parseInformationElement(byte id, byte[] data) {
+        // TODO
+        return null;
+    }
+
+    /** @hide */
+    public LocationConfigurationInformation(double latitude, double latitudeUncertainty,
+            double longitude, double longitudeUncertainty, @AltitudeTypes int altitudeType,
+            double altitude, double altitudeUncertainty) {
+        mLatitude = latitude;
+        mLatitudeUncertainty = latitudeUncertainty;
+        mLongitude = longitude;
+        mLongitudeUncertainty = longitudeUncertainty;
+        mAltitudeType = altitudeType;
+        mAltitude = altitude;
+        mAltitudeUncertainty = altitudeUncertainty;
+    }
+
+    /**
+     * Get latitude in degrees. Values are per WGS 84 reference system. Valid values are between
+     * -90 and 90.
+     *
+     * @return Latitude in degrees.
+     */
+    public double getLatitude() {
+        return mLatitude;
+    }
+
+    /**
+     * Get the uncertainty of the latitude {@link #getLatitude()} in degrees. A value of 0 indicates
+     * an unknown uncertainty.
+     *
+     * @return Uncertainty of the latitude in degrees.
+     */
+    public double getLatitudeUncertainty() {
+        return mLatitudeUncertainty;
+    }
+
+    /**
+     * Get longitude in degrees. Values are per WGS 84 reference system. Valid values are between
+     * -180 and 180.
+     *
+     * @return Longitude in degrees.
+     */
+    public double getLongitude() {
+        return mLongitude;
+    }
+
+    /**
+     * Get the uncertainty of the longitude {@link #getLongitude()} ()} in degrees.  A value of 0
+     * indicates an unknown uncertainty.
+     *
+     * @return Uncertainty of the longitude in degrees.
+     */
+    public double getLongitudeUncertainty() {
+        return mLongitudeUncertainty;
+    }
+
+    /**
+     * Specifies the type of the altitude measurement returned by {@link #getAltitude()} and
+     * {@link #getAltitudeUncertainty()}. The possible values are:
+     * <li>{@link #ALTITUDE_UNKNOWN}: The altitude and altitude uncertainty are not provided.
+     * <li>{@link #ALTITUDE_IN_METERS}: The altitude and altitude uncertainty are provided in
+     * meters. Values are per WGS 84 reference system.
+     * <li>{@link #ALTITUDE_IN_FLOORS}: The altitude is provided in floors, the altitude uncertainty
+     * is not provided.
+     *
+     * @return The type of the altitude and altitude uncertainty.
+     */
+    public @AltitudeTypes int getAltitudeType() {
+        return mAltitudeType;
+    }
+
+    /**
+     * The altitude is interpreted according to the {@link #getAltitudeType()}. The possible values
+     * are:
+     * <li>{@link #ALTITUDE_UNKNOWN}: The altitude is not provided - this method will throw an
+     * exception.
+     * <li>{@link #ALTITUDE_IN_METERS}: The altitude is provided in meters. Values are per WGS 84
+     * reference system.
+     * <li>{@link #ALTITUDE_IN_FLOORS}: The altitude is provided in floors.
+     *
+     * @return Altitude value whose meaning is specified by {@link #getAltitudeType()}.
+     */
+    public double getAltitude() {
+        if (mAltitudeType == ALTITUDE_UNKNOWN) {
+            throw new IllegalStateException(
+                    "getAltitude(): invoked on an invalid type: getAltitudeType()==UNKNOWN");
+        }
+        return mAltitude;
+    }
+
+    /**
+     * Only valid if the the {@link #getAltitudeType()} is equal to {@link #ALTITUDE_IN_METERS} -
+     * otherwise this method will throw an exception.
+     * <p>
+     * Get the uncertainty of the altitude {@link #getAltitude()} in meters.  A value of 0
+     * indicates an unknown uncertainty.
+     *
+     * @return Uncertainty of the altitude in meters.
+     */
+    public double getAltitudeUncertainty() {
+        if (mAltitudeType != ALTITUDE_IN_METERS) {
+            throw new IllegalStateException(
+                    "getAltitude(): invoked on an invalid type: getAltitudeType()!=IN_METERS");
+        }
+        return mAltitudeUncertainty;
+    }
+
+    /** @hide */
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    /** @hide */
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeDouble(mLatitude);
+        dest.writeDouble(mLatitudeUncertainty);
+        dest.writeDouble(mLongitude);
+        dest.writeDouble(mLongitudeUncertainty);
+        dest.writeInt(mAltitudeType);
+        dest.writeDouble(mAltitude);
+        dest.writeDouble(mAltitudeUncertainty);
+    }
+
+    public static final Creator<LocationConfigurationInformation> CREATOR =
+            new Creator<LocationConfigurationInformation>() {
+        @Override
+        public LocationConfigurationInformation[] newArray(int size) {
+            return new LocationConfigurationInformation[size];
+        }
+
+        @Override
+        public LocationConfigurationInformation createFromParcel(Parcel in) {
+            double latitude = in.readDouble();
+            double latitudeUnc = in.readDouble();
+            double longitude = in.readDouble();
+            double longitudeUnc = in.readDouble();
+            int altitudeType = in.readInt();
+            double altitude = in.readDouble();
+            double altitudeUnc = in.readDouble();
+
+            return new LocationConfigurationInformation(latitude, latitudeUnc, longitude,
+                    longitudeUnc, altitudeType, altitude, altitudeUnc);
+        }
+    };
+
+    /** @hide */
+    @Override
+    public String toString() {
+        return new StringBuilder("LCI: latitude=").append(mLatitude).append(
+                ", latitudeUncertainty=").append(mLatitudeUncertainty).append(
+                ", longitude=").append(mLongitude).append(", longitudeUncertainty=").append(
+                mLongitudeUncertainty).append(", altitudeType=").append(mAltitudeType).append(
+                ", altitude=").append(mAltitude).append(", altitudeUncertainty=").append(
+                mAltitudeUncertainty).toString();
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) {
+            return true;
+        }
+
+        if (!(o instanceof LocationConfigurationInformation)) {
+            return false;
+        }
+
+        LocationConfigurationInformation lhs = (LocationConfigurationInformation) o;
+
+        return mLatitude == lhs.mLatitude && mLatitudeUncertainty == lhs.mLatitudeUncertainty
+                && mLongitude == lhs.mLongitude
+                && mLongitudeUncertainty == lhs.mLongitudeUncertainty
+                && mAltitudeType == lhs.mAltitudeType && mAltitude == lhs.mAltitude
+                && mAltitudeUncertainty == lhs.mAltitudeUncertainty;
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(mLatitude, mLatitudeUncertainty, mLongitude, mLongitudeUncertainty,
+                mAltitudeType, mAltitude, mAltitudeUncertainty);
+    }
+}
diff --git a/wifi/java/android/net/wifi/rtt/RangingResult.java b/wifi/java/android/net/wifi/rtt/RangingResult.java
index d5ca8f7..201833b 100644
--- a/wifi/java/android/net/wifi/rtt/RangingResult.java
+++ b/wifi/java/android/net/wifi/rtt/RangingResult.java
@@ -65,29 +65,37 @@
     private final int mDistanceMm;
     private final int mDistanceStdDevMm;
     private final int mRssi;
+    private final LocationConfigurationInformation mLci;
+    private final LocationCivic mLcr;
     private final long mTimestamp;
 
     /** @hide */
     public RangingResult(@RangeResultStatus int status, @NonNull MacAddress mac, int distanceMm,
-            int distanceStdDevMm, int rssi, long timestamp) {
+            int distanceStdDevMm, int rssi, LocationConfigurationInformation lci, LocationCivic lcr,
+            long timestamp) {
         mStatus = status;
         mMac = mac;
         mPeerHandle = null;
         mDistanceMm = distanceMm;
         mDistanceStdDevMm = distanceStdDevMm;
         mRssi = rssi;
+        mLci = lci;
+        mLcr = lcr;
         mTimestamp = timestamp;
     }
 
     /** @hide */
     public RangingResult(@RangeResultStatus int status, PeerHandle peerHandle, int distanceMm,
-            int distanceStdDevMm, int rssi, long timestamp) {
+            int distanceStdDevMm, int rssi, LocationConfigurationInformation lci, LocationCivic lcr,
+            long timestamp) {
         mStatus = status;
         mMac = null;
         mPeerHandle = peerHandle;
         mDistanceMm = distanceMm;
         mDistanceStdDevMm = distanceStdDevMm;
         mRssi = rssi;
+        mLci = lci;
+        mLcr = lcr;
         mTimestamp = timestamp;
     }
 
@@ -169,6 +177,38 @@
     }
 
     /**
+     * @return The Location Configuration Information (LCI) as self-reported by the peer.
+     * <p>
+     * Note: the information is NOT validated - use with caution. Consider validating it with
+     * other sources of information before using it.
+     */
+    @Nullable
+    public LocationConfigurationInformation getReportedLocationConfigurationInformation() {
+        if (mStatus != STATUS_SUCCESS) {
+            throw new IllegalStateException(
+                    "getReportedLocationConfigurationInformation(): invoked on an invalid result: "
+                            + "getStatus()=" + mStatus);
+        }
+        return mLci;
+    }
+
+    /**
+     * @return The Location Civic report (LCR) as self-reported by the peer.
+     * <p>
+     * Note: the information is NOT validated - use with caution. Consider validating it with
+     * other sources of information before using it.
+     */
+    @Nullable
+    public LocationCivic getReportedLocationCivic() {
+        if (mStatus != STATUS_SUCCESS) {
+            throw new IllegalStateException(
+                    "getReportedLocationCivic(): invoked on an invalid result: getStatus()="
+                            + mStatus);
+        }
+        return mLcr;
+    }
+
+    /**
      * @return The timestamp, in us since boot, at which the ranging operation was performed.
      * <p>
      * Only valid if {@link #getStatus()} returns {@link #STATUS_SUCCESS}, otherwise will throw an
@@ -205,6 +245,18 @@
         dest.writeInt(mDistanceMm);
         dest.writeInt(mDistanceStdDevMm);
         dest.writeInt(mRssi);
+        if (mLci == null) {
+            dest.writeBoolean(false);
+        } else {
+            dest.writeBoolean(true);
+            mLci.writeToParcel(dest, flags);
+        }
+        if (mLcr == null) {
+            dest.writeBoolean(false);
+        } else {
+            dest.writeBoolean(true);
+            mLcr.writeToParcel(dest, flags);
+        }
         dest.writeLong(mTimestamp);
     }
 
@@ -230,13 +282,23 @@
             int distanceMm = in.readInt();
             int distanceStdDevMm = in.readInt();
             int rssi = in.readInt();
+            boolean lciPresent = in.readBoolean();
+            LocationConfigurationInformation lci = null;
+            if (lciPresent) {
+                lci = LocationConfigurationInformation.CREATOR.createFromParcel(in);
+            }
+            boolean lcrPresent = in.readBoolean();
+            LocationCivic lcr = null;
+            if (lcrPresent) {
+                lcr = LocationCivic.CREATOR.createFromParcel(in);
+            }
             long timestamp = in.readLong();
             if (peerHandlePresent) {
                 return new RangingResult(status, peerHandle, distanceMm, distanceStdDevMm, rssi,
-                        timestamp);
+                        lci, lcr, timestamp);
             } else {
                 return new RangingResult(status, mac, distanceMm, distanceStdDevMm, rssi,
-                        timestamp);
+                        lci, lcr, timestamp);
             }
         }
     };
@@ -248,8 +310,8 @@
                 mMac).append(", peerHandle=").append(
                 mPeerHandle == null ? "<null>" : mPeerHandle.peerId).append(", distanceMm=").append(
                 mDistanceMm).append(", distanceStdDevMm=").append(mDistanceStdDevMm).append(
-                ", rssi=").append(mRssi).append(", timestamp=").append(mTimestamp).append(
-                "]").toString();
+                ", rssi=").append(mRssi).append(", lci=").append(mLci).append(", lcr=").append(
+                mLcr).append(", timestamp=").append(mTimestamp).append("]").toString();
     }
 
     @Override
@@ -267,12 +329,13 @@
         return mStatus == lhs.mStatus && Objects.equals(mMac, lhs.mMac) && Objects.equals(
                 mPeerHandle, lhs.mPeerHandle) && mDistanceMm == lhs.mDistanceMm
                 && mDistanceStdDevMm == lhs.mDistanceStdDevMm && mRssi == lhs.mRssi
+                && Objects.equals(mLci, lhs.mLci) && Objects.equals(mLcr, lhs.mLcr)
                 && mTimestamp == lhs.mTimestamp;
     }
 
     @Override
     public int hashCode() {
         return Objects.hash(mStatus, mMac, mPeerHandle, mDistanceMm, mDistanceStdDevMm, mRssi,
-                mTimestamp);
+                mLci, mLcr, mTimestamp);
     }
 }
diff --git a/wifi/tests/src/android/net/wifi/rtt/WifiRttManagerTest.java b/wifi/tests/src/android/net/wifi/rtt/WifiRttManagerTest.java
index 72e95b9..41c7f86 100644
--- a/wifi/tests/src/android/net/wifi/rtt/WifiRttManagerTest.java
+++ b/wifi/tests/src/android/net/wifi/rtt/WifiRttManagerTest.java
@@ -17,6 +17,7 @@
 package android.net.wifi.rtt;
 
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.mock;
@@ -32,7 +33,6 @@
 import android.os.IBinder;
 import android.os.Parcel;
 import android.os.test.TestLooper;
-import android.test.suitebuilder.annotation.SmallTest;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -46,7 +46,6 @@
 /**
  * Unit test harness for WifiRttManager class.
  */
-@SmallTest
 public class WifiRttManagerTest {
     private WifiRttManager mDut;
     private TestLooper mMockLooper;
@@ -80,7 +79,7 @@
         List<RangingResult> results = new ArrayList<>();
         results.add(
                 new RangingResult(RangingResult.STATUS_SUCCESS, MacAddress.BROADCAST_ADDRESS, 15, 5,
-                        10, 666));
+                        10, null, null, 666));
         RangingResultCallback callbackMock = mock(RangingResultCallback.class);
         ArgumentCaptor<IRttCallback> callbackCaptor = ArgumentCaptor.forClass(IRttCallback.class);
 
@@ -236,10 +235,23 @@
         int distanceStdDevCm = 10;
         int rssi = 5;
         long timestamp = System.currentTimeMillis();
+        double latitude = 5.5;
+        double latitudeUncertainty = 6.5;
+        double longitude = 7.5;
+        double longitudeUncertainty = 8.5;
+        int altitudeType = LocationConfigurationInformation.ALTITUDE_IN_METERS;
+        double altitude = 9.5;
+        double altitudeUncertainty = 55.5;
+        byte[] lcrData = { 0x1, 0x2, 0x3, 0xA, 0xB, 0xC };
+
+        LocationConfigurationInformation lci = new LocationConfigurationInformation(latitude,
+                latitudeUncertainty, longitude, longitudeUncertainty, altitudeType, altitude,
+                altitudeUncertainty);
+        LocationCivic lcr = new LocationCivic(lcrData);
 
         // RangingResults constructed with a MAC address
         RangingResult result = new RangingResult(status, mac, distanceCm, distanceStdDevCm, rssi,
-                timestamp);
+                lci, lcr, timestamp);
 
         Parcel parcelW = Parcel.obtain();
         result.writeToParcel(parcelW, 0);
@@ -255,7 +267,7 @@
 
         // RangingResults constructed with a PeerHandle
         result = new RangingResult(status, peerHandle, distanceCm, distanceStdDevCm, rssi,
-                timestamp);
+                null, null, timestamp);
 
         parcelW = Parcel.obtain();
         result.writeToParcel(parcelW, 0);
@@ -269,4 +281,83 @@
 
         assertEquals(result, rereadResult);
     }
+
+    /**
+     * Validate that LocationConfigurationInformation parcel works (produces same object on
+     * write/read).
+     */
+    @Test
+    public void testLciParcel() {
+        double latitude = 1.5;
+        double latitudeUncertainty = 2.5;
+        double longitude = 3.5;
+        double longitudeUncertainty = 4.5;
+        int altitudeType = LocationConfigurationInformation.ALTITUDE_IN_FLOORS;
+        double altitude = 5.5;
+        double altitudeUncertainty = 6.5;
+
+        LocationConfigurationInformation lci = new LocationConfigurationInformation(latitude,
+                latitudeUncertainty, longitude, longitudeUncertainty, altitudeType, altitude,
+                altitudeUncertainty);
+
+        Parcel parcelW = Parcel.obtain();
+        lci.writeToParcel(parcelW, 0);
+        byte[] bytes = parcelW.marshall();
+        parcelW.recycle();
+
+        Parcel parcelR = Parcel.obtain();
+        parcelR.unmarshall(bytes, 0, bytes.length);
+        parcelR.setDataPosition(0);
+        LocationConfigurationInformation rereadLci =
+                LocationConfigurationInformation.CREATOR.createFromParcel(parcelR);
+
+        assertEquals(lci, rereadLci);
+    }
+
+    /**
+     * Validate that the LCI throws an exception when accessing invalid fields an certain altitude
+     * types.
+     */
+    @Test
+    public void testLciInvalidAltitudeFieldAccess() {
+        boolean exceptionThrown;
+        LocationConfigurationInformation lci = new LocationConfigurationInformation(0, 0, 0, 0,
+                LocationConfigurationInformation.ALTITUDE_UNKNOWN, 0, 0);
+
+        // UNKNOWN - invalid altitude & altitude uncertainty
+        exceptionThrown = false;
+        try {
+            lci.getAltitude();
+        } catch (IllegalStateException e) {
+            exceptionThrown = true;
+        }
+        assertTrue("UNKNOWN / getAltitude()", exceptionThrown);
+
+        exceptionThrown = false;
+        try {
+            lci.getAltitudeUncertainty();
+        } catch (IllegalStateException e) {
+            exceptionThrown = true;
+        }
+        assertTrue("UNKNOWN / getAltitudeUncertainty()", exceptionThrown);
+
+        lci = new LocationConfigurationInformation(0, 0, 0, 0,
+                LocationConfigurationInformation.ALTITUDE_IN_FLOORS, 0, 0);
+
+        // FLOORS - invalid altitude uncertainty
+        exceptionThrown = false;
+        try {
+            lci.getAltitudeUncertainty();
+        } catch (IllegalStateException e) {
+            exceptionThrown = true;
+        }
+        assertTrue("FLOORS / getAltitudeUncertainty()", exceptionThrown);
+
+        // and good accesses just in case
+        lci.getAltitude();
+        lci = new LocationConfigurationInformation(0, 0, 0, 0,
+                LocationConfigurationInformation.ALTITUDE_IN_METERS, 0, 0);
+        lci.getAltitude();
+        lci.getAltitudeUncertainty();
+    }
 }