Merge "Fix incorrect asserts"
diff --git a/Android.mk b/Android.mk
index 7fda1dd..cca87ca 100644
--- a/Android.mk
+++ b/Android.mk
@@ -238,6 +238,7 @@
 	core/java/android/net/INetworkScoreService.aidl \
 	core/java/android/net/INetworkStatsService.aidl \
 	core/java/android/net/INetworkStatsSession.aidl \
+	core/java/android/net/ITetheringStatsProvider.aidl \
 	core/java/android/net/nsd/INsdManager.aidl \
 	core/java/android/nfc/IAppCallback.aidl \
 	core/java/android/nfc/INfcAdapter.aidl \
@@ -644,7 +645,6 @@
 
 aidl_files := \
         frameworks/base/telephony/java/android/telephony/mbms/DownloadRequest.aidl \
-        frameworks/base/telephony/java/android/telephony/mbms/DownloadStatus.aidl \
         frameworks/base/telephony/java/android/telephony/mbms/FileInfo.aidl \
         frameworks/base/telephony/java/android/telephony/mbms/FileServiceInfo.aidl \
         frameworks/base/telephony/java/android/telephony/mbms/ServiceInfo.aidl \
@@ -712,6 +712,7 @@
 	frameworks/base/core/java/android/print/PrinterInfo.aidl \
 	frameworks/base/core/java/android/print/PrintJobId.aidl \
 	frameworks/base/core/java/android/printservice/recommendation/RecommendationInfo.aidl \
+	frameworks/base/core/java/android/hardware/radio/ProgramSelector.aidl \
 	frameworks/base/core/java/android/hardware/radio/RadioManager.aidl \
 	frameworks/base/core/java/android/hardware/radio/RadioMetadata.aidl \
 	frameworks/base/core/java/android/hardware/usb/UsbDevice.aidl \
diff --git a/apct-tests/perftests/multiuser/src/android/multiuser/BenchmarkResults.java b/apct-tests/perftests/multiuser/src/android/multiuser/BenchmarkResults.java
index 0d764ce..e417ca7 100644
--- a/apct-tests/perftests/multiuser/src/android/multiuser/BenchmarkResults.java
+++ b/apct-tests/perftests/multiuser/src/android/multiuser/BenchmarkResults.java
@@ -28,7 +28,13 @@
         mResults.add(TimeUnit.NANOSECONDS.toMillis(duration));
     }
 
-    public Bundle getStats() {
+    public Bundle getStatsToReport() {
+        final Bundle stats = new Bundle();
+        stats.putDouble("Mean (ms)", mean());
+        return stats;
+    }
+
+    public Bundle getStatsToLog() {
         final Bundle stats = new Bundle();
         stats.putDouble("Mean (ms)", mean());
         stats.putDouble("Median (ms)", median());
diff --git a/apct-tests/perftests/multiuser/src/android/multiuser/BenchmarkResultsReporter.java b/apct-tests/perftests/multiuser/src/android/multiuser/BenchmarkResultsReporter.java
index 7472865..d3a3ce5 100644
--- a/apct-tests/perftests/multiuser/src/android/multiuser/BenchmarkResultsReporter.java
+++ b/apct-tests/perftests/multiuser/src/android/multiuser/BenchmarkResultsReporter.java
@@ -40,9 +40,11 @@
             @Override
             public void evaluate() throws Throwable {
                 base.evaluate();
-                final Bundle stats = mRunner.getStats();
-                final String summary = getSummaryString(description.getMethodName(), stats);
-                logSummary(description.getTestClass().getSimpleName(), summary, mRunner.getAllDurations());
+                final Bundle stats = mRunner.getStatsToReport();
+                final String summary = getSummaryString(description.getMethodName(),
+                        mRunner.getStatsToLog());
+                logSummary(description.getTestClass().getSimpleName(), summary,
+                        mRunner.getAllDurations());
                 stats.putString(Instrumentation.REPORT_KEY_STREAMRESULT, summary);
                 InstrumentationRegistry.getInstrumentation().sendStatus(
                         Activity.RESULT_OK, stats);
diff --git a/apct-tests/perftests/multiuser/src/android/multiuser/BenchmarkRunner.java b/apct-tests/perftests/multiuser/src/android/multiuser/BenchmarkRunner.java
index ccadc9a..c7bebf3 100644
--- a/apct-tests/perftests/multiuser/src/android/multiuser/BenchmarkRunner.java
+++ b/apct-tests/perftests/multiuser/src/android/multiuser/BenchmarkRunner.java
@@ -93,8 +93,12 @@
         mState = RUNNING;
     }
 
-    public Bundle getStats() {
-        return mResults.getStats();
+    public Bundle getStatsToReport() {
+        return mResults.getStatsToReport();
+    }
+
+    public Bundle getStatsToLog() {
+        return mResults.getStatsToLog();
     }
 
     public ArrayList<Long> getAllDurations() {
diff --git a/apct-tests/perftests/multiuser/src/android/multiuser/UserLifecycleTest.java b/apct-tests/perftests/multiuser/src/android/multiuser/UserLifecycleTest.java
deleted file mode 100644
index 6e802a9..0000000
--- a/apct-tests/perftests/multiuser/src/android/multiuser/UserLifecycleTest.java
+++ /dev/null
@@ -1,313 +0,0 @@
-/*
- * 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.multiuser;
-
-import android.app.ActivityManager;
-import android.app.IActivityManager;
-import android.app.IStopUserCallback;
-import android.app.UserSwitchObserver;
-import android.content.BroadcastReceiver;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.content.pm.UserInfo;
-import android.os.RemoteException;
-import android.os.UserHandle;
-import android.os.UserManager;
-import android.support.test.InstrumentationRegistry;
-import android.support.test.filters.LargeTest;
-import android.support.test.runner.AndroidJUnit4;
-
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-import java.util.ArrayList;
-import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.TimeUnit;
-
-/**
- * Perf tests for user life cycle events.
- *
- * Running the tests:
- *
- * make MultiUserPerfTests &&
- * adb install -r \
- *     ${ANDROID_PRODUCT_OUT}/data/app/MultiUserPerfTests/MultiUserPerfTests.apk &&
- * adb shell am instrument -e class android.multiuser.UserLifecycleTest \
- *     -w com.android.perftests.multiuser/android.support.test.runner.AndroidJUnitRunner
- *
- * or
- *
- * bit MultiUserPerfTests:android.multiuser.UserLifecycleTest
- *
- * Note: If you use bit for running the tests, benchmark results won't be printed on the host side.
- * But in either case, results can be checked on the device side 'adb logcat -s UserLifecycleTest'
- */
-@LargeTest
-@RunWith(AndroidJUnit4.class)
-public class UserLifecycleTest {
-    private final int TIMEOUT_IN_SECOND = 30;
-    private final int CHECK_USER_REMOVED_INTERVAL_MS = 200;
-
-    private UserManager mUm;
-    private ActivityManager mAm;
-    private IActivityManager mIam;
-    private ArrayList<Integer> mUsersToRemove;
-
-    private final BenchmarkRunner mRunner = new BenchmarkRunner();
-    @Rule
-    public BenchmarkResultsReporter mReporter = new BenchmarkResultsReporter(mRunner);
-
-    @Before
-    public void setUp() {
-        final Context context = InstrumentationRegistry.getContext();
-        mUm = UserManager.get(context);
-        mAm = context.getSystemService(ActivityManager.class);
-        mIam = ActivityManager.getService();
-        mUsersToRemove = new ArrayList<>();
-    }
-
-    @After
-    public void tearDown() {
-        for (int userId : mUsersToRemove) {
-            try {
-                mUm.removeUser(userId);
-            } catch (Exception e) {
-                // Ignore
-            }
-        }
-    }
-
-    @Test
-    public void createAndStartUser() throws Exception {
-        while (mRunner.keepRunning()) {
-            final UserInfo userInfo = mUm.createUser("TestUser", 0);
-
-            final CountDownLatch latch = new CountDownLatch(1);
-            registerBroadcastReceiver(Intent.ACTION_USER_STARTED, latch, userInfo.id);
-            mIam.startUserInBackground(userInfo.id);
-            latch.await(TIMEOUT_IN_SECOND, TimeUnit.SECONDS);
-
-            mRunner.pauseTiming();
-            removeUser(userInfo.id);
-            mRunner.resumeTiming();
-        }
-    }
-
-    @Test
-    public void switchUser() throws Exception {
-        while (mRunner.keepRunning()) {
-            mRunner.pauseTiming();
-            final int startUser = mAm.getCurrentUser();
-            final UserInfo userInfo = mUm.createUser("TestUser", 0);
-            mRunner.resumeTiming();
-
-            switchUser(userInfo.id);
-
-            mRunner.pauseTiming();
-            switchUser(startUser);
-            removeUser(userInfo.id);
-            mRunner.resumeTiming();
-        }
-    }
-
-    @Test
-    public void stopUser() throws Exception {
-        while (mRunner.keepRunning()) {
-            mRunner.pauseTiming();
-            final UserInfo userInfo = mUm.createUser("TestUser", 0);
-            final CountDownLatch latch = new CountDownLatch(1);
-            registerBroadcastReceiver(Intent.ACTION_USER_STARTED, latch, userInfo.id);
-            mIam.startUserInBackground(userInfo.id);
-            latch.await(TIMEOUT_IN_SECOND, TimeUnit.SECONDS);
-            mRunner.resumeTiming();
-
-            stopUser(userInfo.id, false);
-
-            mRunner.pauseTiming();
-            removeUser(userInfo.id);
-            mRunner.resumeTiming();
-        }
-    }
-
-    @Test
-    public void lockedBootCompleted() throws Exception {
-        while (mRunner.keepRunning()) {
-            mRunner.pauseTiming();
-            final int startUser = mAm.getCurrentUser();
-            final UserInfo userInfo = mUm.createUser("TestUser", 0);
-            final CountDownLatch latch = new CountDownLatch(1);
-            registerUserSwitchObserver(null, latch, userInfo.id);
-            mRunner.resumeTiming();
-
-            mAm.switchUser(userInfo.id);
-            latch.await(TIMEOUT_IN_SECOND, TimeUnit.SECONDS);
-
-            mRunner.pauseTiming();
-            switchUser(startUser);
-            removeUser(userInfo.id);
-            mRunner.resumeTiming();
-        }
-    }
-
-    @Test
-    public void managedProfileUnlock() throws Exception {
-        while (mRunner.keepRunning()) {
-            mRunner.pauseTiming();
-            final UserInfo userInfo = mUm.createProfileForUser("TestUser",
-                    UserInfo.FLAG_MANAGED_PROFILE, mAm.getCurrentUser());
-            final CountDownLatch latch = new CountDownLatch(1);
-            registerBroadcastReceiver(Intent.ACTION_USER_UNLOCKED, latch, userInfo.id);
-            mRunner.resumeTiming();
-
-            mIam.startUserInBackground(userInfo.id);
-            latch.await(TIMEOUT_IN_SECOND, TimeUnit.SECONDS);
-
-            mRunner.pauseTiming();
-            removeUser(userInfo.id);
-            mRunner.resumeTiming();
-        }
-    }
-
-    @Test
-    public void ephemeralUserStopped() throws Exception {
-        while (mRunner.keepRunning()) {
-            mRunner.pauseTiming();
-            final int startUser = mAm.getCurrentUser();
-            final UserInfo userInfo = mUm.createUser("TestUser",
-                    UserInfo.FLAG_EPHEMERAL | UserInfo.FLAG_DEMO);
-            switchUser(userInfo.id);
-            final CountDownLatch latch = new CountDownLatch(1);
-            InstrumentationRegistry.getContext().registerReceiver(new BroadcastReceiver() {
-                @Override
-                public void onReceive(Context context, Intent intent) {
-                    if (Intent.ACTION_USER_STOPPED.equals(intent.getAction()) && intent.getIntExtra(
-                            Intent.EXTRA_USER_HANDLE, UserHandle.USER_NULL) == userInfo.id) {
-                        latch.countDown();
-                    }
-                }
-            }, new IntentFilter(Intent.ACTION_USER_STOPPED));
-            final CountDownLatch switchLatch = new CountDownLatch(1);
-            registerUserSwitchObserver(switchLatch, null, startUser);
-            mRunner.resumeTiming();
-
-            mAm.switchUser(startUser);
-            latch.await(TIMEOUT_IN_SECOND, TimeUnit.SECONDS);
-
-            mRunner.pauseTiming();
-            switchLatch.await(TIMEOUT_IN_SECOND, TimeUnit.SECONDS);
-            removeUser(userInfo.id);
-            mRunner.resumeTiming();
-        }
-    }
-
-    @Test
-    public void managedProfileStopped() throws Exception {
-        while (mRunner.keepRunning()) {
-            mRunner.pauseTiming();
-            final UserInfo userInfo = mUm.createProfileForUser("TestUser",
-                    UserInfo.FLAG_MANAGED_PROFILE, mAm.getCurrentUser());
-            final CountDownLatch latch = new CountDownLatch(1);
-            registerBroadcastReceiver(Intent.ACTION_USER_UNLOCKED, latch, userInfo.id);
-            mIam.startUserInBackground(userInfo.id);
-            latch.await(TIMEOUT_IN_SECOND, TimeUnit.SECONDS);
-            mRunner.resumeTiming();
-
-            stopUser(userInfo.id, true);
-
-            mRunner.pauseTiming();
-            removeUser(userInfo.id);
-            mRunner.resumeTiming();
-        }
-    }
-
-    private void switchUser(int userId) throws Exception {
-        final CountDownLatch latch = new CountDownLatch(1);
-        registerUserSwitchObserver(latch, null, userId);
-        mAm.switchUser(userId);
-        latch.await(TIMEOUT_IN_SECOND, TimeUnit.SECONDS);
-    }
-
-    private void stopUser(int userId, boolean force) throws Exception {
-        final CountDownLatch latch = new CountDownLatch(1);
-        mIam.stopUser(userId, force /* force */, new IStopUserCallback.Stub() {
-            @Override
-            public void userStopped(int userId) throws RemoteException {
-                latch.countDown();
-            }
-
-            @Override
-            public void userStopAborted(int userId) throws RemoteException {
-            }
-        });
-        latch.await(TIMEOUT_IN_SECOND, TimeUnit.SECONDS);
-    }
-
-    private void registerUserSwitchObserver(final CountDownLatch switchLatch,
-            final CountDownLatch bootCompleteLatch, final int userId) throws Exception {
-        ActivityManager.getService().registerUserSwitchObserver(
-                new UserSwitchObserver() {
-                    @Override
-                    public void onUserSwitchComplete(int newUserId) throws RemoteException {
-                        if (switchLatch != null && userId == newUserId) {
-                            switchLatch.countDown();
-                        }
-                    }
-
-                    @Override
-                    public void onLockedBootComplete(int newUserId) {
-                        if (bootCompleteLatch != null && userId == newUserId) {
-                            bootCompleteLatch.countDown();
-                        }
-                    }
-                }, "UserLifecycleTest");
-    }
-
-    private void registerBroadcastReceiver(final String action, final CountDownLatch latch,
-            final int userId) {
-        InstrumentationRegistry.getContext().registerReceiverAsUser(new BroadcastReceiver() {
-            @Override
-            public void onReceive(Context context, Intent intent) {
-                if (action.equals(intent.getAction()) && intent.getIntExtra(
-                        Intent.EXTRA_USER_HANDLE, UserHandle.USER_NULL) == userId) {
-                    latch.countDown();
-                }
-            }
-        }, UserHandle.of(userId), new IntentFilter(action), null, null);
-    }
-
-    private void removeUser(int userId) {
-        try {
-            mUm.removeUser(userId);
-            final long startTime = System.currentTimeMillis();
-            final long timeoutInMs = TIMEOUT_IN_SECOND * 1000;
-            while (mUm.getUserInfo(userId) != null &&
-                    System.currentTimeMillis() - startTime < timeoutInMs) {
-                TimeUnit.MILLISECONDS.sleep(CHECK_USER_REMOVED_INTERVAL_MS);
-            }
-        } catch (InterruptedException e) {
-            Thread.currentThread().interrupt();
-        } catch (Exception e) {
-            // Ignore
-        }
-        if (mUm.getUserInfo(userId) != null) {
-            mUsersToRemove.add(userId);
-        }
-    }
-}
diff --git a/apct-tests/perftests/multiuser/src/android/multiuser/UserLifecycleTests.java b/apct-tests/perftests/multiuser/src/android/multiuser/UserLifecycleTests.java
new file mode 100644
index 0000000..855be08
--- /dev/null
+++ b/apct-tests/perftests/multiuser/src/android/multiuser/UserLifecycleTests.java
@@ -0,0 +1,315 @@
+/*
+ * 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.multiuser;
+
+import android.app.ActivityManager;
+import android.app.IActivityManager;
+import android.app.IStopUserCallback;
+import android.app.UserSwitchObserver;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.pm.UserInfo;
+import android.os.RemoteException;
+import android.os.UserHandle;
+import android.os.UserManager;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.LargeTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.ArrayList;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Perf tests for user life cycle events.
+ *
+ * Running the tests:
+ *
+ * make MultiUserPerfTests &&
+ * adb install -r \
+ *     ${ANDROID_PRODUCT_OUT}/data/app/MultiUserPerfTests/MultiUserPerfTests.apk &&
+ * adb shell am instrument -e class android.multiuser.UserLifecycleTests \
+ *     -w com.android.perftests.multiuser/android.support.test.runner.AndroidJUnitRunner
+ *
+ * or
+ *
+ * bit MultiUserPerfTests:android.multiuser.UserLifecycleTests
+ *
+ * Note: If you use bit for running the tests, benchmark results won't be printed on the host side.
+ * But in either case, results can be checked on the device side 'adb logcat -s UserLifecycleTests'
+ */
+@LargeTest
+@RunWith(AndroidJUnit4.class)
+public class UserLifecycleTests {
+    private static final String TAG = UserLifecycleTests.class.getSimpleName();
+
+    private final int TIMEOUT_IN_SECOND = 30;
+    private final int CHECK_USER_REMOVED_INTERVAL_MS = 200;
+
+    private UserManager mUm;
+    private ActivityManager mAm;
+    private IActivityManager mIam;
+    private ArrayList<Integer> mUsersToRemove;
+
+    private final BenchmarkRunner mRunner = new BenchmarkRunner();
+    @Rule
+    public BenchmarkResultsReporter mReporter = new BenchmarkResultsReporter(mRunner);
+
+    @Before
+    public void setUp() {
+        final Context context = InstrumentationRegistry.getContext();
+        mUm = UserManager.get(context);
+        mAm = context.getSystemService(ActivityManager.class);
+        mIam = ActivityManager.getService();
+        mUsersToRemove = new ArrayList<>();
+    }
+
+    @After
+    public void tearDown() {
+        for (int userId : mUsersToRemove) {
+            try {
+                mUm.removeUser(userId);
+            } catch (Exception e) {
+                // Ignore
+            }
+        }
+    }
+
+    @Test
+    public void createAndStartUser() throws Exception {
+        while (mRunner.keepRunning()) {
+            final UserInfo userInfo = mUm.createUser("TestUser", 0);
+
+            final CountDownLatch latch = new CountDownLatch(1);
+            registerBroadcastReceiver(Intent.ACTION_USER_STARTED, latch, userInfo.id);
+            mIam.startUserInBackground(userInfo.id);
+            latch.await(TIMEOUT_IN_SECOND, TimeUnit.SECONDS);
+
+            mRunner.pauseTiming();
+            removeUser(userInfo.id);
+            mRunner.resumeTiming();
+        }
+    }
+
+    @Test
+    public void switchUser() throws Exception {
+        while (mRunner.keepRunning()) {
+            mRunner.pauseTiming();
+            final int startUser = mAm.getCurrentUser();
+            final UserInfo userInfo = mUm.createUser("TestUser", 0);
+            mRunner.resumeTiming();
+
+            switchUser(userInfo.id);
+
+            mRunner.pauseTiming();
+            switchUser(startUser);
+            removeUser(userInfo.id);
+            mRunner.resumeTiming();
+        }
+    }
+
+    @Test
+    public void stopUser() throws Exception {
+        while (mRunner.keepRunning()) {
+            mRunner.pauseTiming();
+            final UserInfo userInfo = mUm.createUser("TestUser", 0);
+            final CountDownLatch latch = new CountDownLatch(1);
+            registerBroadcastReceiver(Intent.ACTION_USER_STARTED, latch, userInfo.id);
+            mIam.startUserInBackground(userInfo.id);
+            latch.await(TIMEOUT_IN_SECOND, TimeUnit.SECONDS);
+            mRunner.resumeTiming();
+
+            stopUser(userInfo.id, false);
+
+            mRunner.pauseTiming();
+            removeUser(userInfo.id);
+            mRunner.resumeTiming();
+        }
+    }
+
+    @Test
+    public void lockedBootCompleted() throws Exception {
+        while (mRunner.keepRunning()) {
+            mRunner.pauseTiming();
+            final int startUser = mAm.getCurrentUser();
+            final UserInfo userInfo = mUm.createUser("TestUser", 0);
+            final CountDownLatch latch = new CountDownLatch(1);
+            registerUserSwitchObserver(null, latch, userInfo.id);
+            mRunner.resumeTiming();
+
+            mAm.switchUser(userInfo.id);
+            latch.await(TIMEOUT_IN_SECOND, TimeUnit.SECONDS);
+
+            mRunner.pauseTiming();
+            switchUser(startUser);
+            removeUser(userInfo.id);
+            mRunner.resumeTiming();
+        }
+    }
+
+    @Test
+    public void managedProfileUnlock() throws Exception {
+        while (mRunner.keepRunning()) {
+            mRunner.pauseTiming();
+            final UserInfo userInfo = mUm.createProfileForUser("TestUser",
+                    UserInfo.FLAG_MANAGED_PROFILE, mAm.getCurrentUser());
+            final CountDownLatch latch = new CountDownLatch(1);
+            registerBroadcastReceiver(Intent.ACTION_USER_UNLOCKED, latch, userInfo.id);
+            mRunner.resumeTiming();
+
+            mIam.startUserInBackground(userInfo.id);
+            latch.await(TIMEOUT_IN_SECOND, TimeUnit.SECONDS);
+
+            mRunner.pauseTiming();
+            removeUser(userInfo.id);
+            mRunner.resumeTiming();
+        }
+    }
+
+    @Test
+    public void ephemeralUserStopped() throws Exception {
+        while (mRunner.keepRunning()) {
+            mRunner.pauseTiming();
+            final int startUser = mAm.getCurrentUser();
+            final UserInfo userInfo = mUm.createUser("TestUser",
+                    UserInfo.FLAG_EPHEMERAL | UserInfo.FLAG_DEMO);
+            switchUser(userInfo.id);
+            final CountDownLatch latch = new CountDownLatch(1);
+            InstrumentationRegistry.getContext().registerReceiver(new BroadcastReceiver() {
+                @Override
+                public void onReceive(Context context, Intent intent) {
+                    if (Intent.ACTION_USER_STOPPED.equals(intent.getAction()) && intent.getIntExtra(
+                            Intent.EXTRA_USER_HANDLE, UserHandle.USER_NULL) == userInfo.id) {
+                        latch.countDown();
+                    }
+                }
+            }, new IntentFilter(Intent.ACTION_USER_STOPPED));
+            final CountDownLatch switchLatch = new CountDownLatch(1);
+            registerUserSwitchObserver(switchLatch, null, startUser);
+            mRunner.resumeTiming();
+
+            mAm.switchUser(startUser);
+            latch.await(TIMEOUT_IN_SECOND, TimeUnit.SECONDS);
+
+            mRunner.pauseTiming();
+            switchLatch.await(TIMEOUT_IN_SECOND, TimeUnit.SECONDS);
+            removeUser(userInfo.id);
+            mRunner.resumeTiming();
+        }
+    }
+
+    @Test
+    public void managedProfileStopped() throws Exception {
+        while (mRunner.keepRunning()) {
+            mRunner.pauseTiming();
+            final UserInfo userInfo = mUm.createProfileForUser("TestUser",
+                    UserInfo.FLAG_MANAGED_PROFILE, mAm.getCurrentUser());
+            final CountDownLatch latch = new CountDownLatch(1);
+            registerBroadcastReceiver(Intent.ACTION_USER_UNLOCKED, latch, userInfo.id);
+            mIam.startUserInBackground(userInfo.id);
+            latch.await(TIMEOUT_IN_SECOND, TimeUnit.SECONDS);
+            mRunner.resumeTiming();
+
+            stopUser(userInfo.id, true);
+
+            mRunner.pauseTiming();
+            removeUser(userInfo.id);
+            mRunner.resumeTiming();
+        }
+    }
+
+    private void switchUser(int userId) throws Exception {
+        final CountDownLatch latch = new CountDownLatch(1);
+        registerUserSwitchObserver(latch, null, userId);
+        mAm.switchUser(userId);
+        latch.await(TIMEOUT_IN_SECOND, TimeUnit.SECONDS);
+    }
+
+    private void stopUser(int userId, boolean force) throws Exception {
+        final CountDownLatch latch = new CountDownLatch(1);
+        mIam.stopUser(userId, force /* force */, new IStopUserCallback.Stub() {
+            @Override
+            public void userStopped(int userId) throws RemoteException {
+                latch.countDown();
+            }
+
+            @Override
+            public void userStopAborted(int userId) throws RemoteException {
+            }
+        });
+        latch.await(TIMEOUT_IN_SECOND, TimeUnit.SECONDS);
+    }
+
+    private void registerUserSwitchObserver(final CountDownLatch switchLatch,
+            final CountDownLatch bootCompleteLatch, final int userId) throws Exception {
+        ActivityManager.getService().registerUserSwitchObserver(
+                new UserSwitchObserver() {
+                    @Override
+                    public void onUserSwitchComplete(int newUserId) throws RemoteException {
+                        if (switchLatch != null && userId == newUserId) {
+                            switchLatch.countDown();
+                        }
+                    }
+
+                    @Override
+                    public void onLockedBootComplete(int newUserId) {
+                        if (bootCompleteLatch != null && userId == newUserId) {
+                            bootCompleteLatch.countDown();
+                        }
+                    }
+                }, TAG);
+    }
+
+    private void registerBroadcastReceiver(final String action, final CountDownLatch latch,
+            final int userId) {
+        InstrumentationRegistry.getContext().registerReceiverAsUser(new BroadcastReceiver() {
+            @Override
+            public void onReceive(Context context, Intent intent) {
+                if (action.equals(intent.getAction()) && intent.getIntExtra(
+                        Intent.EXTRA_USER_HANDLE, UserHandle.USER_NULL) == userId) {
+                    latch.countDown();
+                }
+            }
+        }, UserHandle.of(userId), new IntentFilter(action), null, null);
+    }
+
+    private void removeUser(int userId) {
+        try {
+            mUm.removeUser(userId);
+            final long startTime = System.currentTimeMillis();
+            final long timeoutInMs = TIMEOUT_IN_SECOND * 1000;
+            while (mUm.getUserInfo(userId) != null &&
+                    System.currentTimeMillis() - startTime < timeoutInMs) {
+                TimeUnit.MILLISECONDS.sleep(CHECK_USER_REMOVED_INTERVAL_MS);
+            }
+        } catch (InterruptedException e) {
+            Thread.currentThread().interrupt();
+        } catch (Exception e) {
+            // Ignore
+        }
+        if (mUm.getUserInfo(userId) != null) {
+            mUsersToRemove.add(userId);
+        }
+    }
+}
diff --git a/api/current.txt b/api/current.txt
index fe309dd..b1d9391 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -121,6 +121,7 @@
     field public static final java.lang.String REQUEST_IGNORE_BATTERY_OPTIMIZATIONS = "android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS";
     field public static final java.lang.String REQUEST_INSTALL_PACKAGES = "android.permission.REQUEST_INSTALL_PACKAGES";
     field public static final deprecated java.lang.String RESTART_PACKAGES = "android.permission.RESTART_PACKAGES";
+    field public static final java.lang.String SEND_EMBMS_INTENTS = "android.permission.SEND_EMBMS_INTENTS";
     field public static final java.lang.String SEND_RESPOND_VIA_MESSAGE = "android.permission.SEND_RESPOND_VIA_MESSAGE";
     field public static final java.lang.String SEND_SMS = "android.permission.SEND_SMS";
     field public static final java.lang.String SET_ALARM = "com.android.alarm.permission.SET_ALARM";
@@ -25695,6 +25696,7 @@
     field public static final int TRANSPORT_BLUETOOTH = 2; // 0x2
     field public static final int TRANSPORT_CELLULAR = 0; // 0x0
     field public static final int TRANSPORT_ETHERNET = 3; // 0x3
+    field public static final int TRANSPORT_LOWPAN = 6; // 0x6
     field public static final int TRANSPORT_VPN = 4; // 0x4
     field public static final int TRANSPORT_WIFI = 1; // 0x1
     field public static final int TRANSPORT_WIFI_AWARE = 5; // 0x5
@@ -26010,6 +26012,7 @@
     method public boolean protect(java.net.Socket);
     method public boolean protect(java.net.DatagramSocket);
     method public boolean setUnderlyingNetworks(android.net.Network[]);
+    field public static final java.lang.String METADATA_SUPPORTS_ALWAYS_ON = "android.net.VpnService.SUPPORTS_ALWAYS_ON";
     field public static final java.lang.String SERVICE_INTERFACE = "android.net.VpnService";
   }
 
@@ -31899,6 +31902,7 @@
     method public android.os.storage.StorageVolume getStorageVolume(java.io.File);
     method public java.util.List<android.os.storage.StorageVolume> getStorageVolumes();
     method public java.util.UUID getUuidForPath(java.io.File) throws java.io.IOException;
+    method public boolean isAllocationSupported(java.io.FileDescriptor);
     method public boolean isCacheBehaviorGroup(java.io.File) throws java.io.IOException;
     method public boolean isCacheBehaviorTombstone(java.io.File) throws java.io.IOException;
     method public boolean isEncrypted(java.io.File);
@@ -36950,15 +36954,15 @@
     field public static final java.lang.String SERVICE_META_DATA = "android.autofill";
   }
 
-  public final class CharSequenceTransformation implements android.os.Parcelable {
+  public final class CharSequenceTransformation implements android.os.Parcelable android.service.autofill.Transformation {
     method public int describeContents();
     method public void writeToParcel(android.os.Parcel, int);
     field public static final android.os.Parcelable.Creator<android.service.autofill.CharSequenceTransformation> CREATOR;
   }
 
   public static class CharSequenceTransformation.Builder {
-    ctor public CharSequenceTransformation.Builder(android.view.autofill.AutofillId, java.lang.String, java.lang.String);
-    method public android.service.autofill.CharSequenceTransformation.Builder addField(android.view.autofill.AutofillId, java.lang.String, java.lang.String);
+    ctor public CharSequenceTransformation.Builder(android.view.autofill.AutofillId, java.util.regex.Pattern, java.lang.String);
+    method public android.service.autofill.CharSequenceTransformation.Builder addField(android.view.autofill.AutofillId, java.util.regex.Pattern, java.lang.String);
     method public android.service.autofill.CharSequenceTransformation build();
   }
 
@@ -37047,19 +37051,19 @@
     method public android.service.autofill.FillResponse.Builder setSaveInfo(android.service.autofill.SaveInfo);
   }
 
-  public final class ImageTransformation implements android.os.Parcelable {
+  public final class ImageTransformation implements android.os.Parcelable android.service.autofill.Transformation {
     method public int describeContents();
     method public void writeToParcel(android.os.Parcel, int);
     field public static final android.os.Parcelable.Creator<android.service.autofill.ImageTransformation> CREATOR;
   }
 
   public static class ImageTransformation.Builder {
-    ctor public ImageTransformation.Builder(android.view.autofill.AutofillId, java.lang.String, int);
-    method public android.service.autofill.ImageTransformation.Builder addOption(java.lang.String, int);
+    ctor public ImageTransformation.Builder(android.view.autofill.AutofillId, java.util.regex.Pattern, int);
+    method public android.service.autofill.ImageTransformation.Builder addOption(java.util.regex.Pattern, int);
     method public android.service.autofill.ImageTransformation build();
   }
 
-  public final class LuhnChecksumValidator implements android.os.Parcelable {
+  public final class LuhnChecksumValidator implements android.os.Parcelable android.service.autofill.Validator {
     ctor public LuhnChecksumValidator(android.view.autofill.AutofillId...);
     method public int describeContents();
     method public void writeToParcel(android.os.Parcel, int);
@@ -37088,6 +37092,7 @@
 
   public static final class SaveInfo.Builder {
     ctor public SaveInfo.Builder(int, android.view.autofill.AutofillId[]);
+    ctor public SaveInfo.Builder(int);
     method public android.service.autofill.SaveInfo build();
     method public android.service.autofill.SaveInfo.Builder setCustomDescription(android.service.autofill.CustomDescription);
     method public android.service.autofill.SaveInfo.Builder setDescription(java.lang.CharSequence);
@@ -37105,8 +37110,8 @@
     field public static final android.os.Parcelable.Creator<android.service.autofill.SaveRequest> CREATOR;
   }
 
-  public final class SimpleRegexValidator implements android.os.Parcelable {
-    ctor public SimpleRegexValidator(android.view.autofill.AutofillId, java.lang.String);
+  public final class SimpleRegexValidator implements android.os.Parcelable android.service.autofill.Validator {
+    ctor public SimpleRegexValidator(android.view.autofill.AutofillId, java.util.regex.Pattern);
     method public int describeContents();
     method public void writeToParcel(android.os.Parcel, int);
     field public static final android.os.Parcelable.Creator<android.service.autofill.SimpleRegexValidator> CREATOR;
@@ -38682,14 +38687,18 @@
 
   public final class StructStat {
     ctor public StructStat(long, long, int, long, int, int, long, long, long, long, long, long, long);
+    ctor public StructStat(long, long, int, long, int, int, long, long, android.system.StructTimespec, android.system.StructTimespec, android.system.StructTimespec, long, long);
+    field public final android.system.StructTimespec st_atim;
     field public final long st_atime;
     field public final long st_blksize;
     field public final long st_blocks;
+    field public final android.system.StructTimespec st_ctim;
     field public final long st_ctime;
     field public final long st_dev;
     field public final int st_gid;
     field public final long st_ino;
     field public final int st_mode;
+    field public final android.system.StructTimespec st_mtim;
     field public final long st_mtime;
     field public final long st_nlink;
     field public final long st_rdev;
@@ -38712,6 +38721,13 @@
     field public final long f_namemax;
   }
 
+  public final class StructTimespec implements java.lang.Comparable {
+    ctor public StructTimespec(long, long);
+    method public int compareTo(android.system.StructTimespec);
+    field public final long tv_nsec;
+    field public final long tv_sec;
+  }
+
   public final class StructUtsname {
     ctor public StructUtsname(java.lang.String, java.lang.String, java.lang.String, java.lang.String, java.lang.String);
     field public final java.lang.String machine;
@@ -48595,6 +48611,9 @@
 
   public abstract class SafeBrowsingResponse {
     ctor public SafeBrowsingResponse();
+    method public abstract void backToSafety(boolean);
+    method public abstract void proceed(boolean);
+    method public abstract void showInterstitial(boolean);
   }
 
   public class ServiceWorkerClient {
@@ -49064,7 +49083,7 @@
     method public void setNetworkAvailable(boolean);
     method public deprecated void setPictureListener(android.webkit.WebView.PictureListener);
     method public void setRendererPriorityPolicy(int, boolean);
-    method public static void setSafeBrowsingWhiteList(java.lang.String[]);
+    method public static void setSafeBrowsingWhitelist(java.util.List<java.lang.String>, android.webkit.ValueCallback<java.lang.Boolean>);
     method public deprecated void setVerticalScrollbarOverlay(boolean);
     method public void setWebChromeClient(android.webkit.WebChromeClient);
     method public static void setWebContentsDebuggingEnabled(boolean);
diff --git a/api/removed.txt b/api/removed.txt
index 3968fd3..ca34142 100644
--- a/api/removed.txt
+++ b/api/removed.txt
@@ -267,6 +267,14 @@
 
 }
 
+package android.net.wifi {
+
+  public class WifiManager {
+    method public deprecated boolean setWifiApEnabled(android.net.wifi.WifiConfiguration, boolean);
+  }
+
+}
+
 package android.os {
 
   public class BatteryManager {
@@ -302,18 +310,8 @@
 package android.os.storage {
 
   public class StorageManager {
-    method public deprecated void allocateBytes(java.io.File, long, int) throws java.io.IOException;
-    method public deprecated long getAllocatableBytes(java.io.File, int) throws java.io.IOException;
-    method public deprecated long getCacheQuotaBytes(java.io.File) throws java.io.IOException;
-    method public deprecated long getCacheQuotaBytes() throws java.io.IOException;
-    method public deprecated long getCacheSizeBytes(java.io.File) throws java.io.IOException;
-    method public deprecated long getCacheSizeBytes() throws java.io.IOException;
-    method public deprecated long getExternalCacheQuotaBytes() throws java.io.IOException;
-    method public deprecated long getExternalCacheSizeBytes() throws java.io.IOException;
     method public android.os.storage.StorageVolume getPrimaryVolume();
     method public android.os.storage.StorageVolume[] getVolumeList();
-    method public deprecated boolean isCacheBehaviorAtomic(java.io.File) throws java.io.IOException;
-    method public deprecated void setCacheBehaviorAtomic(java.io.File, boolean) throws java.io.IOException;
   }
 
 }
diff --git a/api/system-current.txt b/api/system-current.txt
index efe7288..8c0f9c1 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -146,6 +146,7 @@
     field public static final java.lang.String MANAGE_CA_CERTIFICATES = "android.permission.MANAGE_CA_CERTIFICATES";
     field public static final java.lang.String MANAGE_DEVICE_ADMINS = "android.permission.MANAGE_DEVICE_ADMINS";
     field public static final java.lang.String MANAGE_DOCUMENTS = "android.permission.MANAGE_DOCUMENTS";
+    field public static final java.lang.String MANAGE_FALLBACK_SUBSCRIPTION_PLANS = "android.permission.MANAGE_FALLBACK_SUBSCRIPTION_PLANS";
     field public static final java.lang.String MANAGE_OWN_CALLS = "android.permission.MANAGE_OWN_CALLS";
     field public static final java.lang.String MANAGE_USB = "android.permission.MANAGE_USB";
     field public static final java.lang.String MANAGE_USERS = "android.permission.MANAGE_USERS";
@@ -157,7 +158,7 @@
     field public static final java.lang.String MODIFY_AUDIO_SETTINGS = "android.permission.MODIFY_AUDIO_SETTINGS";
     field public static final java.lang.String MODIFY_CELL_BROADCASTS = "android.permission.MODIFY_CELL_BROADCASTS";
     field public static final java.lang.String MODIFY_DAY_NIGHT_MODE = "android.permission.MODIFY_DAY_NIGHT_MODE";
-    field public static final java.lang.String MODIFY_NETWORK_ACCOUNTING = "android.permission.MODIFY_NETWORK_ACCOUNTING";
+    field public static final deprecated java.lang.String MODIFY_NETWORK_ACCOUNTING = "android.permission.MODIFY_NETWORK_ACCOUNTING";
     field public static final java.lang.String MODIFY_PARENTAL_CONTROLS = "android.permission.MODIFY_PARENTAL_CONTROLS";
     field public static final java.lang.String MODIFY_PHONE_STATE = "android.permission.MODIFY_PHONE_STATE";
     field public static final java.lang.String MOUNT_FORMAT_FILESYSTEMS = "android.permission.MOUNT_FORMAT_FILESYSTEMS";
@@ -190,6 +191,8 @@
     field public static final java.lang.String READ_OEM_UNLOCK_STATE = "android.permission.READ_OEM_UNLOCK_STATE";
     field public static final java.lang.String READ_PHONE_NUMBERS = "android.permission.READ_PHONE_NUMBERS";
     field public static final java.lang.String READ_PHONE_STATE = "android.permission.READ_PHONE_STATE";
+    field public static final java.lang.String READ_PRINT_SERVICES = "android.permission.READ_PRINT_SERVICES";
+    field public static final java.lang.String READ_PRINT_SERVICE_RECOMMENDATIONS = "android.permission.READ_PRINT_SERVICE_RECOMMENDATIONS";
     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_SEARCH_INDEXABLES = "android.permission.READ_SEARCH_INDEXABLES";
     field public static final java.lang.String READ_SMS = "android.permission.READ_SMS";
@@ -223,6 +226,7 @@
     field public static final java.lang.String RETRIEVE_WINDOW_CONTENT = "android.permission.RETRIEVE_WINDOW_CONTENT";
     field public static final java.lang.String REVOKE_RUNTIME_PERMISSIONS = "android.permission.REVOKE_RUNTIME_PERMISSIONS";
     field public static final java.lang.String SCORE_NETWORKS = "android.permission.SCORE_NETWORKS";
+    field public static final java.lang.String SEND_EMBMS_INTENTS = "android.permission.SEND_EMBMS_INTENTS";
     field public static final java.lang.String SEND_RESPOND_VIA_MESSAGE = "android.permission.SEND_RESPOND_VIA_MESSAGE";
     field public static final java.lang.String SEND_SMS = "android.permission.SEND_SMS";
     field public static final java.lang.String SEND_SMS_NO_CONFIRMATION = "android.permission.SEND_SMS_NO_CONFIRMATION";
@@ -11398,6 +11402,7 @@
     field public static final java.lang.String FEATURE_BACKUP = "android.software.backup";
     field public static final java.lang.String FEATURE_BLUETOOTH = "android.hardware.bluetooth";
     field public static final java.lang.String FEATURE_BLUETOOTH_LE = "android.hardware.bluetooth_le";
+    field public static final java.lang.String FEATURE_BROADCAST_RADIO = "android.hardware.broadcastradio";
     field public static final java.lang.String FEATURE_CAMERA = "android.hardware.camera";
     field public static final java.lang.String FEATURE_CAMERA_ANY = "android.hardware.camera.any";
     field public static final java.lang.String FEATURE_CAMERA_AUTOFOCUS = "android.hardware.camera.autofocus";
@@ -17210,6 +17215,63 @@
 
 package android.hardware.radio {
 
+  public final class ProgramSelector implements android.os.Parcelable {
+    ctor public ProgramSelector(int, android.hardware.radio.ProgramSelector.Identifier, android.hardware.radio.ProgramSelector.Identifier[], long[]);
+    method public static android.hardware.radio.ProgramSelector createAmFmSelector(int, int);
+    method public static android.hardware.radio.ProgramSelector createAmFmSelector(int, int, int);
+    method public int describeContents();
+    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 android.hardware.radio.ProgramSelector.Identifier[] getSecondaryIds();
+    method public 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
+    field public static final int IDENTIFIER_TYPE_DAB_ENSEMBLE = 6; // 0x6
+    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_DRMO_FREQUENCY = 10; // 0xa
+    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_RDS_PI = 2; // 0x2
+    field public static final int IDENTIFIER_TYPE_SXM_CHANNEL = 12; // 0xc
+    field public static final int IDENTIFIER_TYPE_SXM_SERVICE_ID = 11; // 0xb
+    field public static final int IDENTIFIER_TYPE_VENDOR1_PRIMARY = 13; // 0xd
+    field public static final int IDENTIFIER_TYPE_VENDOR2_PRIMARY = 14; // 0xe
+    field public static final int IDENTIFIER_TYPE_VENDOR3_PRIMARY = 15; // 0xf
+    field public static final int IDENTIFIER_TYPE_VENDOR4_PRIMARY = 16; // 0x10
+    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_SXM = 7; // 0x7
+    field public static final int PROGRAM_TYPE_VENDOR1 = 8; // 0x8
+    field public static final int PROGRAM_TYPE_VENDOR2 = 9; // 0x9
+    field public static final int PROGRAM_TYPE_VENDOR3 = 10; // 0xa
+    field public static final int PROGRAM_TYPE_VENDOR4 = 11; // 0xb
+  }
+
+  public static final class ProgramSelector.Identifier implements android.os.Parcelable {
+    ctor public ProgramSelector.Identifier(int, long);
+    method public int describeContents();
+    method public int getType();
+    method public long getValue();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.hardware.radio.ProgramSelector.Identifier> CREATOR;
+  }
+
+  public static abstract class ProgramSelector.IdentifierType implements java.lang.annotation.Annotation {
+  }
+
+  public static abstract class ProgramSelector.ProgramType implements java.lang.annotation.Annotation {
+  }
+
   public class RadioManager {
     method public int listModules(java.util.List<android.hardware.radio.RadioManager.ModuleProperties>);
     method public android.hardware.radio.RadioTuner openTuner(int, android.hardware.radio.RadioManager.BandConfig, boolean, android.hardware.radio.RadioTuner.Callback, android.os.Handler);
@@ -17217,6 +17279,7 @@
     field public static final int BAND_AM_HD = 3; // 0x3
     field public static final int BAND_FM = 1; // 0x1
     field public static final int BAND_FM_HD = 2; // 0x2
+    field public static final int BAND_INVALID = -1; // 0xffffffff
     field public static final int CLASS_AM_FM = 0; // 0x0
     field public static final int CLASS_DT = 2; // 0x2
     field public static final int CLASS_SAT = 1; // 0x1
@@ -17252,6 +17315,9 @@
     field public static final android.os.Parcelable.Creator<android.hardware.radio.RadioManager.AmBandDescriptor> CREATOR;
   }
 
+  public static abstract class RadioManager.Band implements java.lang.annotation.Annotation {
+  }
+
   public static class RadioManager.BandConfig implements android.os.Parcelable {
     method public int describeContents();
     method public int getLowerLimit();
@@ -17315,20 +17381,31 @@
     method public int getNumTuners();
     method public java.lang.String getProduct();
     method public java.lang.String getSerial();
+    method public java.lang.String getServiceName();
+    method public java.lang.String getVendorExension();
     method public java.lang.String getVersion();
+    method public boolean isBackgroundScanningSupported();
     method public boolean isCaptureSupported();
+    method public boolean isProgramIdentifierSupported(int);
+    method public boolean isProgramTypeSupported(int);
     method public void writeToParcel(android.os.Parcel, int);
     field public static final android.os.Parcelable.Creator<android.hardware.radio.RadioManager.ModuleProperties> CREATOR;
   }
 
   public static class RadioManager.ProgramInfo implements android.os.Parcelable {
     method public int describeContents();
-    method public int getChannel();
+    method public deprecated int getChannel();
     method public android.hardware.radio.RadioMetadata getMetadata();
+    method public android.hardware.radio.ProgramSelector getSelector();
     method public int getSignalStrength();
-    method public int getSubChannel();
+    method public deprecated int getSubChannel();
+    method public java.lang.String getVendorExension();
     method public boolean isDigital();
+    method public boolean isLive();
+    method public boolean isMuted();
     method public boolean isStereo();
+    method public boolean isTrafficAnnouncementActive();
+    method public boolean isTrafficProgram();
     method public boolean isTuned();
     method public void writeToParcel(android.os.Parcel, int);
     field public static final android.os.Parcelable.Creator<android.hardware.radio.RadioManager.ProgramInfo> CREATOR;
@@ -17381,19 +17458,27 @@
   public abstract class RadioTuner {
     ctor public RadioTuner();
     method public abstract int cancel();
+    method public abstract void cancelAnnouncement();
     method public abstract void close();
     method public abstract int getConfiguration(android.hardware.radio.RadioManager.BandConfig[]);
     method public abstract boolean getMute();
     method public abstract int getProgramInformation(android.hardware.radio.RadioManager.ProgramInfo[]);
+    method public abstract java.util.List<android.hardware.radio.RadioManager.ProgramInfo> getProgramList(java.lang.String);
     method public abstract boolean hasControl();
+    method public abstract boolean isAnalogForced();
     method public abstract boolean isAntennaConnected();
     method public abstract int scan(int, boolean);
+    method public abstract void setAnalogForced(boolean);
     method public abstract int setConfiguration(android.hardware.radio.RadioManager.BandConfig);
     method public abstract int setMute(boolean);
+    method public abstract boolean startBackgroundScan();
     method public abstract int step(int, boolean);
-    method public abstract int tune(int, int);
+    method public abstract deprecated int tune(int, int);
+    method public abstract void tune(android.hardware.radio.ProgramSelector);
     field public static final int DIRECTION_DOWN = 1; // 0x1
     field public static final int DIRECTION_UP = 0; // 0x0
+    field public static final int ERROR_BACKGROUND_SCAN_FAILED = 6; // 0x6
+    field public static final int ERROR_BACKGROUND_SCAN_UNAVAILABLE = 5; // 0x5
     field public static final int ERROR_CANCELLED = 2; // 0x2
     field public static final int ERROR_CONFIG = 4; // 0x4
     field public static final int ERROR_HARDWARE_FAILURE = 0; // 0x0
@@ -17404,12 +17489,15 @@
   public static abstract class RadioTuner.Callback {
     ctor public RadioTuner.Callback();
     method public void onAntennaState(boolean);
+    method public void onBackgroundScanAvailabilityChange(boolean);
+    method public void onBackgroundScanComplete();
     method public void onConfigurationChanged(android.hardware.radio.RadioManager.BandConfig);
     method public void onControlChanged(boolean);
     method public void onEmergencyAnnouncement(boolean);
     method public void onError(int);
-    method public void onMetadataChanged(android.hardware.radio.RadioMetadata);
+    method public deprecated void onMetadataChanged(android.hardware.radio.RadioMetadata);
     method public void onProgramInfoChanged(android.hardware.radio.RadioManager.ProgramInfo);
+    method public void onProgramListChanged();
     method public void onTrafficAnnouncement(boolean);
   }
 
@@ -27902,6 +27990,7 @@
     field public static final int TRANSPORT_BLUETOOTH = 2; // 0x2
     field public static final int TRANSPORT_CELLULAR = 0; // 0x0
     field public static final int TRANSPORT_ETHERNET = 3; // 0x3
+    field public static final int TRANSPORT_LOWPAN = 6; // 0x6
     field public static final int TRANSPORT_VPN = 4; // 0x4
     field public static final int TRANSPORT_WIFI = 1; // 0x1
     field public static final int TRANSPORT_WIFI_AWARE = 5; // 0x5
@@ -28286,6 +28375,7 @@
     method public boolean protect(java.net.Socket);
     method public boolean protect(java.net.DatagramSocket);
     method public boolean setUnderlyingNetworks(android.net.Network[]);
+    field public static final java.lang.String METADATA_SUPPORTS_ALWAYS_ON = "android.net.VpnService.SUPPORTS_ALWAYS_ON";
     field public static final java.lang.String SERVICE_INTERFACE = "android.net.VpnService";
   }
 
@@ -29166,7 +29256,7 @@
     method public void setTdlsEnabled(java.net.InetAddress, boolean);
     method public void setTdlsEnabledWithMacAddress(java.lang.String, boolean);
     method public boolean setWifiApConfiguration(android.net.wifi.WifiConfiguration);
-    method public boolean setWifiApEnabled(android.net.wifi.WifiConfiguration, boolean);
+    method public deprecated boolean setWifiApEnabled(android.net.wifi.WifiConfiguration, boolean);
     method public boolean setWifiEnabled(boolean);
     method public void startLocalOnlyHotspot(android.net.wifi.WifiManager.LocalOnlyHotspotCallback, android.os.Handler);
     method public deprecated boolean startLocationRestrictedScan(android.os.WorkSource);
@@ -34762,6 +34852,7 @@
     method public android.os.storage.StorageVolume getStorageVolume(java.io.File);
     method public java.util.List<android.os.storage.StorageVolume> getStorageVolumes();
     method public java.util.UUID getUuidForPath(java.io.File) throws java.io.IOException;
+    method public boolean isAllocationSupported(java.io.FileDescriptor);
     method public boolean isCacheBehaviorGroup(java.io.File) throws java.io.IOException;
     method public boolean isCacheBehaviorTombstone(java.io.File) throws java.io.IOException;
     method public boolean isEncrypted(java.io.File);
@@ -38545,6 +38636,22 @@
     field public static final java.lang.String TYPE = "type";
   }
 
+  public final class TimeZoneRulesDataContract {
+    field public static final java.lang.String AUTHORITY = "com.android.timezone";
+  }
+
+  public static final class TimeZoneRulesDataContract.Operation {
+    field public static final java.lang.String COLUMN_DISTRO_MAJOR_VERSION = "distro_major_version";
+    field public static final java.lang.String COLUMN_DISTRO_MINOR_VERSION = "distro_minor_version";
+    field public static final java.lang.String COLUMN_REVISION = "revision";
+    field public static final java.lang.String COLUMN_RULES_VERSION = "rules_version";
+    field public static final java.lang.String COLUMN_TYPE = "type";
+    field public static final android.net.Uri CONTENT_URI;
+    field public static final java.lang.String TYPE_INSTALL = "INSTALL";
+    field public static final java.lang.String TYPE_NO_OP = "NOOP";
+    field public static final java.lang.String TYPE_UNINSTALL = "UNINSTALL";
+  }
+
   public class UserDictionary {
     ctor public UserDictionary();
     field public static final java.lang.String AUTHORITY = "user_dictionary";
@@ -40031,15 +40138,15 @@
     field public static final java.lang.String SERVICE_META_DATA = "android.autofill";
   }
 
-  public final class CharSequenceTransformation implements android.os.Parcelable {
+  public final class CharSequenceTransformation implements android.os.Parcelable android.service.autofill.Transformation {
     method public int describeContents();
     method public void writeToParcel(android.os.Parcel, int);
     field public static final android.os.Parcelable.Creator<android.service.autofill.CharSequenceTransformation> CREATOR;
   }
 
   public static class CharSequenceTransformation.Builder {
-    ctor public CharSequenceTransformation.Builder(android.view.autofill.AutofillId, java.lang.String, java.lang.String);
-    method public android.service.autofill.CharSequenceTransformation.Builder addField(android.view.autofill.AutofillId, java.lang.String, java.lang.String);
+    ctor public CharSequenceTransformation.Builder(android.view.autofill.AutofillId, java.util.regex.Pattern, java.lang.String);
+    method public android.service.autofill.CharSequenceTransformation.Builder addField(android.view.autofill.AutofillId, java.util.regex.Pattern, java.lang.String);
     method public android.service.autofill.CharSequenceTransformation build();
   }
 
@@ -40128,19 +40235,19 @@
     method public android.service.autofill.FillResponse.Builder setSaveInfo(android.service.autofill.SaveInfo);
   }
 
-  public final class ImageTransformation implements android.os.Parcelable {
+  public final class ImageTransformation implements android.os.Parcelable android.service.autofill.Transformation {
     method public int describeContents();
     method public void writeToParcel(android.os.Parcel, int);
     field public static final android.os.Parcelable.Creator<android.service.autofill.ImageTransformation> CREATOR;
   }
 
   public static class ImageTransformation.Builder {
-    ctor public ImageTransformation.Builder(android.view.autofill.AutofillId, java.lang.String, int);
-    method public android.service.autofill.ImageTransformation.Builder addOption(java.lang.String, int);
+    ctor public ImageTransformation.Builder(android.view.autofill.AutofillId, java.util.regex.Pattern, int);
+    method public android.service.autofill.ImageTransformation.Builder addOption(java.util.regex.Pattern, int);
     method public android.service.autofill.ImageTransformation build();
   }
 
-  public final class LuhnChecksumValidator implements android.os.Parcelable {
+  public final class LuhnChecksumValidator implements android.os.Parcelable android.service.autofill.Validator {
     ctor public LuhnChecksumValidator(android.view.autofill.AutofillId...);
     method public int describeContents();
     method public void writeToParcel(android.os.Parcel, int);
@@ -40169,6 +40276,7 @@
 
   public static final class SaveInfo.Builder {
     ctor public SaveInfo.Builder(int, android.view.autofill.AutofillId[]);
+    ctor public SaveInfo.Builder(int);
     method public android.service.autofill.SaveInfo build();
     method public android.service.autofill.SaveInfo.Builder setCustomDescription(android.service.autofill.CustomDescription);
     method public android.service.autofill.SaveInfo.Builder setDescription(java.lang.CharSequence);
@@ -40186,8 +40294,8 @@
     field public static final android.os.Parcelable.Creator<android.service.autofill.SaveRequest> CREATOR;
   }
 
-  public final class SimpleRegexValidator implements android.os.Parcelable {
-    ctor public SimpleRegexValidator(android.view.autofill.AutofillId, java.lang.String);
+  public final class SimpleRegexValidator implements android.os.Parcelable android.service.autofill.Validator {
+    ctor public SimpleRegexValidator(android.view.autofill.AutofillId, java.util.regex.Pattern);
     method public int describeContents();
     method public void writeToParcel(android.os.Parcel, int);
     field public static final android.os.Parcelable.Creator<android.service.autofill.SimpleRegexValidator> CREATOR;
@@ -41906,14 +42014,18 @@
 
   public final class StructStat {
     ctor public StructStat(long, long, int, long, int, int, long, long, long, long, long, long, long);
+    ctor public StructStat(long, long, int, long, int, int, long, long, android.system.StructTimespec, android.system.StructTimespec, android.system.StructTimespec, long, long);
+    field public final android.system.StructTimespec st_atim;
     field public final long st_atime;
     field public final long st_blksize;
     field public final long st_blocks;
+    field public final android.system.StructTimespec st_ctim;
     field public final long st_ctime;
     field public final long st_dev;
     field public final int st_gid;
     field public final long st_ino;
     field public final int st_mode;
+    field public final android.system.StructTimespec st_mtim;
     field public final long st_mtime;
     field public final long st_nlink;
     field public final long st_rdev;
@@ -41936,6 +42048,13 @@
     field public final long f_namemax;
   }
 
+  public final class StructTimespec implements java.lang.Comparable {
+    ctor public StructTimespec(long, long);
+    method public int compareTo(android.system.StructTimespec);
+    field public final long tv_nsec;
+    field public final long tv_sec;
+  }
+
   public final class StructUtsname {
     ctor public StructUtsname(java.lang.String, java.lang.String, java.lang.String, java.lang.String, java.lang.String);
     field public final java.lang.String machine;
@@ -52206,6 +52325,9 @@
 
   public abstract class SafeBrowsingResponse {
     ctor public SafeBrowsingResponse();
+    method public abstract void backToSafety(boolean);
+    method public abstract void proceed(boolean);
+    method public abstract void showInterstitial(boolean);
   }
 
   public class ServiceWorkerClient {
@@ -52715,7 +52837,7 @@
     method public void setNetworkAvailable(boolean);
     method public deprecated void setPictureListener(android.webkit.WebView.PictureListener);
     method public void setRendererPriorityPolicy(int, boolean);
-    method public static void setSafeBrowsingWhiteList(java.lang.String[]);
+    method public static void setSafeBrowsingWhitelist(java.util.List<java.lang.String>, android.webkit.ValueCallback<java.lang.Boolean>);
     method public deprecated void setVerticalScrollbarOverlay(boolean);
     method public void setWebChromeClient(android.webkit.WebChromeClient);
     method public static void setWebContentsDebuggingEnabled(boolean);
@@ -52917,7 +53039,7 @@
     method public abstract java.lang.String getDefaultUserAgent(android.content.Context);
     method public abstract void initSafeBrowsing(android.content.Context, android.webkit.ValueCallback<java.lang.Boolean>);
     method public abstract android.net.Uri[] parseFileChooserResult(int, android.content.Intent);
-    method public abstract void setSafeBrowsingWhiteList(java.lang.String[]);
+    method public abstract void setSafeBrowsingWhitelist(java.util.List<java.lang.String>, android.webkit.ValueCallback<java.lang.Boolean>);
     method public abstract void setWebContentsDebuggingEnabled(boolean);
     method public abstract void shutdownSafeBrowsing();
   }
diff --git a/api/system-removed.txt b/api/system-removed.txt
index 48f62b1..dfadae4 100644
--- a/api/system-removed.txt
+++ b/api/system-removed.txt
@@ -295,18 +295,8 @@
 package android.os.storage {
 
   public class StorageManager {
-    method public deprecated void allocateBytes(java.io.File, long, int) throws java.io.IOException;
-    method public deprecated long getAllocatableBytes(java.io.File, int) throws java.io.IOException;
-    method public deprecated long getCacheQuotaBytes(java.io.File) throws java.io.IOException;
-    method public deprecated long getCacheQuotaBytes() throws java.io.IOException;
-    method public deprecated long getCacheSizeBytes(java.io.File) throws java.io.IOException;
-    method public deprecated long getCacheSizeBytes() throws java.io.IOException;
-    method public deprecated long getExternalCacheQuotaBytes() throws java.io.IOException;
-    method public deprecated long getExternalCacheSizeBytes() throws java.io.IOException;
     method public android.os.storage.StorageVolume getPrimaryVolume();
     method public android.os.storage.StorageVolume[] getVolumeList();
-    method public deprecated boolean isCacheBehaviorAtomic(java.io.File) throws java.io.IOException;
-    method public deprecated void setCacheBehaviorAtomic(java.io.File, boolean) throws java.io.IOException;
   }
 
 }
diff --git a/api/test-current.txt b/api/test-current.txt
index 10a3ba0..da4f6786 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -121,6 +121,7 @@
     field public static final java.lang.String REQUEST_IGNORE_BATTERY_OPTIMIZATIONS = "android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS";
     field public static final java.lang.String REQUEST_INSTALL_PACKAGES = "android.permission.REQUEST_INSTALL_PACKAGES";
     field public static final deprecated java.lang.String RESTART_PACKAGES = "android.permission.RESTART_PACKAGES";
+    field public static final java.lang.String SEND_EMBMS_INTENTS = "android.permission.SEND_EMBMS_INTENTS";
     field public static final java.lang.String SEND_RESPOND_VIA_MESSAGE = "android.permission.SEND_RESPOND_VIA_MESSAGE";
     field public static final java.lang.String SEND_SMS = "android.permission.SEND_SMS";
     field public static final java.lang.String SET_ALARM = "com.android.alarm.permission.SET_ALARM";
@@ -25806,6 +25807,7 @@
     field public static final int TRANSPORT_BLUETOOTH = 2; // 0x2
     field public static final int TRANSPORT_CELLULAR = 0; // 0x0
     field public static final int TRANSPORT_ETHERNET = 3; // 0x3
+    field public static final int TRANSPORT_LOWPAN = 6; // 0x6
     field public static final int TRANSPORT_VPN = 4; // 0x4
     field public static final int TRANSPORT_WIFI = 1; // 0x1
     field public static final int TRANSPORT_WIFI_AWARE = 5; // 0x5
@@ -26121,6 +26123,7 @@
     method public boolean protect(java.net.Socket);
     method public boolean protect(java.net.DatagramSocket);
     method public boolean setUnderlyingNetworks(android.net.Network[]);
+    field public static final java.lang.String METADATA_SUPPORTS_ALWAYS_ON = "android.net.VpnService.SUPPORTS_ALWAYS_ON";
     field public static final java.lang.String SERVICE_INTERFACE = "android.net.VpnService";
   }
 
@@ -32040,6 +32043,7 @@
     method public android.os.storage.StorageVolume getStorageVolume(java.io.File);
     method public java.util.List<android.os.storage.StorageVolume> getStorageVolumes();
     method public java.util.UUID getUuidForPath(java.io.File) throws java.io.IOException;
+    method public boolean isAllocationSupported(java.io.FileDescriptor);
     method public boolean isCacheBehaviorGroup(java.io.File) throws java.io.IOException;
     method public boolean isCacheBehaviorTombstone(java.io.File) throws java.io.IOException;
     method public boolean isEncrypted(java.io.File);
@@ -32731,9 +32735,7 @@
 
   public final class PrintManager {
     method public java.util.List<android.print.PrintJob> getPrintJobs();
-    method public java.util.List<android.printservice.PrintServiceInfo> getPrintServices(int);
     method public android.print.PrintJob print(java.lang.String, android.print.PrintDocumentAdapter, android.print.PrintAttributes);
-    field public static final int ALL_SERVICES = 3; // 0x3
   }
 
   public final class PrinterCapabilitiesInfo implements android.os.Parcelable {
@@ -32863,13 +32865,6 @@
     field public static final java.lang.String SERVICE_META_DATA = "android.printservice";
   }
 
-  public final class PrintServiceInfo implements android.os.Parcelable {
-    method public int describeContents();
-    method public android.content.ComponentName getComponentName();
-    method public void writeToParcel(android.os.Parcel, int);
-    field public static final android.os.Parcelable.Creator<android.printservice.PrintServiceInfo> CREATOR;
-  }
-
   public abstract class PrinterDiscoverySession {
     ctor public PrinterDiscoverySession();
     method public final void addPrinters(java.util.List<android.print.PrinterInfo>);
@@ -37123,7 +37118,7 @@
     field public static final java.lang.String SERVICE_META_DATA = "android.autofill";
   }
 
-  public final class CharSequenceTransformation implements android.os.Parcelable {
+  public final class CharSequenceTransformation implements android.os.Parcelable android.service.autofill.Transformation {
     method public void apply(android.service.autofill.ValueFinder, android.widget.RemoteViews, int);
     method public int describeContents();
     method public void writeToParcel(android.os.Parcel, int);
@@ -37131,8 +37126,8 @@
   }
 
   public static class CharSequenceTransformation.Builder {
-    ctor public CharSequenceTransformation.Builder(android.view.autofill.AutofillId, java.lang.String, java.lang.String);
-    method public android.service.autofill.CharSequenceTransformation.Builder addField(android.view.autofill.AutofillId, java.lang.String, java.lang.String);
+    ctor public CharSequenceTransformation.Builder(android.view.autofill.AutofillId, java.util.regex.Pattern, java.lang.String);
+    method public android.service.autofill.CharSequenceTransformation.Builder addField(android.view.autofill.AutofillId, java.util.regex.Pattern, java.lang.String);
     method public android.service.autofill.CharSequenceTransformation build();
   }
 
@@ -37221,7 +37216,7 @@
     method public android.service.autofill.FillResponse.Builder setSaveInfo(android.service.autofill.SaveInfo);
   }
 
-  public final class ImageTransformation implements android.os.Parcelable {
+  public final class ImageTransformation implements android.os.Parcelable android.service.autofill.Transformation {
     method public void apply(android.service.autofill.ValueFinder, android.widget.RemoteViews, int);
     method public int describeContents();
     method public void writeToParcel(android.os.Parcel, int);
@@ -37229,12 +37224,12 @@
   }
 
   public static class ImageTransformation.Builder {
-    ctor public ImageTransformation.Builder(android.view.autofill.AutofillId, java.lang.String, int);
-    method public android.service.autofill.ImageTransformation.Builder addOption(java.lang.String, int);
+    ctor public ImageTransformation.Builder(android.view.autofill.AutofillId, java.util.regex.Pattern, int);
+    method public android.service.autofill.ImageTransformation.Builder addOption(java.util.regex.Pattern, int);
     method public android.service.autofill.ImageTransformation build();
   }
 
-  public final class LuhnChecksumValidator implements android.os.Parcelable {
+  public final class LuhnChecksumValidator implements android.os.Parcelable android.service.autofill.Validator {
     ctor public LuhnChecksumValidator(android.view.autofill.AutofillId...);
     method public int describeContents();
     method public boolean isValid(android.service.autofill.ValueFinder);
@@ -37264,6 +37259,7 @@
 
   public static final class SaveInfo.Builder {
     ctor public SaveInfo.Builder(int, android.view.autofill.AutofillId[]);
+    ctor public SaveInfo.Builder(int);
     method public android.service.autofill.SaveInfo build();
     method public android.service.autofill.SaveInfo.Builder setCustomDescription(android.service.autofill.CustomDescription);
     method public android.service.autofill.SaveInfo.Builder setDescription(java.lang.CharSequence);
@@ -37281,9 +37277,10 @@
     field public static final android.os.Parcelable.Creator<android.service.autofill.SaveRequest> CREATOR;
   }
 
-  public final class SimpleRegexValidator implements android.os.Parcelable {
-    ctor public SimpleRegexValidator(android.view.autofill.AutofillId, java.lang.String);
+  public final class SimpleRegexValidator implements android.os.Parcelable android.service.autofill.Validator {
+    ctor public SimpleRegexValidator(android.view.autofill.AutofillId, java.util.regex.Pattern);
     method public int describeContents();
+    method public boolean isValid(android.service.autofill.ValueFinder);
     method public void writeToParcel(android.os.Parcel, int);
     field public static final android.os.Parcelable.Creator<android.service.autofill.SimpleRegexValidator> CREATOR;
   }
@@ -38900,14 +38897,18 @@
 
   public final class StructStat {
     ctor public StructStat(long, long, int, long, int, int, long, long, long, long, long, long, long);
+    ctor public StructStat(long, long, int, long, int, int, long, long, android.system.StructTimespec, android.system.StructTimespec, android.system.StructTimespec, long, long);
+    field public final android.system.StructTimespec st_atim;
     field public final long st_atime;
     field public final long st_blksize;
     field public final long st_blocks;
+    field public final android.system.StructTimespec st_ctim;
     field public final long st_ctime;
     field public final long st_dev;
     field public final int st_gid;
     field public final long st_ino;
     field public final int st_mode;
+    field public final android.system.StructTimespec st_mtim;
     field public final long st_mtime;
     field public final long st_nlink;
     field public final long st_rdev;
@@ -38930,6 +38931,13 @@
     field public final long f_namemax;
   }
 
+  public final class StructTimespec implements java.lang.Comparable {
+    ctor public StructTimespec(long, long);
+    method public int compareTo(android.system.StructTimespec);
+    field public final long tv_nsec;
+    field public final long tv_sec;
+  }
+
   public final class StructUtsname {
     ctor public StructUtsname(java.lang.String, java.lang.String, java.lang.String, java.lang.String, java.lang.String);
     field public final java.lang.String machine;
@@ -46188,6 +46196,7 @@
     method public void setFocusable(int);
     method public void setFocusableInTouchMode(boolean);
     method public void setFocusedByDefault(boolean);
+    method public final void setFocusedInCluster();
     method public void setForeground(android.graphics.drawable.Drawable);
     method public void setForegroundGravity(int);
     method public void setForegroundTintList(android.content.res.ColorStateList);
@@ -49024,6 +49033,9 @@
 
   public abstract class SafeBrowsingResponse {
     ctor public SafeBrowsingResponse();
+    method public abstract void backToSafety(boolean);
+    method public abstract void proceed(boolean);
+    method public abstract void showInterstitial(boolean);
   }
 
   public class ServiceWorkerClient {
@@ -49493,7 +49505,7 @@
     method public void setNetworkAvailable(boolean);
     method public deprecated void setPictureListener(android.webkit.WebView.PictureListener);
     method public void setRendererPriorityPolicy(int, boolean);
-    method public static void setSafeBrowsingWhiteList(java.lang.String[]);
+    method public static void setSafeBrowsingWhitelist(java.util.List<java.lang.String>, android.webkit.ValueCallback<java.lang.Boolean>);
     method public deprecated void setVerticalScrollbarOverlay(boolean);
     method public void setWebChromeClient(android.webkit.WebChromeClient);
     method public static void setWebContentsDebuggingEnabled(boolean);
diff --git a/api/test-removed.txt b/api/test-removed.txt
index 3968fd3..ca34142 100644
--- a/api/test-removed.txt
+++ b/api/test-removed.txt
@@ -267,6 +267,14 @@
 
 }
 
+package android.net.wifi {
+
+  public class WifiManager {
+    method public deprecated boolean setWifiApEnabled(android.net.wifi.WifiConfiguration, boolean);
+  }
+
+}
+
 package android.os {
 
   public class BatteryManager {
@@ -302,18 +310,8 @@
 package android.os.storage {
 
   public class StorageManager {
-    method public deprecated void allocateBytes(java.io.File, long, int) throws java.io.IOException;
-    method public deprecated long getAllocatableBytes(java.io.File, int) throws java.io.IOException;
-    method public deprecated long getCacheQuotaBytes(java.io.File) throws java.io.IOException;
-    method public deprecated long getCacheQuotaBytes() throws java.io.IOException;
-    method public deprecated long getCacheSizeBytes(java.io.File) throws java.io.IOException;
-    method public deprecated long getCacheSizeBytes() throws java.io.IOException;
-    method public deprecated long getExternalCacheQuotaBytes() throws java.io.IOException;
-    method public deprecated long getExternalCacheSizeBytes() throws java.io.IOException;
     method public android.os.storage.StorageVolume getPrimaryVolume();
     method public android.os.storage.StorageVolume[] getVolumeList();
-    method public deprecated boolean isCacheBehaviorAtomic(java.io.File) throws java.io.IOException;
-    method public deprecated void setCacheBehaviorAtomic(java.io.File, boolean) throws java.io.IOException;
   }
 
 }
diff --git a/cmds/bootanimation/BootAnimationUtil.cpp b/cmds/bootanimation/BootAnimationUtil.cpp
index 377d6ce..7718daf 100644
--- a/cmds/bootanimation/BootAnimationUtil.cpp
+++ b/cmds/bootanimation/BootAnimationUtil.cpp
@@ -29,7 +29,7 @@
     char value[PROPERTY_VALUE_MAX];
     property_get("debug.sf.nobootanimation", value, "0");
     if (atoi(value) > 0) {
-      return false;
+        return true;
     }
 
     property_get("ro.boot.quiescent", value, "0");
diff --git a/cmds/bootanimation/bootanimation_main.cpp b/cmds/bootanimation/bootanimation_main.cpp
index daac588..8501982 100644
--- a/cmds/bootanimation/bootanimation_main.cpp
+++ b/cmds/bootanimation/bootanimation_main.cpp
@@ -157,8 +157,10 @@
 
         // create the boot animation object
         sp<BootAnimation> boot = new BootAnimation(new AudioAnimationCallbacks());
+        ALOGV("Boot animation set up. Joining pool.");
 
         IPCThreadState::self()->joinThreadPool();
     }
+    ALOGV("Boot animation exit");
     return 0;
 }
diff --git a/core/java/android/accounts/AbstractAccountAuthenticator.java b/core/java/android/accounts/AbstractAccountAuthenticator.java
index 87e512c..bf9bd79 100644
--- a/core/java/android/accounts/AbstractAccountAuthenticator.java
+++ b/core/java/android/accounts/AbstractAccountAuthenticator.java
@@ -547,7 +547,9 @@
      * @param authTokenType the type of auth token to retrieve after adding the account, may be null
      * @param requiredFeatures a String array of authenticator-specific features that the added
      * account must support, may be null
-     * @param options a Bundle of authenticator-specific options, may be null
+     * @param options a Bundle of authenticator-specific options. It always contains
+     * {@link AccountManager#KEY_CALLER_PID} and {@link AccountManager#KEY_CALLER_UID}
+     * fields which will let authenticator know the identity of the caller.
      * @return a Bundle result or null if the result is to be returned via the response. The result
      * will contain either:
      * <ul>
@@ -603,21 +605,24 @@
      * addition {@link AbstractAccountAuthenticator} implementations that declare themselves
      * {@code android:customTokens=true} may also provide a non-negative {@link
      * #KEY_CUSTOM_TOKEN_EXPIRY} long value containing the expiration timestamp of the expiration
-     * time (in millis since the unix epoch).
+     * time (in millis since the unix epoch), tokens will be cached in memory based on
+     * application's packageName/signature for however long that was specified.
      * <p>
      * Implementers should assume that tokens will be cached on the basis of account and
      * authTokenType. The system may ignore the contents of the supplied options Bundle when
      * determining to re-use a cached token. Furthermore, implementers should assume a supplied
      * expiration time will be treated as non-binding advice.
      * <p>
-     * Finally, note that for android:customTokens=false authenticators, tokens are cached
+     * Finally, note that for {@code android:customTokens=false} authenticators, tokens are cached
      * indefinitely until some client calls {@link
      * AccountManager#invalidateAuthToken(String,String)}.
      *
      * @param response to send the result back to the AccountManager, will never be null
      * @param account the account whose credentials are to be retrieved, will never be null
      * @param authTokenType the type of auth token to retrieve, will never be null
-     * @param options a Bundle of authenticator-specific options, may be null
+     * @param options a Bundle of authenticator-specific options. It always contains
+     * {@link AccountManager#KEY_CALLER_PID} and {@link AccountManager#KEY_CALLER_UID}
+     * fields which will let authenticator know the identity of the caller.
      * @return a Bundle result or null if the result is to be returned via the response.
      * @throws NetworkErrorException if the authenticator could not honor the request due to a
      * network error
diff --git a/core/java/android/accounts/AccountManager.java b/core/java/android/accounts/AccountManager.java
index a446296..a209d28 100644
--- a/core/java/android/accounts/AccountManager.java
+++ b/core/java/android/accounts/AccountManager.java
@@ -242,10 +242,13 @@
     public static final String KEY_LAST_AUTHENTICATED_TIME = "lastAuthenticatedTime";
 
     /**
-     * Authenticators using 'customTokens' option will also get the UID of the
-     * caller
+     * The UID of caller app.
      */
     public static final String KEY_CALLER_UID = "callerUid";
+
+    /**
+     * The process id of caller app.
+     */
     public static final String KEY_CALLER_PID = "callerPid";
 
     /**
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index c994e13..d03b347 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -658,6 +658,8 @@
     }
 
     static final class DumpHeapData {
+        public boolean managed;
+        public boolean mallocInfo;
         public boolean runGc;
         String path;
         ParcelFileDescriptor fd;
@@ -1025,12 +1027,15 @@
         }
 
         @Override
-        public void dumpHeap(boolean managed, boolean runGc, String path, ParcelFileDescriptor fd) {
+        public void dumpHeap(boolean managed, boolean mallocInfo, boolean runGc, String path,
+                ParcelFileDescriptor fd) {
             DumpHeapData dhd = new DumpHeapData();
+            dhd.managed = managed;
+            dhd.mallocInfo = mallocInfo;
             dhd.runGc = runGc;
             dhd.path = path;
             dhd.fd = fd;
-            sendMessage(H.DUMP_HEAP, dhd, managed ? 1 : 0, 0, true /*async*/);
+            sendMessage(H.DUMP_HEAP, dhd, 0, 0, true /*async*/);
         }
 
         public void attachAgent(String agent) {
@@ -1762,7 +1767,7 @@
                 case SCHEDULE_CRASH:
                     throw new RemoteServiceException((String)msg.obj);
                 case DUMP_HEAP:
-                    handleDumpHeap(msg.arg1 != 0, (DumpHeapData)msg.obj);
+                    handleDumpHeap((DumpHeapData) msg.obj);
                     break;
                 case DUMP_ACTIVITY:
                     handleDumpActivity((DumpComponentInfo)msg.obj);
@@ -5173,13 +5178,13 @@
         }
     }
 
-    static final void handleDumpHeap(boolean managed, DumpHeapData dhd) {
+    static void handleDumpHeap(DumpHeapData dhd) {
         if (dhd.runGc) {
             System.gc();
             System.runFinalization();
             System.gc();
         }
-        if (managed) {
+        if (dhd.managed) {
             try {
                 Debug.dumpHprofData(dhd.path, dhd.fd.getFileDescriptor());
             } catch (IOException e) {
@@ -5192,6 +5197,8 @@
                     Slog.w(TAG, "Failure closing profile fd", e);
                 }
             }
+        } else if (dhd.mallocInfo) {
+            Debug.dumpNativeMallocInfo(dhd.fd.getFileDescriptor());
         } else {
             Debug.dumpNativeHeap(dhd.fd.getFileDescriptor());
         }
@@ -5348,8 +5355,8 @@
                     int uid = Process.myUid();
                     String[] packages = getPackageManager().getPackagesForUid(uid);
                     if (packages != null) {
-                        ThreadedRenderer.setupDiskCache(cacheDir);
-                        RenderScriptCacheDir.setupDiskCache(cacheDir);
+                        ThreadedRenderer.setupDiskCache(codeCacheDir);
+                        RenderScriptCacheDir.setupDiskCache(codeCacheDir);
                     }
                 } catch (RemoteException e) {
                     Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
@@ -6416,9 +6423,9 @@
     private <T> T instantiate(ClassLoader cl, String className, Context c,
             Instantiator<T> instantiator)
             throws ClassNotFoundException, IllegalAccessException, InstantiationException {
-        if (c.getApplicationContext() instanceof Application) {
-            T a = instantiator.instantiate((Application) c.getApplicationContext(),
-                    cl, className);
+        Application app = getApp(c);
+        if (app != null) {
+            T a = instantiator.instantiate(app, cl, className);
             if (a != null) return a;
         }
         return (T) cl.loadClass(className).newInstance();
@@ -6427,14 +6434,25 @@
     private <T> T instantiate(ClassLoader cl, String className, Intent intent, Context c,
             IntentInstantiator<T> instantiator)
             throws ClassNotFoundException, IllegalAccessException, InstantiationException {
-        if (c.getApplicationContext() instanceof Application) {
-            T a = instantiator.instantiate((Application) c.getApplicationContext(),
-                    cl, className, intent);
+        Application app = getApp(c);
+        if (app != null) {
+            T a = instantiator.instantiate(app, cl, className, intent);
             if (a != null) return a;
         }
         return (T) cl.loadClass(className).newInstance();
     }
 
+    private Application getApp(Context c) {
+        // We need this shortcut to avoid actually calling getApplicationContext() on an Application
+        // because the Application may not return itself for getApplicationContext() because the
+        // API doesn't enforce it.
+        if (c instanceof Application) return (Application) c;
+        if (c.getApplicationContext() instanceof Application) {
+            return (Application) c.getApplicationContext();
+        }
+        return null;
+    }
+
     private interface Instantiator<T> {
         T instantiate(Application app, ClassLoader cl, String className)
                 throws ClassNotFoundException, IllegalAccessException, InstantiationException;
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index 9c9d655..c48be77 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -60,12 +60,10 @@
 import android.os.Process;
 import android.os.RemoteException;
 import android.os.ServiceManager;
-import android.os.SystemProperties;
 import android.os.Trace;
 import android.os.UserHandle;
 import android.os.UserManager;
 import android.os.storage.IStorageManager;
-import android.os.storage.StorageManager;
 import android.system.ErrnoException;
 import android.system.Os;
 import android.system.OsConstants;
@@ -621,7 +619,8 @@
     @Override
     public File getExternalFilesDir(String type) {
         // Operates on primary external storage
-        return getExternalFilesDirs(type)[0];
+        final File[] dirs = getExternalFilesDirs(type);
+        return (dirs != null && dirs.length > 0) ? dirs[0] : null;
     }
 
     @Override
@@ -638,7 +637,8 @@
     @Override
     public File getObbDir() {
         // Operates on primary external storage
-        return getObbDirs()[0];
+        final File[] dirs = getObbDirs();
+        return (dirs != null && dirs.length > 0) ? dirs[0] : null;
     }
 
     @Override
@@ -672,7 +672,8 @@
     @Override
     public File getExternalCacheDir() {
         // Operates on primary external storage
-        return getExternalCacheDirs()[0];
+        final File[] dirs = getExternalCacheDirs();
+        return (dirs != null && dirs.length > 0) ? dirs[0] : null;
     }
 
     @Override
diff --git a/core/java/android/app/DownloadManager.java b/core/java/android/app/DownloadManager.java
index 5baaeb3..b444f17 100644
--- a/core/java/android/app/DownloadManager.java
+++ b/core/java/android/app/DownloadManager.java
@@ -18,9 +18,9 @@
 
 import android.annotation.Nullable;
 import android.annotation.SdkConstant;
+import android.annotation.SdkConstant.SdkConstantType;
 import android.annotation.SystemApi;
 import android.annotation.SystemService;
-import android.annotation.SdkConstant.SdkConstantType;
 import android.content.ContentResolver;
 import android.content.ContentUris;
 import android.content.ContentValues;
@@ -36,8 +36,8 @@
 import android.os.FileUtils;
 import android.os.ParcelFileDescriptor;
 import android.provider.Downloads;
-import android.provider.Settings;
 import android.provider.MediaStore.Images;
+import android.provider.Settings;
 import android.provider.Settings.SettingNotFoundException;
 import android.text.TextUtils;
 import android.util.Pair;
@@ -393,7 +393,6 @@
         private int mFlags = 0;
         private boolean mIsVisibleInDownloadsUi = true;
         private boolean mScannable = false;
-        private boolean mUseSystemCache = false;
         /** if a file is designated as a MediaScanner scannable file, the following value is
          * stored in the database column {@link Downloads.Impl#COLUMN_MEDIA_SCANNED}.
          */
@@ -474,24 +473,6 @@
         }
 
         /**
-         * Set the local destination for the downloaded file to the system cache dir (/cache).
-         * This is only available to System apps with the permission
-         * {@link android.Manifest.permission#ACCESS_CACHE_FILESYSTEM}.
-         * <p>
-         * The downloaded file is not scanned by MediaScanner.
-         * But it can be made scannable by calling {@link #allowScanningByMediaScanner()}.
-         * <p>
-         * Files downloaded to /cache may be deleted by the system at any time to reclaim space.
-         *
-         * @return this object
-         * @hide
-         */
-        public Request setDestinationToSystemCache() {
-            mUseSystemCache = true;
-            return this;
-        }
-
-        /**
          * Set the local destination for the downloaded file to a path within
          * the application's external files directory (as returned by
          * {@link Context#getExternalFilesDir(String)}.
@@ -772,13 +753,13 @@
             values.put(Downloads.Impl.COLUMN_NOTIFICATION_PACKAGE, packageName);
 
             if (mDestinationUri != null) {
-                values.put(Downloads.Impl.COLUMN_DESTINATION, Downloads.Impl.DESTINATION_FILE_URI);
-                values.put(Downloads.Impl.COLUMN_FILE_NAME_HINT, mDestinationUri.toString());
+                values.put(Downloads.Impl.COLUMN_DESTINATION,
+                        Downloads.Impl.DESTINATION_FILE_URI);
+                values.put(Downloads.Impl.COLUMN_FILE_NAME_HINT,
+                        mDestinationUri.toString());
             } else {
                 values.put(Downloads.Impl.COLUMN_DESTINATION,
-                           (this.mUseSystemCache) ?
-                                   Downloads.Impl.DESTINATION_SYSTEMCACHE_PARTITION :
-                                   Downloads.Impl.DESTINATION_CACHE_PARTITION_PURGEABLE);
+                        Downloads.Impl.DESTINATION_CACHE_PARTITION_PURGEABLE);
             }
             // is the file supposed to be media-scannable?
             values.put(Downloads.Impl.COLUMN_MEDIA_SCANNED, (mScannable) ? SCANNABLE_VALUE_YES :
diff --git a/core/java/android/app/IActivityManager.aidl b/core/java/android/app/IActivityManager.aidl
index 3be6f97..df1a412 100644
--- a/core/java/android/app/IActivityManager.aidl
+++ b/core/java/android/app/IActivityManager.aidl
@@ -277,8 +277,8 @@
     int checkGrantUriPermission(int callingUid, in String targetPkg, in Uri uri,
             int modeFlags, int userId);
     // Cause the specified process to dump the specified heap.
-    boolean dumpHeap(in String process, int userId, boolean managed, boolean runGc, in String path,
-            in ParcelFileDescriptor fd);
+    boolean dumpHeap(in String process, int userId, boolean managed, boolean mallocInfo,
+            boolean runGc, in String path, in ParcelFileDescriptor fd);
     int startActivities(in IApplicationThread caller, in String callingPackage,
             in Intent[] intents, in String[] resolvedTypes, in IBinder resultTo,
             in Bundle options, int userId);
diff --git a/core/java/android/app/IApplicationThread.aidl b/core/java/android/app/IApplicationThread.aidl
index 7191fc4..aeed7e1 100644
--- a/core/java/android/app/IApplicationThread.aidl
+++ b/core/java/android/app/IApplicationThread.aidl
@@ -117,7 +117,8 @@
     void scheduleSuicide();
     void dispatchPackageBroadcast(int cmd, in String[] packages);
     void scheduleCrash(in String msg);
-    void dumpHeap(boolean managed, boolean runGc, in String path, in ParcelFileDescriptor fd);
+    void dumpHeap(boolean managed, boolean mallocInfo, boolean runGc, in String path,
+            in ParcelFileDescriptor fd);
     void dumpActivity(in ParcelFileDescriptor fd, IBinder servicetoken, in String prefix,
             in String[] args);
     void clearDnsCache();
diff --git a/core/java/android/app/MediaRouteButton.java b/core/java/android/app/MediaRouteButton.java
index d2d7b6d..a4a89fa 100644
--- a/core/java/android/app/MediaRouteButton.java
+++ b/core/java/android/app/MediaRouteButton.java
@@ -24,6 +24,7 @@
 import android.content.ContextWrapper;
 import android.content.res.TypedArray;
 import android.graphics.Canvas;
+import android.graphics.drawable.AnimationDrawable;
 import android.graphics.drawable.Drawable;
 import android.media.MediaRouter;
 import android.media.MediaRouter.RouteGroup;
@@ -339,28 +340,42 @@
     }
 
     private void refreshRoute() {
+        final MediaRouter.RouteInfo route = mRouter.getSelectedRoute();
+        final boolean isRemote = !route.isDefault() && route.matchesTypes(mRouteTypes);
+        final boolean isConnecting = isRemote && route.isConnecting();
+        boolean needsRefresh = false;
+        if (mRemoteActive != isRemote) {
+            mRemoteActive = isRemote;
+            needsRefresh = true;
+        }
+        if (mIsConnecting != isConnecting) {
+            mIsConnecting = isConnecting;
+            needsRefresh = true;
+        }
+
+        if (needsRefresh) {
+            refreshDrawableState();
+        }
         if (mAttachedToWindow) {
-            final MediaRouter.RouteInfo route = mRouter.getSelectedRoute();
-            final boolean isRemote = !route.isDefault() && route.matchesTypes(mRouteTypes);
-            final boolean isConnecting = isRemote && route.isConnecting();
-
-            boolean needsRefresh = false;
-            if (mRemoteActive != isRemote) {
-                mRemoteActive = isRemote;
-                needsRefresh = true;
-            }
-            if (mIsConnecting != isConnecting) {
-                mIsConnecting = isConnecting;
-                needsRefresh = true;
-            }
-
-            if (needsRefresh) {
-                refreshDrawableState();
-            }
-
             setEnabled(mRouter.isRouteAvailable(mRouteTypes,
                     MediaRouter.AVAILABILITY_FLAG_IGNORE_DEFAULT_ROUTE));
         }
+        if (mRemoteIndicator != null
+                && mRemoteIndicator.getCurrent() instanceof AnimationDrawable) {
+            AnimationDrawable curDrawable = (AnimationDrawable) mRemoteIndicator.getCurrent();
+            if (mAttachedToWindow) {
+                if ((needsRefresh || isConnecting) && !curDrawable.isRunning()) {
+                    curDrawable.start();
+                }
+            } else if (isRemote && !isConnecting) {
+                // When the route is already connected before the view is attached, show the last
+                // frame of the connected animation immediately.
+                if (curDrawable.isRunning()) {
+                    curDrawable.stop();
+                }
+                curDrawable.selectDrawable(curDrawable.getNumberOfFrames() - 1);
+            }
+        }
     }
 
     private final class MediaRouterCallback extends MediaRouter.SimpleCallback {
diff --git a/core/java/android/app/NotificationChannel.java b/core/java/android/app/NotificationChannel.java
index 143d147..d6e3691 100644
--- a/core/java/android/app/NotificationChannel.java
+++ b/core/java/android/app/NotificationChannel.java
@@ -15,11 +15,6 @@
  */
 package android.app;
 
-import org.json.JSONException;
-import org.json.JSONObject;
-import org.xmlpull.v1.XmlPullParser;
-import org.xmlpull.v1.XmlSerializer;
-
 import android.annotation.SystemApi;
 import android.app.NotificationManager.Importance;
 import android.content.Intent;
@@ -31,6 +26,11 @@
 import android.service.notification.NotificationListenerService;
 import android.text.TextUtils;
 
+import org.json.JSONException;
+import org.json.JSONObject;
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlSerializer;
+
 import java.io.IOException;
 import java.util.Arrays;
 
@@ -743,7 +743,7 @@
 
     private static String longArrayToString(long[] values) {
         StringBuffer sb = new StringBuffer();
-        if (values != null) {
+        if (values != null && values.length > 0) {
             for (int i = 0; i < values.length - 1; i++) {
                 sb.append(values[i]).append(DELIMITER);
             }
diff --git a/core/java/android/app/WallpaperColors.java b/core/java/android/app/WallpaperColors.java
index b9d3e75..d0791cf 100644
--- a/core/java/android/app/WallpaperColors.java
+++ b/core/java/android/app/WallpaperColors.java
@@ -136,12 +136,12 @@
         }
 
         final int bitmapArea = bitmap.getWidth() * bitmap.getHeight();
+        boolean shouldRecycle = false;
         if (bitmapArea > MAX_WALLPAPER_EXTRACTION_AREA) {
+            shouldRecycle = true;
             Size optimalSize = calculateOptimalSize(bitmap.getWidth(), bitmap.getHeight());
-            Bitmap scaledBitmap = Bitmap.createScaledBitmap(bitmap, optimalSize.getWidth(),
+            bitmap = Bitmap.createScaledBitmap(bitmap, optimalSize.getWidth(),
                     optimalSize.getHeight(), true /* filter */);
-            bitmap.recycle();
-            bitmap = scaledBitmap;
         }
 
         final Palette palette = Palette
@@ -181,6 +181,11 @@
         }
 
         int hints = calculateHints(bitmap);
+
+        if (shouldRecycle) {
+            bitmap.recycle();
+        }
+
         return new WallpaperColors(primary, secondary, tertiary, hints);
     }
 
diff --git a/core/java/android/app/admin/DeviceAdminReceiver.java b/core/java/android/app/admin/DeviceAdminReceiver.java
index 5a356d9..d0d98c9 100644
--- a/core/java/android/app/admin/DeviceAdminReceiver.java
+++ b/core/java/android/app/admin/DeviceAdminReceiver.java
@@ -659,13 +659,13 @@
      * managed provisioning.
      *
      * <p>When provisioning of a managed profile is complete, the managed profile is hidden until
-     * the profile owner calls {DevicePolicyManager#setProfileEnabled(ComponentName admin)}.
+     * the profile owner calls {@link DevicePolicyManager#setProfileEnabled(ComponentName admin)}.
      * Typically a profile owner will enable the profile when it has finished any additional setup
-     * such as adding an account by using the {@link AccountManager} and calling apis to bring the
+     * such as adding an account by using the {@link AccountManager} and calling APIs to bring the
      * profile into the desired state.
      *
      * <p> Note that provisioning completes without waiting for any server interactions, so the
-     * profile owner needs to wait for data to be available if required (e.g. android device ids or
+     * profile owner needs to wait for data to be available if required (e.g. Android device IDs or
      * other data that is set as a result of server interactions).
      *
      * <p>From version {@link android.os.Build.VERSION_CODES#O}, when managed provisioning has
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index c4d22a3..d8da8c5 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -3929,26 +3929,18 @@
 
     /**
      * Called by a device or profile owner to configure an always-on VPN connection through a
-     * specific application for the current user.
-     *
-     * @deprecated this version only exists for compability with previous developer preview builds.
-     *             TODO: delete once there are no longer any live references.
-     * @hide
-     */
-    @Deprecated
-    public void setAlwaysOnVpnPackage(@NonNull ComponentName admin, @Nullable String vpnPackage)
-            throws NameNotFoundException, UnsupportedOperationException {
-        setAlwaysOnVpnPackage(admin, vpnPackage, /* lockdownEnabled */ true);
-    }
-
-    /**
-     * Called by a device or profile owner to configure an always-on VPN connection through a
      * specific application for the current user. This connection is automatically granted and
      * persisted after a reboot.
      * <p>
-     * The designated package should declare a {@link android.net.VpnService} in its manifest
-     * guarded by {@link android.Manifest.permission#BIND_VPN_SERVICE}, otherwise the call will
-     * fail.
+     * To support the always-on feature, an app must
+     * <ul>
+     *     <li>declare a {@link android.net.VpnService} in its manifest, guarded by
+     *         {@link android.Manifest.permission#BIND_VPN_SERVICE};</li>
+     *     <li>target {@link android.os.Build.VERSION_CODES#N API 24} or above; and</li>
+     *     <li><i>not</i> explicitly opt out of the feature through
+     *         {@link android.net.VpnService#METADATA_SUPPORTS_ALWAYS_ON}.</li>
+     * </ul>
+     * The call will fail if called with the package name of an unsupported VPN app.
      *
      * @param vpnPackage The package name for an installed VPN app on the device, or {@code null} to
      *        remove an existing always-on VPN configuration.
diff --git a/core/java/android/app/backup/RestoreDescription.java b/core/java/android/app/backup/RestoreDescription.java
index 611ff07..0250326 100644
--- a/core/java/android/app/backup/RestoreDescription.java
+++ b/core/java/android/app/backup/RestoreDescription.java
@@ -34,7 +34,7 @@
     private final String mPackageName;
     private final int mDataType;
 
-    private static final String NO_MORE_PACKAGES_SENTINEL = "";
+    private static final String NO_MORE_PACKAGES_SENTINEL = "NO_MORE_PACKAGES";
 
     /**
      * Return this constant RestoreDescription from BackupTransport.nextRestorePackage()
diff --git a/core/java/android/app/timezone/Callback.java b/core/java/android/app/timezone/Callback.java
index b51e5ba..aea8038 100644
--- a/core/java/android/app/timezone/Callback.java
+++ b/core/java/android/app/timezone/Callback.java
@@ -27,7 +27,6 @@
  *
  * @hide
  */
-// TODO(nfuller): Expose necessary APIs for OEMs with @SystemApi. http://b/31008728
 public abstract class Callback {
 
     @Retention(RetentionPolicy.SOURCE)
diff --git a/core/java/android/app/timezone/DistroFormatVersion.java b/core/java/android/app/timezone/DistroFormatVersion.java
index e879e8f..be732e4 100644
--- a/core/java/android/app/timezone/DistroFormatVersion.java
+++ b/core/java/android/app/timezone/DistroFormatVersion.java
@@ -35,7 +35,6 @@
  *
  * @hide
  */
-// TODO(nfuller): Expose necessary APIs for OEMs with @SystemApi. http://b/31008728
 public final class DistroFormatVersion implements Parcelable {
 
     private final int mMajorVersion;
diff --git a/core/java/android/app/timezone/DistroRulesVersion.java b/core/java/android/app/timezone/DistroRulesVersion.java
index 1eb9f45..a680594 100644
--- a/core/java/android/app/timezone/DistroRulesVersion.java
+++ b/core/java/android/app/timezone/DistroRulesVersion.java
@@ -36,7 +36,6 @@
  *
  * @hide
  */
-// TODO(nfuller): Expose necessary APIs for OEMs with @SystemApi. http://b/31008728
 public final class DistroRulesVersion implements Parcelable {
 
     private final String mRulesVersion;
diff --git a/core/java/android/app/timezone/RulesManager.java b/core/java/android/app/timezone/RulesManager.java
index 649d894..ad9b698 100644
--- a/core/java/android/app/timezone/RulesManager.java
+++ b/core/java/android/app/timezone/RulesManager.java
@@ -64,7 +64,6 @@
  * {@link Context#TIME_ZONE_RULES_MANAGER_SERVICE}.
  * @hide
  */
-// TODO(nfuller): Expose necessary APIs for OEMs with @SystemApi. http://b/31008728
 public final class RulesManager {
     private static final String TAG = "timezone.RulesManager";
     private static final boolean DEBUG = false;
diff --git a/core/java/android/app/timezone/RulesState.java b/core/java/android/app/timezone/RulesState.java
index 7d6ad21..ec247eb 100644
--- a/core/java/android/app/timezone/RulesState.java
+++ b/core/java/android/app/timezone/RulesState.java
@@ -60,7 +60,6 @@
  *
  * @hide
  */
-// TODO(nfuller): Expose necessary APIs for OEMs with @SystemApi. http://b/31008728
 public final class RulesState implements Parcelable {
 
     @Retention(RetentionPolicy.SOURCE)
diff --git a/core/java/android/app/timezone/RulesUpdaterContract.java b/core/java/android/app/timezone/RulesUpdaterContract.java
index 07b2f33..9c62f46 100644
--- a/core/java/android/app/timezone/RulesUpdaterContract.java
+++ b/core/java/android/app/timezone/RulesUpdaterContract.java
@@ -27,7 +27,6 @@
  *
  * @hide
  */
-// TODO(nfuller): Expose necessary APIs for OEMs with @SystemApi. http://b/31008728
 public final class RulesUpdaterContract {
 
     /**
diff --git a/core/java/android/content/pm/ActivityInfo.java b/core/java/android/content/pm/ActivityInfo.java
index b75c39c..bab3980 100644
--- a/core/java/android/content/pm/ActivityInfo.java
+++ b/core/java/android/content/pm/ActivityInfo.java
@@ -782,6 +782,16 @@
      * constant starts at the high bits.
      */
     public static final int CONFIG_FONT_SCALE = 0x40000000;
+    /**
+     * Bit in {@link #configChanges} that indicates that the activity
+     * can itself handle changes to the rotation.  Set from the
+     * {@link android.R.attr#configChanges} attribute.  This is
+     * not a core resource configuration, but a higher-level value, so its
+     * constant starts at the high bits.
+     * @hide We do not want apps to handle this. It will eventually be moved out of
+     * {@link Configuration}.
+     */
+    public static final int CONFIG_ROTATION = 0x20000000;
 
     /** @hide
      * Unfortunately the constants for config changes in native code are
diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl
index e800e88..afb798c 100644
--- a/core/java/android/content/pm/IPackageManager.aidl
+++ b/core/java/android/content/pm/IPackageManager.aidl
@@ -513,7 +513,7 @@
      * configuration.
      */
     boolean performDexOpt(String packageName, boolean checkProfiles,
-            int compileReason, boolean force, boolean bootComplete);
+            int compileReason, boolean force, boolean bootComplete, boolean downgrade);
 
     /**
      * Ask the package manager to perform a dex-opt with the given compiler filter.
diff --git a/core/java/android/content/pm/LauncherApps.java b/core/java/android/content/pm/LauncherApps.java
index ed41e79..aa9562f 100644
--- a/core/java/android/content/pm/LauncherApps.java
+++ b/core/java/android/content/pm/LauncherApps.java
@@ -608,15 +608,15 @@
     }
 
     /**
-     * Get {@link ApplicationInfo} for a profile
+     * Returns {@link ApplicationInfo} about an application installed for a specific user profile.
      *
      * @param packageName The package name of the application
      * @param flags Additional option flags {@link PackageManager#getApplicationInfo}
      * @param user The UserHandle of the profile.
      *
-     * @return An {@link ApplicationInfo} containing information about the package or
-     *         null if the package isn't installed for the given user, or the target user
-     *         is not enabled.
+     * @return {@link ApplicationInfo} containing information about the package. Returns
+     *         {@code null} if the package isn't installed for the given profile, or the profile
+     *         isn't enabled.
      */
     public ApplicationInfo getApplicationInfo(@NonNull String packageName,
             @ApplicationInfoFlags int flags, @NonNull UserHandle user)
diff --git a/core/java/android/content/pm/PackageBackwardCompatibility.java b/core/java/android/content/pm/PackageBackwardCompatibility.java
new file mode 100644
index 0000000..4de160b
--- /dev/null
+++ b/core/java/android/content/pm/PackageBackwardCompatibility.java
@@ -0,0 +1,78 @@
+/*
+ * 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.content.pm;
+
+import android.annotation.Nullable;
+import android.content.pm.PackageParser.Package;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.ArrayUtils;
+
+import java.util.ArrayList;
+
+/**
+ * Modifies {@link Package} in order to maintain backwards compatibility.
+ *
+ * @hide
+ */
+@VisibleForTesting
+public class PackageBackwardCompatibility {
+
+    private static final String ANDROID_TEST_MOCK = "android.test.mock";
+
+    private static final String ANDROID_TEST_RUNNER = "android.test.runner";
+
+    /**
+     * Modify the shared libraries in the supplied {@link Package} to maintain backwards
+     * compatibility.
+     *
+     * @param pkg the {@link Package} to modify.
+     */
+    @VisibleForTesting
+    public static void modifySharedLibraries(Package pkg) {
+        ArrayList<String> usesLibraries = pkg.usesLibraries;
+        ArrayList<String> usesOptionalLibraries = pkg.usesOptionalLibraries;
+
+        usesLibraries = orgApacheHttpLegacy(usesLibraries);
+        usesOptionalLibraries = orgApacheHttpLegacy(usesOptionalLibraries);
+
+        // android.test.runner has a dependency on android.test.mock so if android.test.runner
+        // is present but android.test.mock is not then add android.test.mock.
+        boolean androidTestMockPresent = ArrayUtils.contains(usesLibraries, ANDROID_TEST_MOCK)
+                || ArrayUtils.contains(usesOptionalLibraries, ANDROID_TEST_MOCK);
+        if (ArrayUtils.contains(usesLibraries, ANDROID_TEST_RUNNER) && !androidTestMockPresent) {
+            usesLibraries.add(ANDROID_TEST_MOCK);
+        }
+        if (ArrayUtils.contains(usesOptionalLibraries, ANDROID_TEST_RUNNER)
+                && !androidTestMockPresent) {
+            usesOptionalLibraries.add(ANDROID_TEST_MOCK);
+        }
+
+        pkg.usesLibraries = usesLibraries;
+        pkg.usesOptionalLibraries = usesOptionalLibraries;
+    }
+
+    private static ArrayList<String> orgApacheHttpLegacy(@Nullable ArrayList<String> libraries) {
+        // "org.apache.http.legacy" is now a part of the boot classpath so it doesn't need
+        // to be an explicit dependency.
+        //
+        // A future change will remove this library from the boot classpath, at which point
+        // all apps that target SDK 21 and earlier will have it automatically added to their
+        // dependency lists.
+        return ArrayUtils.remove(libraries, "org.apache.http.legacy");
+    }
+}
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index 827711a..040f85b 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -54,7 +54,6 @@
 import android.os.UserManager;
 import android.os.storage.StorageManager;
 import android.os.storage.VolumeInfo;
-import android.provider.Settings;
 import android.util.AndroidException;
 import android.util.Log;
 
@@ -1901,12 +1900,13 @@
     public static final String FEATURE_VULKAN_HARDWARE_VERSION = "android.hardware.vulkan.version";
 
     /**
-     * The device includes broadcast radio tuner.
-     *
-     * @hide FutureFeature
+     * Feature for {@link #getSystemAvailableFeatures} and
+     * {@link #hasSystemFeature}: The device includes broadcast radio tuner.
+     * @hide
      */
+    @SystemApi
     @SdkConstant(SdkConstantType.FEATURE)
-    public static final String FEATURE_RADIO = "android.hardware.radio";
+    public static final String FEATURE_BROADCAST_RADIO = "android.hardware.broadcastradio";
 
     /**
      * Feature for {@link #getSystemAvailableFeatures} and
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index eb6e0d8..c76ca6d 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -40,7 +40,7 @@
 import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION;
 import static android.os.Build.VERSION_CODES.O;
 import static android.os.Trace.TRACE_TAG_PACKAGE_MANAGER;
-import static android.view.WindowManager.LayoutParams.ROTATION_ANIMATION_ROTATE;
+import static android.view.WindowManager.LayoutParams.ROTATION_ANIMATION_UNSPECIFIED;
 
 import android.annotation.IntRange;
 import android.annotation.NonNull;
@@ -202,6 +202,8 @@
     // Temporary workaround; allow meta-data to expose components to instant apps
     private static final String META_DATA_INSTANT_APPS = "instantapps.clients.allowed";
 
+    private static final String METADATA_MAX_ASPECT_RATIO = "android.max_aspect";
+
     /**
      * Bit mask of all the valid bits that can be set in recreateOnConfigChanges.
      * @hide
@@ -3662,6 +3664,7 @@
         // getting added to the wrong package.
         final CachedComponentArgs cachedArgs = new CachedComponentArgs();
         int type;
+
         while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
                 && (type != XmlPullParser.END_TAG || parser.getDepth() > innerDepth)) {
             if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
@@ -3839,7 +3842,11 @@
             }
         }
 
-        modifySharedLibrariesForBackwardCompatibility(owner);
+        // Must be ran after the entire {@link ApplicationInfo} has been fully processed and after
+        // every activity info has had a chance to set it from its attributes.
+        setMaxAspectRatio(owner);
+
+        PackageBackwardCompatibility.modifySharedLibraries(owner);
 
         if (hasDomainURLs(owner)) {
             owner.applicationInfo.privateFlags |= ApplicationInfo.PRIVATE_FLAG_HAS_DOMAIN_URLS;
@@ -3850,18 +3857,6 @@
         return true;
     }
 
-    private static void modifySharedLibrariesForBackwardCompatibility(Package owner) {
-        // "org.apache.http.legacy" is now a part of the boot classpath so it doesn't need
-        // to be an explicit dependency.
-        //
-        // A future change will remove this library from the boot classpath, at which point
-        // all apps that target SDK 21 and earlier will have it automatically added to their
-        // dependency lists.
-        owner.usesLibraries = ArrayUtils.remove(owner.usesLibraries, "org.apache.http.legacy");
-        owner.usesOptionalLibraries = ArrayUtils.remove(owner.usesOptionalLibraries,
-                "org.apache.http.legacy");
-    }
-
     /**
      * Check if one of the IntentFilter as both actions DEFAULT / VIEW and a HTTP/HTTPS data URI
      */
@@ -4286,7 +4281,12 @@
                 a.info.flags |= FLAG_ALWAYS_FOCUSABLE;
             }
 
-            setActivityMaxAspectRatio(a.info, sa, owner);
+            if (sa.hasValue(R.styleable.AndroidManifestActivity_maxAspectRatio)
+                    && sa.getType(R.styleable.AndroidManifestActivity_maxAspectRatio)
+                    == TypedValue.TYPE_FLOAT) {
+                a.setMaxAspectRatio(sa.getFloat(R.styleable.AndroidManifestActivity_maxAspectRatio,
+                        0 /*default*/));
+            }
 
             a.info.lockTaskLaunchMode =
                     sa.getInt(R.styleable.AndroidManifestActivity_lockTaskMode, 0);
@@ -4299,7 +4299,7 @@
                 sa.getString(R.styleable.AndroidManifestActivity_enableVrMode);
 
             a.info.rotationAnimation =
-                sa.getInt(R.styleable.AndroidManifestActivity_rotationAnimation, ROTATION_ANIMATION_ROTATE);
+                sa.getInt(R.styleable.AndroidManifestActivity_rotationAnimation, ROTATION_ANIMATION_UNSPECIFIED);
 
             a.info.colorMode = sa.getInt(R.styleable.AndroidManifestActivity_colorMode,
                     ActivityInfo.COLOR_MODE_DEFAULT);
@@ -4533,28 +4533,40 @@
         }
     }
 
-    private void setActivityMaxAspectRatio(ActivityInfo aInfo, TypedArray sa, Package owner) {
-        if (aInfo.resizeMode == RESIZE_MODE_RESIZEABLE
-                || aInfo.resizeMode == RESIZE_MODE_RESIZEABLE_VIA_SDK_VERSION) {
-            // Resizeable activities can be put in any aspect ratio.
-            aInfo.maxAspectRatio = 0;
-            return;
-        }
-
+    /**
+     * Sets every the max aspect ratio of every child activity that doesn't already have an aspect
+     * ratio set.
+     */
+    private void setMaxAspectRatio(Package owner) {
         // Default to (1.86) 16.7:9 aspect ratio for pre-O apps and unset for O and greater.
         // NOTE: 16.7:9 was the max aspect ratio Android devices can support pre-O per the CDD.
-        float defaultMaxAspectRatio = owner.applicationInfo.targetSdkVersion < O
+        float maxAspectRatio = owner.applicationInfo.targetSdkVersion < O
                 ? DEFAULT_PRE_O_MAX_ASPECT_RATIO : 0;
-        if (owner.applicationInfo.maxAspectRatio != 0 ) {
+
+        if (owner.applicationInfo.maxAspectRatio != 0) {
             // Use the application max aspect ration as default if set.
-            defaultMaxAspectRatio = owner.applicationInfo.maxAspectRatio;
+            maxAspectRatio = owner.applicationInfo.maxAspectRatio;
+        } else if (owner.mAppMetaData != null
+                && owner.mAppMetaData.containsKey(METADATA_MAX_ASPECT_RATIO)) {
+            maxAspectRatio = owner.mAppMetaData.getFloat(METADATA_MAX_ASPECT_RATIO, maxAspectRatio);
         }
 
-        aInfo.maxAspectRatio = sa.getFloat(
-                R.styleable.AndroidManifestActivity_maxAspectRatio, defaultMaxAspectRatio);
-        if (aInfo.maxAspectRatio < 1.0f && aInfo.maxAspectRatio != 0) {
-            // Ignore any value lesser than 1.0.
-            aInfo.maxAspectRatio = 0;
+        for (Activity activity : owner.activities) {
+            // If the max aspect ratio for the activity has already been set, skip.
+            if (activity.hasMaxAspectRatio()) {
+                continue;
+            }
+
+            // By default we prefer to use a values defined on the activity directly than values
+            // defined on the application. We do not check the styled attributes on the activity
+            // as it would have already been set when we processed the activity. We wait to process
+            // the meta data here since this method is called at the end of processing the
+            // application and all meta data is guaranteed.
+            final float activityAspectRatio = activity.metaData != null
+                    ? activity.metaData.getFloat(METADATA_MAX_ASPECT_RATIO, maxAspectRatio)
+                    : maxAspectRatio;
+
+            activity.setMaxAspectRatio(activityAspectRatio);
         }
     }
 
@@ -4696,6 +4708,7 @@
         info.windowLayout = target.info.windowLayout;
         info.resizeMode = target.info.resizeMode;
         info.maxAspectRatio = target.info.maxAspectRatio;
+
         info.encryptionAware = info.directBootAware = target.info.directBootAware;
 
         Activity a = new Activity(cachedArgs.mActivityAliasArgs, info);
@@ -6980,6 +6993,11 @@
 
     public final static class Activity extends Component<ActivityIntentInfo> implements Parcelable {
         public final ActivityInfo info;
+        private boolean mHasMaxAspectRatio;
+
+        private boolean hasMaxAspectRatio() {
+            return mHasMaxAspectRatio;
+        }
 
         public Activity(final ParseComponentArgs args, final ActivityInfo _info) {
             super(args, _info);
@@ -6992,6 +7010,23 @@
             info.packageName = packageName;
         }
 
+
+        private void setMaxAspectRatio(float maxAspectRatio) {
+            if (info.resizeMode == RESIZE_MODE_RESIZEABLE
+                    || info.resizeMode == RESIZE_MODE_RESIZEABLE_VIA_SDK_VERSION) {
+                // Resizeable activities can be put in any aspect ratio.
+                return;
+            }
+
+            if (maxAspectRatio < 1.0f && maxAspectRatio != 0) {
+                // Ignore any value lesser than 1.0.
+                return;
+            }
+
+            info.maxAspectRatio = maxAspectRatio;
+            mHasMaxAspectRatio = true;
+        }
+
         public String toString() {
             StringBuilder sb = new StringBuilder(128);
             sb.append("Activity{");
@@ -7011,11 +7046,13 @@
         public void writeToParcel(Parcel dest, int flags) {
             super.writeToParcel(dest, flags);
             dest.writeParcelable(info, flags | Parcelable.PARCELABLE_ELIDE_DUPLICATES);
+            dest.writeBoolean(mHasMaxAspectRatio);
         }
 
         private Activity(Parcel in) {
             super(in);
             info = in.readParcelable(Object.class.getClassLoader());
+            mHasMaxAspectRatio = in.readBoolean();
 
             for (ActivityIntentInfo aii : intents) {
                 aii.activity = this;
diff --git a/core/java/android/content/res/Configuration.java b/core/java/android/content/res/Configuration.java
index ef279b8..68d4cd8 100644
--- a/core/java/android/content/res/Configuration.java
+++ b/core/java/android/content/res/Configuration.java
@@ -1395,7 +1395,7 @@
         }
         if ((compareUndefined || delta.mRotation != ROTATION_UNDEFINED)
                 && mRotation != delta.mRotation) {
-            changed |= ActivityInfo.CONFIG_ORIENTATION;
+            changed |= ActivityInfo.CONFIG_ROTATION;
         }
         if ((compareUndefined || getScreenLayoutNoDirection(delta.screenLayout) !=
                 (SCREENLAYOUT_SIZE_UNDEFINED | SCREENLAYOUT_LONG_UNDEFINED))
diff --git a/core/java/android/content/res/Resources.java b/core/java/android/content/res/Resources.java
index 60226d5..e173653c 100644
--- a/core/java/android/content/res/Resources.java
+++ b/core/java/android/content/res/Resources.java
@@ -19,6 +19,7 @@
 import android.animation.Animator;
 import android.animation.StateListAnimator;
 import android.annotation.AnimRes;
+import android.annotation.AnimatorRes;
 import android.annotation.AnyRes;
 import android.annotation.ArrayRes;
 import android.annotation.AttrRes;
@@ -1162,7 +1163,7 @@
      *         
      * @see #getXml
      */
-    public XmlResourceParser getAnimation(@AnimRes int id) throws NotFoundException {
+    public XmlResourceParser getAnimation(@AnimatorRes @AnimRes int id) throws NotFoundException {
         return loadXmlResourceParser(id, "anim");
     }
 
diff --git a/core/java/android/content/res/ResourcesImpl.java b/core/java/android/content/res/ResourcesImpl.java
index 23591c7..a8b8c4b 100644
--- a/core/java/android/content/res/ResourcesImpl.java
+++ b/core/java/android/content/res/ResourcesImpl.java
@@ -15,9 +15,6 @@
  */
 package android.content.res;
 
-import org.xmlpull.v1.XmlPullParser;
-import org.xmlpull.v1.XmlPullParserException;
-
 import android.animation.Animator;
 import android.animation.StateListAnimator;
 import android.annotation.AnyRes;
@@ -32,7 +29,7 @@
 import android.content.pm.ActivityInfo.Config;
 import android.content.res.Configuration.NativeConfig;
 import android.content.res.Resources.NotFoundException;
-import android.graphics.FontFamily;
+import android.graphics.Bitmap;
 import android.graphics.Typeface;
 import android.graphics.drawable.ColorDrawable;
 import android.graphics.drawable.Drawable;
@@ -40,8 +37,9 @@
 import android.icu.text.PluralRules;
 import android.os.Build;
 import android.os.LocaleList;
+import android.os.SystemClock;
+import android.os.SystemProperties;
 import android.os.Trace;
-import android.text.FontConfig;
 import android.util.AttributeSet;
 import android.util.DisplayMetrics;
 import android.util.Log;
@@ -51,10 +49,12 @@
 import android.util.Xml;
 import android.view.DisplayAdjustments;
 
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
 import java.io.IOException;
 import java.io.InputStream;
 import java.util.Arrays;
-import java.util.List;
 import java.util.Locale;
 
 /**
@@ -72,9 +72,20 @@
 
     private static final boolean DEBUG_LOAD = false;
     private static final boolean DEBUG_CONFIG = false;
-    private static final boolean TRACE_FOR_PRELOAD = false;
-    private static final boolean TRACE_FOR_MISS_PRELOAD = false;
 
+    static final String TAG_PRELOAD = TAG + ".preload";
+
+    private static final boolean TRACE_FOR_PRELOAD = false; // Do we still need it?
+    private static final boolean TRACE_FOR_MISS_PRELOAD = false; // Do we still need it?
+
+    public static final boolean TRACE_FOR_DETAILED_PRELOAD =
+            SystemProperties.getBoolean("debug.trace_resource_preload", false);
+
+    /** Used only when TRACE_FOR_DETAILED_PRELOAD is true. */
+    private static int sPreloadTracingNumLoadedDrawables;
+    private long mPreloadTracingPreloadStartTime;
+    private long mPreloadTracingStartBitmapSize;
+    private long mPreloadTracingStartBitmapCount;
 
     private static final int ID_OTHER = 0x01000004;
 
@@ -593,6 +604,16 @@
             Drawable dr;
             boolean needsNewDrawableAfterCache = false;
             if (cs != null) {
+                if (TRACE_FOR_DETAILED_PRELOAD) {
+                    // Log only framework resources
+                    if (((id >>> 24) == 0x1) && (android.os.Process.myUid() != 0)) {
+                        final String name = getResourceName(id);
+                        if (name != null) {
+                            Log.d(TAG_PRELOAD, "Hit preloaded FW drawable #"
+                                    + Integer.toHexString(id) + " " + name);
+                        }
+                    }
+                }
                 dr = cs.newDrawable(wrapper);
             } else if (isColorDrawable) {
                 dr = new ColorDrawable(value.data);
@@ -744,6 +765,18 @@
             }
         }
 
+        // For prelaod tracing.
+        long startTime = 0;
+        int startBitmapCount = 0;
+        long startBitmapSize = 0;
+        int startDrwableCount = 0;
+        if (TRACE_FOR_DETAILED_PRELOAD) {
+            startTime = System.nanoTime();
+            startBitmapCount = Bitmap.sPreloadTracingNumInstantiatedBitmaps;
+            startBitmapSize = Bitmap.sPreloadTracingTotalBitmapsSize;
+            startDrwableCount = sPreloadTracingNumLoadedDrawables;
+        }
+
         if (DEBUG_LOAD) {
             Log.v(TAG, "Loading drawable for cookie " + value.assetCookie + ": " + file);
         }
@@ -772,6 +805,37 @@
         }
         Trace.traceEnd(Trace.TRACE_TAG_RESOURCES);
 
+        if (TRACE_FOR_DETAILED_PRELOAD) {
+            if (((id >>> 24) == 0x1)) {
+                final String name = getResourceName(id);
+                if (name != null) {
+                    final long time = System.nanoTime() - startTime;
+                    final int loadedBitmapCount =
+                            Bitmap.sPreloadTracingNumInstantiatedBitmaps - startBitmapCount;
+                    final long loadedBitmapSize =
+                            Bitmap.sPreloadTracingTotalBitmapsSize - startBitmapSize;
+                    final int loadedDrawables =
+                            sPreloadTracingNumLoadedDrawables - startDrwableCount;
+
+                    sPreloadTracingNumLoadedDrawables++;
+
+                    final boolean isRoot = (android.os.Process.myUid() == 0);
+
+                    Log.d(TAG_PRELOAD,
+                            (isRoot ? "Preloaded FW drawable #"
+                                    : "Loaded non-preloaded FW drawable #")
+                            + Integer.toHexString(id)
+                            + " " + name
+                            + " " + file
+                            + " " + dr.getClass().getCanonicalName()
+                            + " #nested_drawables= " + loadedDrawables
+                            + " #bitmaps= " + loadedBitmapCount
+                            + " total_bitmap_size= " + loadedBitmapSize
+                            + " in[us] " + (time / 1000));
+                }
+            }
+        }
+
         return dr;
     }
 
@@ -1102,6 +1166,13 @@
             mPreloading = true;
             mConfiguration.densityDpi = DisplayMetrics.DENSITY_DEVICE;
             updateConfiguration(null, null, null);
+
+            if (TRACE_FOR_DETAILED_PRELOAD) {
+                mPreloadTracingPreloadStartTime = SystemClock.uptimeMillis();
+                mPreloadTracingStartBitmapSize = Bitmap.sPreloadTracingTotalBitmapsSize;
+                mPreloadTracingStartBitmapCount = Bitmap.sPreloadTracingNumInstantiatedBitmaps;
+                Log.d(TAG_PRELOAD, "Preload starting");
+            }
         }
     }
 
@@ -1111,6 +1182,16 @@
      */
     void finishPreloading() {
         if (mPreloading) {
+            if (TRACE_FOR_DETAILED_PRELOAD) {
+                final long time = SystemClock.uptimeMillis() - mPreloadTracingPreloadStartTime;
+                final long size =
+                        Bitmap.sPreloadTracingTotalBitmapsSize - mPreloadTracingStartBitmapSize;
+                final long count = Bitmap.sPreloadTracingNumInstantiatedBitmaps
+                        - mPreloadTracingStartBitmapCount;
+                Log.d(TAG_PRELOAD, "Preload finished, "
+                        + count + " bitmaps of " + size + " bytes in " + time + " ms");
+            }
+
             mPreloading = false;
             flushLayoutCache();
         }
diff --git a/core/java/android/database/ContentObserver.java b/core/java/android/database/ContentObserver.java
index 4795e97..5f01e30 100644
--- a/core/java/android/database/ContentObserver.java
+++ b/core/java/android/database/ContentObserver.java
@@ -193,11 +193,6 @@
      */
     private void dispatchChange(boolean selfChange, Uri uri, int userId) {
         if (mHandler == null) {
-            synchronized (mLock) {
-                if (mTransport == null) {
-                    return;
-                }
-            }
             onChange(selfChange, uri, userId);
         } else {
             mHandler.post(new NotificationRunnable(selfChange, uri, userId));
@@ -218,11 +213,6 @@
 
         @Override
         public void run() {
-            synchronized (mLock) {
-                if (mTransport == null) {
-                    return;
-                }
-            }
             ContentObserver.this.onChange(mSelfChange, mUri, mUserId);
         }
     }
diff --git a/core/java/android/hardware/SensorManager.java b/core/java/android/hardware/SensorManager.java
index 4bc62b1..e1cd451 100644
--- a/core/java/android/hardware/SensorManager.java
+++ b/core/java/android/hardware/SensorManager.java
@@ -894,8 +894,9 @@
      * to free up resource in sensor system associated with the direct channel.
      *
      * @param mem A {@link android.os.MemoryFile} shared memory object.
-     * @return A {@link android.hardware.SensorDirectChannel} object if successful, null otherwise.
+     * @return A {@link android.hardware.SensorDirectChannel} object.
      * @throws NullPointerException when mem is null.
+     * @throws UncheckedIOException if not able to create channel.
      * @see SensorDirectChannel#close()
      * @see #configureDirectChannel(SensorDirectChannel, Sensor, int)
      */
@@ -916,9 +917,9 @@
      * to free up resource in sensor system associated with the direct channel.
      *
      * @param mem A {@link android.hardware.HardwareBuffer} shared memory object.
-     * @return A {@link android.hardware.SensorDirectChannel} object if successful,
-     *         null otherwise.
+     * @return A {@link android.hardware.SensorDirectChannel} object.
      * @throws NullPointerException when mem is null.
+     * @throws UncheckedIOException if not able to create channel.
      * @see SensorDirectChannel#close()
      * @see #configureDirectChannel(SensorDirectChannel, Sensor, int)
      */
diff --git a/core/java/android/hardware/camera2/CameraManager.java b/core/java/android/hardware/camera2/CameraManager.java
index 1b150bf..90bf896 100644
--- a/core/java/android/hardware/camera2/CameraManager.java
+++ b/core/java/android/hardware/camera2/CameraManager.java
@@ -16,28 +16,29 @@
 
 package android.hardware.camera2;
 
-import android.annotation.RequiresPermission;
-import android.annotation.SystemService;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.annotation.RequiresPermission;
+import android.annotation.SystemService;
 import android.content.Context;
-import android.hardware.ICameraService;
-import android.hardware.ICameraServiceListener;
 import android.hardware.CameraInfo;
 import android.hardware.CameraStatus;
+import android.hardware.ICameraService;
+import android.hardware.ICameraServiceListener;
 import android.hardware.camera2.impl.CameraMetadataNative;
 import android.hardware.camera2.legacy.CameraDeviceUserShim;
 import android.hardware.camera2.legacy.LegacyMetadataMapper;
-import android.os.IBinder;
 import android.os.Binder;
 import android.os.DeadObjectException;
 import android.os.Handler;
+import android.os.IBinder;
 import android.os.Looper;
 import android.os.RemoteException;
 import android.os.ServiceManager;
 import android.os.ServiceSpecificException;
-import android.util.Log;
+import android.os.SystemProperties;
 import android.util.ArrayMap;
+import android.util.Log;
 
 import java.util.ArrayList;
 
@@ -210,7 +211,9 @@
     public CameraCharacteristics getCameraCharacteristics(@NonNull String cameraId)
             throws CameraAccessException {
         CameraCharacteristics characteristics = null;
-
+        if (CameraManagerGlobal.sCameraServiceDisabled) {
+            throw new IllegalArgumentException("No cameras available on device");
+        }
         synchronized (mLock) {
             /*
              * Get the camera characteristics from the camera service directly if it supports it,
@@ -462,6 +465,9 @@
                         "Handler argument is null, but no looper exists in the calling thread");
             }
         }
+        if (CameraManagerGlobal.sCameraServiceDisabled) {
+            throw new IllegalArgumentException("No cameras available on device");
+        }
 
         openCameraDeviceUserAsync(cameraId, callback, handler, clientUid);
     }
@@ -507,6 +513,9 @@
      */
     public void setTorchMode(@NonNull String cameraId, boolean enabled)
             throws CameraAccessException {
+        if (CameraManagerGlobal.sCameraServiceDisabled) {
+            throw new IllegalArgumentException("No cameras available on device");
+        }
         CameraManagerGlobal.get().setTorchMode(cameraId, enabled);
     }
 
@@ -745,6 +754,9 @@
         private CameraManagerGlobal() {
         }
 
+        public static final boolean sCameraServiceDisabled =
+                SystemProperties.getBoolean("config.disable_cameraservice", false);
+
         public static CameraManagerGlobal get() {
             return gCameraManager;
         }
@@ -764,7 +776,7 @@
         public ICameraService getCameraService() {
             synchronized(mLock) {
                 connectCameraServiceLocked();
-                if (mCameraService == null) {
+                if (mCameraService == null && !sCameraServiceDisabled) {
                     Log.e(TAG, "Camera service is unavailable");
                 }
                 return mCameraService;
@@ -779,7 +791,7 @@
          */
         private void connectCameraServiceLocked() {
             // Only reconnect if necessary
-            if (mCameraService != null) return;
+            if (mCameraService != null || sCameraServiceDisabled) return;
 
             Log.i(TAG, "Connecting to camera service");
 
diff --git a/core/java/android/hardware/location/NanoApp.java b/core/java/android/hardware/location/NanoApp.java
index d5d428e..0465def 100644
--- a/core/java/android/hardware/location/NanoApp.java
+++ b/core/java/android/hardware/location/NanoApp.java
@@ -56,10 +56,10 @@
      * {@link #setAppBinary(byte[])} and {@link #setAppId(long)} must be called
      * prior to passing this object to any managers.
      *
-     * @see #NanoApp(int, byte[])
+     * @see #NanoApp(long, byte[])
      */
     public NanoApp() {
-        this(0, null);
+        this(0L, null);
         mAppIdSet = false;
     }
 
diff --git a/core/java/android/hardware/radio/ITuner.aidl b/core/java/android/hardware/radio/ITuner.aidl
index 69e135d..2dc6601 100644
--- a/core/java/android/hardware/radio/ITuner.aidl
+++ b/core/java/android/hardware/radio/ITuner.aidl
@@ -16,6 +16,7 @@
 
 package android.hardware.radio;
 
+import android.hardware.radio.ProgramSelector;
 import android.hardware.radio.RadioManager;
 
 /** {@hide} */
@@ -52,13 +53,15 @@
      * @throws IllegalArgumentException if invalid arguments are passed
      * @throws IllegalStateException if called out of sequence
      */
-    void tune(int channel, int subChannel);
+    void tune(in ProgramSelector selector);
 
     /**
      * @throws IllegalStateException if called out of sequence
      */
     void cancel();
 
+    void cancelAnnouncement();
+
     RadioManager.ProgramInfo getProgramInformation();
 
     /**
diff --git a/core/java/android/hardware/radio/ITunerCallback.aidl b/core/java/android/hardware/radio/ITunerCallback.aidl
index aed114e..c3bbaec 100644
--- a/core/java/android/hardware/radio/ITunerCallback.aidl
+++ b/core/java/android/hardware/radio/ITunerCallback.aidl
@@ -23,8 +23,7 @@
 oneway interface ITunerCallback {
     void onError(int status);
     void onConfigurationChanged(in RadioManager.BandConfig config);
-    void onProgramInfoChanged(in RadioManager.ProgramInfo info);
-    void onMetadataChanged(in RadioMetadata metadata);
+    void onProgramInfoChanged();
     void onTrafficAnnouncement(boolean active);
     void onEmergencyAnnouncement(boolean active);
     void onAntennaState(boolean connected);
diff --git a/core/java/android/hardware/radio/ProgramSelector.aidl b/core/java/android/hardware/radio/ProgramSelector.aidl
new file mode 100644
index 0000000..545269a
--- /dev/null
+++ b/core/java/android/hardware/radio/ProgramSelector.aidl
@@ -0,0 +1,20 @@
+/**
+ * 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.hardware.radio;
+
+/** @hide */
+parcelable ProgramSelector;
diff --git a/core/java/android/hardware/radio/ProgramSelector.java b/core/java/android/hardware/radio/ProgramSelector.java
new file mode 100644
index 0000000..4638dd5
--- /dev/null
+++ b/core/java/android/hardware/radio/ProgramSelector.java
@@ -0,0 +1,506 @@
+/**
+ * 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.hardware.radio;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
+import java.util.stream.Stream;
+
+/**
+ * A set of identifiers necessary to tune to a given station.
+ *
+ * This can hold various identifiers, like
+ * - AM/FM frequency
+ * - HD Radio subchannel
+ * - DAB channel info
+ *
+ * The primary ID uniquely identifies a station and can be used for equality
+ * check. The secondary IDs are supplementary and can speed up tuning process,
+ * but the primary ID is sufficient (ie. after a full band scan).
+ *
+ * Two selectors with different secondary IDs, but the same primary ID are
+ * considered equal. In particular, secondary IDs vector may get updated for
+ * an entry on the program list (ie. when a better frequency for a given
+ * station is found).
+ *
+ * The primaryId of a given programType MUST be of a specific type:
+ * - AM, FM: RDS_PI if the station broadcasts RDS, AMFM_FREQUENCY otherwise;
+ * - AM_HD, FM_HD: HD_STATION_ID_EXT;
+ * - DAB: DAB_SIDECC;
+ * - DRMO: DRMO_SERVICE_ID;
+ * - SXM: SXM_SERVICE_ID;
+ * - VENDOR: VENDOR_PRIMARY.
+ * @hide
+ */
+@SystemApi
+public final class ProgramSelector implements Parcelable {
+    /** Analogue AM radio (with or without RDS). */
+    public static final int PROGRAM_TYPE_AM = 1;
+    /** analogue FM radio (with or without RDS). */
+    public static final int PROGRAM_TYPE_FM = 2;
+    /** AM HD Radio. */
+    public static final int PROGRAM_TYPE_AM_HD = 3;
+    /** FM HD Radio. */
+    public static final int PROGRAM_TYPE_FM_HD = 4;
+    /** Digital audio broadcasting. */
+    public static final int PROGRAM_TYPE_DAB = 5;
+    /** Digital Radio Mondiale. */
+    public static final int PROGRAM_TYPE_DRMO = 6;
+    /** SiriusXM Satellite Radio. */
+    public static final int PROGRAM_TYPE_SXM = 7;
+    /** Vendor-specific, not synced across devices. */
+    public static final int PROGRAM_TYPE_VENDOR1 = 8;
+    public static final int PROGRAM_TYPE_VENDOR2 = 9;
+    public static final int PROGRAM_TYPE_VENDOR3 = 10;
+    public static final int PROGRAM_TYPE_VENDOR4 = 11;
+    @IntDef(prefix = { "PROGRAM_TYPE_" }, value = {
+        PROGRAM_TYPE_AM,
+        PROGRAM_TYPE_FM,
+        PROGRAM_TYPE_AM_HD,
+        PROGRAM_TYPE_FM_HD,
+        PROGRAM_TYPE_DAB,
+        PROGRAM_TYPE_DRMO,
+        PROGRAM_TYPE_SXM,
+        PROGRAM_TYPE_VENDOR1,
+        PROGRAM_TYPE_VENDOR2,
+        PROGRAM_TYPE_VENDOR3,
+        PROGRAM_TYPE_VENDOR4,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface ProgramType {}
+
+    /** kHz */
+    public static final int IDENTIFIER_TYPE_AMFM_FREQUENCY = 1;
+    /** 16bit */
+    public static final int IDENTIFIER_TYPE_RDS_PI = 2;
+    /**
+     * 64bit compound primary identifier for HD Radio.
+     *
+     * Consists of (from the LSB):
+     * - 32bit: Station ID number;
+     * - 4bit: HD_SUBCHANNEL;
+     * - 18bit: AMFM_FREQUENCY.
+     * The remaining bits should be set to zeros when writing on the chip side
+     * and ignored when read.
+     */
+    public static final int IDENTIFIER_TYPE_HD_STATION_ID_EXT = 3;
+    /**
+     * HD Radio subchannel - a value of range 0-7.
+     *
+     * 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).
+     */
+    public static final int IDENTIFIER_TYPE_HD_SUBCHANNEL = 4;
+    /**
+     * 24bit compound primary identifier for DAB.
+     *
+     * Consists of (from the LSB):
+     * - 16bit: SId;
+     * - 8bit: ECC code.
+     * 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;
+    /** 16bit */
+    public static final int IDENTIFIER_TYPE_DAB_ENSEMBLE = 6;
+    /** 12bit */
+    public static final int IDENTIFIER_TYPE_DAB_SCID = 7;
+    /** kHz */
+    public static final int IDENTIFIER_TYPE_DAB_FREQUENCY = 8;
+    /** 24bit */
+    public static final int IDENTIFIER_TYPE_DRMO_SERVICE_ID = 9;
+    /** kHz */
+    public static final int IDENTIFIER_TYPE_DRMO_FREQUENCY = 10;
+    /** 32bit */
+    public static final int IDENTIFIER_TYPE_SXM_SERVICE_ID = 11;
+    /** 0-999 range */
+    public static final int IDENTIFIER_TYPE_SXM_CHANNEL = 12;
+    /**
+     * Primary identifier for vendor-specific radio technology.
+     * The value format is determined by a vendor.
+     *
+     * It must not be used in any other programType than VENDORx.
+     */
+    public static final int IDENTIFIER_TYPE_VENDOR1_PRIMARY = 13;
+    public static final int IDENTIFIER_TYPE_VENDOR2_PRIMARY = 14;
+    public static final int IDENTIFIER_TYPE_VENDOR3_PRIMARY = 15;
+    public static final int IDENTIFIER_TYPE_VENDOR4_PRIMARY = 16;
+    @IntDef(prefix = { "IDENTIFIER_TYPE_" }, value = {
+        IDENTIFIER_TYPE_AMFM_FREQUENCY,
+        IDENTIFIER_TYPE_RDS_PI,
+        IDENTIFIER_TYPE_HD_STATION_ID_EXT,
+        IDENTIFIER_TYPE_HD_SUBCHANNEL,
+        IDENTIFIER_TYPE_DAB_SIDECC,
+        IDENTIFIER_TYPE_DAB_ENSEMBLE,
+        IDENTIFIER_TYPE_DAB_SCID,
+        IDENTIFIER_TYPE_DAB_FREQUENCY,
+        IDENTIFIER_TYPE_DRMO_SERVICE_ID,
+        IDENTIFIER_TYPE_DRMO_FREQUENCY,
+        IDENTIFIER_TYPE_SXM_SERVICE_ID,
+        IDENTIFIER_TYPE_SXM_CHANNEL,
+        IDENTIFIER_TYPE_VENDOR1_PRIMARY,
+        IDENTIFIER_TYPE_VENDOR2_PRIMARY,
+        IDENTIFIER_TYPE_VENDOR3_PRIMARY,
+        IDENTIFIER_TYPE_VENDOR4_PRIMARY,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface IdentifierType {}
+
+    private final @ProgramType int mProgramType;
+    private final @NonNull Identifier mPrimaryId;
+    private final @NonNull Identifier[] mSecondaryIds;
+    private final @NonNull long[] mVendorIds;
+
+    /**
+     * Constructor for ProgramSelector.
+     *
+     * It's not desired to modify selector objects, so all its fields are initialized at creation.
+     *
+     * Identifier lists must not contain any nulls, but can itself be null to be interpreted
+     * as empty list at object creation.
+     *
+     * @param programType type of a radio technology.
+     * @param primaryId primary program identifier.
+     * @param secondaryIds list of secondary program identifiers.
+     * @param vendorIds list of vendor-specific program identifiers.
+     */
+    public ProgramSelector(@ProgramType int programType, @NonNull Identifier primaryId,
+            @Nullable Identifier[] secondaryIds, @Nullable long[] vendorIds) {
+        if (secondaryIds == null) secondaryIds = new Identifier[0];
+        if (vendorIds == null) vendorIds = new long[0];
+        if (Stream.of(secondaryIds).anyMatch(id -> id == null)) {
+            throw new IllegalArgumentException("secondaryIds list must not contain nulls");
+        }
+        mProgramType = programType;
+        mPrimaryId = Objects.requireNonNull(primaryId);
+        mSecondaryIds = secondaryIds;
+        mVendorIds = vendorIds;
+    }
+
+    /**
+     * Type of a radio technology.
+     *
+     * @returns program type.
+     */
+    public @ProgramType int getProgramType() {
+        return mProgramType;
+    }
+
+    /**
+     * Primary program identifier uniquely identifies a station and is used to
+     * determine equality between two ProgramSelectors.
+     *
+     * @returns primary identifier.
+     */
+    public @NonNull Identifier getPrimaryId() {
+        return mPrimaryId;
+    }
+
+    /**
+     * Secondary program identifier is not required for tuning, but may make it
+     * faster or more reliable.
+     *
+     * @returns secondary identifier list, must not be modified.
+     */
+    public @NonNull Identifier[] getSecondaryIds() {
+        return mSecondaryIds;
+    }
+
+    /**
+     * Looks up an identifier of a given type (either primary or secondary).
+     *
+     * If there are multiple identifiers if a given type, then first in order (where primary id is
+     * before any secondary) is selected.
+     *
+     * @param type type of identifier.
+     * @return identifier value, if found.
+     * @throws IllegalArgumentException, if not found.
+     */
+    public long getFirstId(@IdentifierType int type) {
+        if (mPrimaryId.getType() == type) return mPrimaryId.getValue();
+        for (Identifier id : mSecondaryIds) {
+            if (id.getType() == type) return id.getValue();
+        }
+        throw new IllegalArgumentException("Identifier " + type + " not found");
+    }
+
+    /**
+     * Looks up all identifier of a given type (either primary or secondary).
+     *
+     * Some identifiers may be provided multiple times, for example
+     * IDENTIFIER_TYPE_AMFM_FREQUENCY for FM Alternate Frequencies.
+     *
+     * @param type type of identifier.
+     * @return a list of identifiers, generated on each call. May be modified.
+     */
+    public @NonNull Identifier[] getAllIds(@IdentifierType int type) {
+        List<Identifier> out = new ArrayList<>();
+
+        if (mPrimaryId.getType() == type) out.add(mPrimaryId);
+        for (Identifier id : mSecondaryIds) {
+            if (id.getType() == type) out.add(id);
+        }
+
+        return out.toArray(new Identifier[out.size()]);
+    }
+
+    /**
+     * Vendor identifiers are passed as-is to the HAL implementation,
+     * preserving elements order.
+     *
+     * @return a array of vendor identifiers, must not be modified.
+     */
+    public @NonNull long[] getVendorIds() {
+        return mVendorIds;
+    }
+
+    /**
+     * Builds new ProgramSelector for AM/FM frequency.
+     *
+     * @param band the band.
+     * @param frequencyKhz the frequency in kHz.
+     * @return new ProgramSelector object representing given frequency.
+     * @throws IllegalArgumentException if provided frequency is out of bounds.
+     */
+    public static @NonNull ProgramSelector createAmFmSelector(
+            @RadioManager.Band int band, int frequencyKhz) {
+        return createAmFmSelector(band, frequencyKhz, 0);
+    }
+
+    /**
+     * Checks, if a given AM/FM frequency is roughly valid and in correct unit.
+     *
+     * It does not check the range precisely. In particular, it may be way off for certain regions.
+     * The main purpose is to avoid passing inproper units, ie. MHz instead of kHz.
+     *
+     * @param isAm true, if AM, false if FM.
+     * @param frequencyKhz the frequency in kHz.
+     * @return true, if the frequency is rougly valid.
+     */
+    private static boolean isValidAmFmFrequency(boolean isAm, int frequencyKhz) {
+        if (isAm) {
+            return frequencyKhz > 150 && frequencyKhz < 30000;
+        } else {
+            return frequencyKhz > 60000 && frequencyKhz < 110000;
+        }
+    }
+
+    /**
+     * Builds new ProgramSelector for AM/FM frequency.
+     *
+     * This method variant supports HD Radio subchannels, but it's undesirable to
+     * select them manually. Instead, the value should be retrieved from program list.
+     *
+     * @param band the band.
+     * @param frequencyKhz the frequency in kHz.
+     * @param subChannel 1-based HD Radio subchannel.
+     * @return new ProgramSelector object representing given frequency.
+     * @throws IllegalArgumentException if provided frequency is out of bounds,
+     *         or tried setting a subchannel for analog AM/FM.
+     */
+    public static @NonNull ProgramSelector createAmFmSelector(
+            @RadioManager.Band int band, int frequencyKhz, int subChannel) {
+        boolean isAm = (band == RadioManager.BAND_AM || band == RadioManager.BAND_AM_HD);
+        boolean isDigital = (band == RadioManager.BAND_AM_HD || band == RadioManager.BAND_FM_HD);
+        if (!isAm && !isDigital && band != RadioManager.BAND_FM) {
+            throw new IllegalArgumentException("Unknown band: " + band);
+        }
+        if (subChannel < 0 || subChannel > 8) {
+            throw new IllegalArgumentException("Invalid subchannel: " + subChannel);
+        }
+        if (subChannel > 0 && !isDigital) {
+            throw new IllegalArgumentException("Subchannels are not supported for non-HD radio");
+        }
+        if (!isValidAmFmFrequency(isAm, frequencyKhz)) {
+            throw new IllegalArgumentException("Provided value is not a valid AM/FM frequency");
+        }
+
+        // We can't use AM_HD or FM_HD, because we don't know HD station ID.
+        @ProgramType int programType = isAm ? PROGRAM_TYPE_AM : PROGRAM_TYPE_FM;
+        Identifier primary = new Identifier(IDENTIFIER_TYPE_AMFM_FREQUENCY, frequencyKhz);
+
+        Identifier[] secondary = null;
+        if (subChannel > 0) {
+            /* Stating sub channel for non-HD AM/FM does not give any guarantees,
+             * but we can't do much more without HD station ID.
+             *
+             * The legacy APIs had 1-based subChannels, while ProgramSelector is 0-based.
+             */
+            secondary = new Identifier[]{
+                    new Identifier(IDENTIFIER_TYPE_HD_SUBCHANNEL, subChannel - 1)};
+        }
+
+        return new ProgramSelector(programType, primary, secondary, null);
+    }
+
+    @Override
+    public String toString() {
+        StringBuilder sb = new StringBuilder("ProgramSelector(type=").append(mProgramType)
+                .append(", primary=").append(mPrimaryId);
+        if (mSecondaryIds.length > 0) sb.append(", secondary=").append(mSecondaryIds);
+        if (mVendorIds.length > 0) sb.append(", vendor=").append(mVendorIds);
+        sb.append(")");
+        return sb.toString();
+    }
+
+    @Override
+    public int hashCode() {
+        // secondaryIds and vendorIds are ignored for equality/hashing
+        return Objects.hash(mProgramType, mPrimaryId);
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj) return true;
+        if (!(obj instanceof ProgramSelector)) return false;
+        ProgramSelector other = (ProgramSelector) obj;
+        // secondaryIds and vendorIds are ignored for equality/hashing
+        return other.getProgramType() == mProgramType && mPrimaryId.equals(other.getPrimaryId());
+    }
+
+    private ProgramSelector(Parcel in) {
+        mProgramType = in.readInt();
+        mPrimaryId = in.readTypedObject(Identifier.CREATOR);
+        mSecondaryIds = in.createTypedArray(Identifier.CREATOR);
+        if (Stream.of(mSecondaryIds).anyMatch(id -> id == null)) {
+            throw new IllegalArgumentException("secondaryIds list must not contain nulls");
+        }
+        mVendorIds = in.createLongArray();
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeInt(mProgramType);
+        dest.writeTypedObject(mPrimaryId, 0);
+        dest.writeTypedArray(mSecondaryIds, 0);
+        dest.writeLongArray(mVendorIds);
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    public static final Parcelable.Creator<ProgramSelector> CREATOR =
+            new Parcelable.Creator<ProgramSelector>() {
+        public ProgramSelector createFromParcel(Parcel in) {
+            return new ProgramSelector(in);
+        }
+
+        public ProgramSelector[] newArray(int size) {
+            return new ProgramSelector[size];
+        }
+    };
+
+    /**
+     * A single program identifier component, eg. frequency or channel ID.
+     *
+     * The long value field holds the value in format described in comments for
+     * IdentifierType constants.
+     */
+    public static final class Identifier implements Parcelable {
+        private final @IdentifierType int mType;
+        private final long mValue;
+
+        public Identifier(@IdentifierType int type, long value) {
+            mType = type;
+            mValue = value;
+        }
+
+        /**
+         * Type of an identifier.
+         *
+         * @return type of an identifier.
+         */
+        public @IdentifierType int getType() {
+            return mType;
+        }
+
+        /**
+         * Value of an identifier.
+         *
+         * Its meaning depends on identifier type, ie. for IDENTIFIER_TYPE_AMFM_FREQUENCY type,
+         * the value is a frequency in kHz.
+         *
+         * The range of a value depends on its type; it does not always require the whole long
+         * range. Casting to necessary type (ie. int) without range checking is correct in front-end
+         * code - any range violations are either errors in the framework or in the
+         * HAL implementation. For example, IDENTIFIER_TYPE_AMFM_FREQUENCY always fits in int,
+         * as Integer.MAX_VALUE would mean 2.1THz.
+         *
+         * @return value of an identifier.
+         */
+        public long getValue() {
+            return mValue;
+        }
+
+        @Override
+        public String toString() {
+            return "Identifier(" + mType + ", " + mValue + ")";
+        }
+
+        @Override
+        public int hashCode() {
+            return Objects.hash(mType, mValue);
+        }
+
+        @Override
+        public boolean equals(Object obj) {
+            if (this == obj) return true;
+            if (!(obj instanceof Identifier)) return false;
+            Identifier other = (Identifier) obj;
+            return other.getType() == mType && other.getValue() == mValue;
+        }
+
+        private Identifier(Parcel in) {
+            mType = in.readInt();
+            mValue = in.readLong();
+        }
+
+        @Override
+        public void writeToParcel(Parcel dest, int flags) {
+            dest.writeInt(mType);
+            dest.writeLong(mValue);
+        }
+
+        @Override
+        public int describeContents() {
+            return 0;
+        }
+
+        public static final Parcelable.Creator<Identifier> CREATOR =
+                new Parcelable.Creator<Identifier>() {
+            public Identifier createFromParcel(Parcel in) {
+                return new Identifier(in);
+            }
+
+            public Identifier[] newArray(int size) {
+                return new Identifier[size];
+            }
+        };
+    }
+}
diff --git a/core/java/android/hardware/radio/RadioManager.java b/core/java/android/hardware/radio/RadioManager.java
index f697b89..6a59be0 100644
--- a/core/java/android/hardware/radio/RadioManager.java
+++ b/core/java/android/hardware/radio/RadioManager.java
@@ -16,6 +16,7 @@
 
 package android.hardware.radio;
 
+import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.SystemApi;
 import android.annotation.SystemService;
@@ -29,8 +30,12 @@
 import android.text.TextUtils;
 import android.util.Log;
 
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
 import java.util.Arrays;
 import java.util.List;
+import java.util.Set;
+import java.util.stream.Collectors;
 
 /**
  * The RadioManager class allows to control a broadcast radio tuner present on the device.
@@ -70,7 +75,7 @@
     /** Radio module class supporting Digital terrestrial radio */
     public static final int CLASS_DT = 2;
 
-    // keep in sync with radio_band_t in /system/core/incluse/system/radio.h
+    public static final int BAND_INVALID = -1;
     /** AM radio band (LW/MW/SW).
      * @see BandDescriptor */
     public static final int BAND_AM = 0;
@@ -83,6 +88,15 @@
     /** AM HD radio or DRM band.
      * @see BandDescriptor */
     public static final int BAND_AM_HD = 3;
+    @IntDef(prefix = { "BAND_" }, value = {
+        BAND_INVALID,
+        BAND_AM,
+        BAND_FM,
+        BAND_AM_HD,
+        BAND_FM_HD,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface Band {}
 
     // keep in sync with radio_region_t in /system/core/incluse/system/radio.h
     /** Africa, Europe.
@@ -120,11 +134,15 @@
         private final boolean mIsCaptureSupported;
         private final BandDescriptor[] mBands;
         private final boolean mIsBgScanSupported;
+        private final Set<Integer> mSupportedProgramTypes;
+        private final Set<Integer> mSupportedIdentifierTypes;
         private final String mVendorExension;
 
         ModuleProperties(int id, String serviceName, int classId, String implementor,
                 String product, String version, String serial, int numTuners, int numAudioSources,
                 boolean isCaptureSupported, BandDescriptor[] bands, boolean isBgScanSupported,
+                @ProgramSelector.ProgramType int[] supportedProgramTypes,
+                @ProgramSelector.IdentifierType int[] supportedIdentifierTypes,
                 String vendorExension) {
             mId = id;
             mServiceName = TextUtils.isEmpty(serviceName) ? "default" : serviceName;
@@ -138,9 +156,18 @@
             mIsCaptureSupported = isCaptureSupported;
             mBands = bands;
             mIsBgScanSupported = isBgScanSupported;
+            mSupportedProgramTypes = arrayToSet(supportedProgramTypes);
+            mSupportedIdentifierTypes = arrayToSet(supportedIdentifierTypes);
             mVendorExension = vendorExension;
         }
 
+        private static Set<Integer> arrayToSet(int[] arr) {
+            return Arrays.stream(arr).boxed().collect(Collectors.toSet());
+        }
+
+        private static int[] setToArray(Set<Integer> set) {
+            return set.stream().mapToInt(Integer::intValue).toArray();
+        }
 
         /** Unique module identifier provided by the native service.
          * For use with {@link #openTuner(int, BandConfig, boolean, Callback, Handler)}.
@@ -153,8 +180,6 @@
         /**
          * Module service (driver) name as registered with HIDL.
          * @return the module service name.
-         *
-         * @hide FutureFeature
          */
         public @NonNull String getServiceName() {
             return mServiceName;
@@ -229,14 +254,37 @@
          *
          * @return {@code true} if background scanning is supported (not necessary available
          * at a given time), {@code false} otherwise.
-         *
-         * @hide FutureFeature
          */
         public boolean isBackgroundScanningSupported() {
             return mIsBgScanSupported;
         }
 
         /**
+         * Checks, if a given program type is supported by this tuner.
+         *
+         * If a program type is supported by radio module, it means it can tune
+         * to ProgramSelector of a given type.
+         *
+         * @return {@code true} if a given program type is supported.
+         */
+        public boolean isProgramTypeSupported(@ProgramSelector.ProgramType int type) {
+            return mSupportedProgramTypes.contains(type);
+        }
+
+        /**
+         * Checks, if a given program identifier is supported by this tuner.
+         *
+         * If an identifier is supported by radio module, it means it can use it for
+         * tuning to ProgramSelector with either primary or secondary Identifier of
+         * a given type.
+         *
+         * @return {@code true} if a given program type is supported.
+         */
+        public boolean isProgramIdentifierSupported(@ProgramSelector.IdentifierType int type) {
+            return mSupportedIdentifierTypes.contains(type);
+        }
+
+        /**
          * Opaque vendor-specific string, passed from HAL without changes.
          * Format of this string can vary across vendors.
          *
@@ -245,8 +293,6 @@
          *
          * Client application MUST verify vendor/product name from the
          * ModuleProperties class before doing any interpretation of this value.
-         *
-         * @hide FutureFeature
          */
         public @NonNull String getVendorExension() {
             return mVendorExension == null ? "" : mVendorExension;
@@ -277,6 +323,8 @@
                 mBands[i] = (BandDescriptor) tmp[i];
             }
             mIsBgScanSupported = in.readInt() == 1;
+            mSupportedProgramTypes = arrayToSet(in.createIntArray());
+            mSupportedIdentifierTypes = arrayToSet(in.createIntArray());
             mVendorExension = in.readString();
         }
 
@@ -305,6 +353,8 @@
             dest.writeInt(mIsCaptureSupported ? 1 : 0);
             dest.writeParcelableArray(mBands, flags);
             dest.writeInt(mIsBgScanSupported ? 1 : 0);
+            dest.writeIntArray(setToArray(mSupportedProgramTypes));
+            dest.writeIntArray(setToArray(mSupportedIdentifierTypes));
             dest.writeString(mVendorExension);
         }
 
@@ -1261,11 +1311,12 @@
     public static class ProgramInfo implements Parcelable {
 
         // sourced from hardware/interfaces/broadcastradio/1.1/types.hal
-        private final static int FLAG_LIVE = 1 << 0;
-        private final static int FLAG_MUTED = 1 << 1;
+        private static final int FLAG_LIVE = 1 << 0;
+        private static final int FLAG_MUTED = 1 << 1;
+        private static final int FLAG_TRAFFIC_PROGRAM = 1 << 2;
+        private static final int FLAG_TRAFFIC_ANNOUNCEMENT = 1 << 3;
 
-        private final int mChannel;
-        private final int mSubChannel;
+        @NonNull private final ProgramSelector mSelector;
         private final boolean mTuned;
         private final boolean mStereo;
         private final boolean mDigital;
@@ -1274,11 +1325,10 @@
         private final RadioMetadata mMetadata;
         private final String mVendorExension;
 
-        ProgramInfo(int channel, int subChannel, boolean tuned, boolean stereo,
+        ProgramInfo(@NonNull ProgramSelector selector, boolean tuned, boolean stereo,
                 boolean digital, int signalStrength, RadioMetadata metadata, int flags,
                 String vendorExension) {
-            mChannel = channel;
-            mSubChannel = subChannel;
+            mSelector = selector;
             mTuned = tuned;
             mStereo = stereo;
             mDigital = digital;
@@ -1288,19 +1338,45 @@
             mVendorExension = vendorExension;
         }
 
+        /**
+         * Program selector, necessary for tuning to a program.
+         *
+         * @return the program selector.
+         */
+        public @NonNull ProgramSelector getSelector() {
+            return mSelector;
+        }
+
         /** Main channel expressed in units according to band type.
          * Currently all defined band types express channels as frequency in kHz
          * @return the program channel
+         * @deprecated Use {@link getSelector()} instead.
          */
+        @Deprecated
         public int getChannel() {
-            return mChannel;
+            try {
+                return (int) mSelector.getFirstId(ProgramSelector.IDENTIFIER_TYPE_AMFM_FREQUENCY);
+            } catch (IllegalArgumentException ex) {
+                Log.w(TAG, "Not an AM/FM program");
+                return 0;
+            }
         }
+
         /** Sub channel ID. E.g 1 for HD radio HD1
          * @return the program sub channel
+         * @deprecated Use {@link getSelector()} instead.
          */
+        @Deprecated
         public int getSubChannel() {
-            return mSubChannel;
+            try {
+                return (int) mSelector.getFirstId(
+                        ProgramSelector.IDENTIFIER_TYPE_HD_SUBCHANNEL) + 1;
+            } catch (IllegalArgumentException ex) {
+                // this is a normal behavior for analog AM/FM selector
+                return 0;
+            }
         }
+
         /** {@code true} if the tuner is currently tuned on a valid station
          * @return {@code true} if currently tuned, {@code false} otherwise.
          */
@@ -1324,8 +1400,6 @@
          * {@code true} if the program is currently playing live stream.
          * This may result in a slightly altered reception parameters,
          * usually targetted at reduced latency.
-         *
-         * @hide FutureFeature
          */
         public boolean isLive() {
             return (mFlags & FLAG_LIVE) != 0;
@@ -1336,13 +1410,27 @@
          * conditions or buffering. In this state volume knob MAY be disabled to
          * prevent user increasing volume too much.
          * It does NOT mean the user has muted audio.
-         *
-         * @hide FutureFeature
          */
         public boolean isMuted() {
             return (mFlags & FLAG_MUTED) != 0;
         }
 
+        /**
+         * {@code true} if radio station transmits traffic information
+         * regularily.
+         */
+        public boolean isTrafficProgram() {
+            return (mFlags & FLAG_TRAFFIC_PROGRAM) != 0;
+        }
+
+        /**
+         * {@code true} if radio station transmits traffic information
+         * at the very moment.
+         */
+        public boolean isTrafficAnnouncementActive() {
+            return (mFlags & FLAG_TRAFFIC_ANNOUNCEMENT) != 0;
+        }
+
         /** Signal strength indicator from 0 (no signal) to 100 (excellent)
          * @return the signal strength indication.
          */
@@ -1366,16 +1454,13 @@
          *
          * Client application MUST verify vendor/product name from the
          * ModuleProperties class before doing any interpretation of this value.
-         *
-         * @hide FutureFeature
          */
         public @NonNull String getVendorExension() {
             return mVendorExension == null ? "" : mVendorExension;
         }
 
         private ProgramInfo(Parcel in) {
-            mChannel = in.readInt();
-            mSubChannel = in.readInt();
+            mSelector = in.readParcelable(null);
             mTuned = in.readByte() == 1;
             mStereo = in.readByte() == 1;
             mDigital = in.readByte() == 1;
@@ -1402,8 +1487,7 @@
 
         @Override
         public void writeToParcel(Parcel dest, int flags) {
-            dest.writeInt(mChannel);
-            dest.writeInt(mSubChannel);
+            dest.writeParcelable(mSelector, 0);
             dest.writeByte((byte)(mTuned ? 1 : 0));
             dest.writeByte((byte)(mStereo ? 1 : 0));
             dest.writeByte((byte)(mDigital ? 1 : 0));
@@ -1425,7 +1509,7 @@
 
         @Override
         public String toString() {
-            return "ProgramInfo [mChannel=" + mChannel + ", mSubChannel=" + mSubChannel
+            return "ProgramInfo [mSelector=" + mSelector
                     + ", mTuned=" + mTuned + ", mStereo=" + mStereo + ", mDigital=" + mDigital
                     + ", mFlags=" + mFlags + ", mSignalStrength=" + mSignalStrength
                     + ((mMetadata == null) ? "" : (", mMetadata=" + mMetadata.toString()))
@@ -1436,8 +1520,7 @@
         public int hashCode() {
             final int prime = 31;
             int result = 1;
-            result = prime * result + mChannel;
-            result = prime * result + mSubChannel;
+            result = prime * result + mSelector.hashCode();
             result = prime * result + (mTuned ? 1 : 0);
             result = prime * result + (mStereo ? 1 : 0);
             result = prime * result + (mDigital ? 1 : 0);
@@ -1455,10 +1538,7 @@
             if (!(obj instanceof ProgramInfo))
                 return false;
             ProgramInfo other = (ProgramInfo) obj;
-            if (mChannel != other.getChannel())
-                return false;
-            if (mSubChannel != other.getSubChannel())
-                return false;
+            if (!mSelector.equals(other.getSelector())) return false;
             if (mTuned != other.isTuned())
                 return false;
             if (mStereo != other.isStereo())
@@ -1542,7 +1622,7 @@
         Log.d(TAG, "Opening tuner " + moduleId + "...");
 
         ITuner tuner;
-        ITunerCallback halCallback = new TunerCallbackAdapter(callback, handler);
+        TunerCallbackAdapter halCallback = new TunerCallbackAdapter(callback, handler);
         try {
             tuner = mService.openTuner(moduleId, config, withAudio, halCallback);
         } catch (RemoteException e) {
@@ -1553,7 +1633,8 @@
             Log.e(TAG, "Failed to open tuner");
             return null;
         }
-        return new TunerAdapter(tuner);
+        halCallback.attachTuner(tuner);
+        return new TunerAdapter(tuner, config != null ? config.getType() : BAND_INVALID);
     }
 
     @NonNull private final Context mContext;
diff --git a/core/java/android/hardware/radio/RadioTuner.java b/core/java/android/hardware/radio/RadioTuner.java
index 78d7b61..6637ed2 100644
--- a/core/java/android/hardware/radio/RadioTuner.java
+++ b/core/java/android/hardware/radio/RadioTuner.java
@@ -170,10 +170,22 @@
      *  <li>{@link RadioManager#STATUS_DEAD_OBJECT} if the binder transaction to the native
      *  service fails, </li>
      * </ul>
+     * @deprecated Use {@link tune(ProgramSelector)} instead.
      */
+    @Deprecated
     public abstract int tune(int channel, int subChannel);
 
     /**
+     * Tune to a program.
+     *
+     * The operation is asynchronous and {@link Callback} onProgramInfoChanged() will be called
+     * when tune completes or onError() when cancelled or on timeout.
+     *
+     * @thows IllegalArgumentException if the provided selector is invalid
+     */
+    public abstract void tune(@NonNull ProgramSelector selector);
+
+    /**
      * Cancel a pending scan or tune operation.
      * If an operation is pending, {@link Callback} onError() will be called with
      * {@link #ERROR_CANCELLED}.
@@ -191,6 +203,17 @@
     public abstract int cancel();
 
     /**
+     * Cancels traffic or emergency announcement.
+     *
+     * If there was no announcement to cancel, no action is taken.
+     *
+     * There is a race condition between calling cancelAnnouncement and the actual announcement
+     * being finished, so onTrafficAnnouncement / onEmergencyAnnouncement callback should be
+     * tracked with proper locking.
+     */
+    public abstract void cancelAnnouncement();
+
+    /**
      * Get current station information.
      * @param info a ProgramInfo array of lengh 1 where the information is returned.
      * @return
@@ -219,7 +242,6 @@
      * is unavailable; ie. temporarily due to ongoing foreground playback in single-tuner device
      * or permanently if the feature is not supported
      * (see ModuleProperties#isBackgroundScanningSupported()).
-     * @hide FutureFeature
      */
     public abstract boolean startBackgroundScan();
 
@@ -234,7 +256,6 @@
      * @throws IllegalStateException if the scan is in progress or has not been started,
      *         startBackgroundScan() call may fix it.
      * @throws IllegalArgumentException if the filter argument is not valid.
-     * @hide FutureFeature
      */
     public abstract @NonNull List<RadioManager.ProgramInfo> getProgramList(@Nullable String filter);
 
@@ -244,7 +265,6 @@
      * @throws IllegalStateException if the switch is not supported at current
      *         configuration.
      * @return {@code true} if analog is forced, {@code false} otherwise.
-     * @hide FutureFeature
      */
     public abstract boolean isAnalogForced();
 
@@ -260,7 +280,6 @@
      * @param isForced {@code true} to force analog, {@code false} for a default behaviour.
      * @throws IllegalStateException if the switch is not supported at current
      *         configuration.
-     * @hide FutureFeature
      */
     public abstract void setAnalogForced(boolean isForced);
 
@@ -298,15 +317,9 @@
     public static final  int ERROR_SCAN_TIMEOUT = 3;
     /** The requested configuration could not be applied */
     public static final  int ERROR_CONFIG = 4;
-    /**
-     * Background scan was interrupted due to hardware becoming temporarily unavailable.
-     * @hide FutureFeature
-     */
+    /** Background scan was interrupted due to hardware becoming temporarily unavailable. */
     public static final int ERROR_BACKGROUND_SCAN_UNAVAILABLE = 5;
-    /**
-     * Background scan failed due to other error, ie. HW failure.
-     * @hide FutureFeature
-     */
+    /** Background scan failed due to other error, ie. HW failure. */
     public static final int ERROR_BACKGROUND_SCAN_FAILED = 6;
 
     /**
@@ -328,20 +341,24 @@
          * or {@link RadioTuner#setConfiguration(RadioManager.BandConfig)}
          */
         public void onConfigurationChanged(RadioManager.BandConfig config) {}
+
         /**
-         * onProgramInfoChanged() is called upon successful completion of
-         * {@link RadioTuner#step(int, boolean)}, {@link RadioTuner#scan(int, boolean)},
-         * {@link RadioTuner#tune(int, int)} or when a switching to alternate frequency occurs.
-         * Note that if metadata only are updated,  {@link #onMetadataChanged(RadioMetadata)} will
-         * be called.
+         * Called when program info (including metadata) for the current program has changed.
+         *
+         * It happens either upon successful completion of {@link RadioTuner#step(int, boolean)},
+         * {@link RadioTuner#scan(int, boolean)}, {@link RadioTuner#tune(int, int)}; when
+         * a switching to alternate frequency occurs; or when metadata is updated.
          */
         public void onProgramInfoChanged(RadioManager.ProgramInfo info) {}
+
         /**
-         * onMetadataChanged() is called when new meta data are received on current program.
-         * Meta data are also received in {@link RadioManager.ProgramInfo} when
-         *  {@link #onProgramInfoChanged(RadioManager.ProgramInfo)} is called.
+         * Called when metadata is updated for the current program.
+         *
+         * @deprecated Use {@link #onProgramInfoChanged(RadioManager.ProgramInfo)} instead.
          */
+        @Deprecated
         public void onMetadataChanged(RadioMetadata metadata) {}
+
         /**
          * onTrafficAnnouncement() is called when a traffic announcement starts and stops.
          */
@@ -373,23 +390,18 @@
          *
          * @param isAvailable true, if the tuner turned temporarily background-
          *                    capable, false in the other case.
-         * @hide FutureFeature
          */
         public void onBackgroundScanAvailabilityChange(boolean isAvailable) {}
 
         /**
          * Called when a background scan completes successfully.
-         *
-         * @hide FutureFeature
          */
         public void onBackgroundScanComplete() {}
 
         /**
          * Called when available program list changed.
          *
-         * Use getProgramList() to get the actual list.
-         *
-         * @hide FutureFeature
+         * Use {@link RadioTuner#getProgramList(String)} to get an actual list.
          */
         public void onProgramListChanged() {}
     }
diff --git a/core/java/android/hardware/radio/TunerAdapter.java b/core/java/android/hardware/radio/TunerAdapter.java
index dbaca62..3bc0ac3 100644
--- a/core/java/android/hardware/radio/TunerAdapter.java
+++ b/core/java/android/hardware/radio/TunerAdapter.java
@@ -32,11 +32,14 @@
     @NonNull private final ITuner mTuner;
     private boolean mIsClosed = false;
 
-    TunerAdapter(ITuner tuner) {
+    private @RadioManager.Band int mBand;
+
+    TunerAdapter(ITuner tuner, @RadioManager.Band int band) {
         if (tuner == null) {
             throw new NullPointerException();
         }
         mTuner = tuner;
+        mBand = band;
     }
 
     @Override
@@ -59,6 +62,7 @@
     public int setConfiguration(RadioManager.BandConfig config) {
         try {
             mTuner.setConfiguration(config);
+            mBand = config.getType();
             return RadioManager.STATUS_OK;
         } catch (IllegalArgumentException e) {
             Log.e(TAG, "Can't set configuration", e);
@@ -138,7 +142,7 @@
     @Override
     public int tune(int channel, int subChannel) {
         try {
-            mTuner.tune(channel, subChannel);
+            mTuner.tune(ProgramSelector.createAmFmSelector(mBand, channel, subChannel));
         } catch (IllegalStateException e) {
             Log.e(TAG, "Can't tune", e);
             return RadioManager.STATUS_INVALID_OPERATION;
@@ -153,6 +157,15 @@
     }
 
     @Override
+    public void tune(@NonNull ProgramSelector selector) {
+        try {
+            mTuner.tune(selector);
+        } catch (RemoteException e) {
+            throw new RuntimeException("service died", e);
+        }
+    }
+
+    @Override
     public int cancel() {
         try {
             mTuner.cancel();
@@ -167,6 +180,15 @@
     }
 
     @Override
+    public void cancelAnnouncement() {
+        try {
+            mTuner.cancelAnnouncement();
+        } catch (RemoteException e) {
+            throw new RuntimeException("service died", e);
+        }
+    }
+
+    @Override
     public int getProgramInformation(RadioManager.ProgramInfo[] info) {
         if (info == null || info.length != 1) {
             throw new IllegalArgumentException("The argument must be an array of length 1");
diff --git a/core/java/android/hardware/radio/TunerCallbackAdapter.java b/core/java/android/hardware/radio/TunerCallbackAdapter.java
index 155ffa7..8c3826c 100644
--- a/core/java/android/hardware/radio/TunerCallbackAdapter.java
+++ b/core/java/android/hardware/radio/TunerCallbackAdapter.java
@@ -20,13 +20,21 @@
 import android.annotation.Nullable;
 import android.os.Handler;
 import android.os.Looper;
+import android.os.RemoteException;
+import android.util.Log;
 
 /**
  * Implements the ITunerCallback interface by forwarding calls to RadioTuner.Callback.
  */
 class TunerCallbackAdapter extends ITunerCallback.Stub {
+    private static final String TAG = "radio.TunerCallbackAdapter";
+
     @NonNull private final RadioTuner.Callback mCallback;
     @NonNull private final Handler mHandler;
+    private final Object mLock = new Object();
+
+    @Nullable private ITuner mTuner;
+    boolean mPendingProgramInfoChanged = false;
 
     TunerCallbackAdapter(@NonNull RadioTuner.Callback callback, @Nullable Handler handler) {
         mCallback = callback;
@@ -37,6 +45,14 @@
         }
     }
 
+    public void attachTuner(@NonNull ITuner tuner) {
+        synchronized (mLock) {
+            if (mTuner != null) throw new IllegalStateException();
+            mTuner = tuner;
+            if (mPendingProgramInfoChanged) onProgramInfoChanged();
+        }
+    }
+
     @Override
     public void onError(int status) {
         mHandler.post(() -> mCallback.onError(status));
@@ -48,13 +64,28 @@
     }
 
     @Override
-    public void onProgramInfoChanged(RadioManager.ProgramInfo info) {
-        mHandler.post(() -> mCallback.onProgramInfoChanged(info));
-    }
+    public void onProgramInfoChanged() {
+        synchronized (mLock) {
+            if (mTuner == null) {
+                mPendingProgramInfoChanged = true;
+                return;
+            }
+        }
 
-    @Override
-    public void onMetadataChanged(RadioMetadata metadata) {
-        mHandler.post(() -> mCallback.onMetadataChanged(metadata));
+        RadioManager.ProgramInfo info;
+        try {
+            info = mTuner.getProgramInformation();
+        } catch (RemoteException e) {
+            Log.e(TAG, "service died", e);
+            return;
+        }
+
+        mHandler.post(() -> {
+            mCallback.onProgramInfoChanged(info);
+
+            RadioMetadata metadata = info.getMetadata();
+            if (metadata != null) mCallback.onMetadataChanged(metadata);
+        });
     }
 
     @Override
diff --git a/core/java/android/hardware/usb/UsbDeviceConnection.java b/core/java/android/hardware/usb/UsbDeviceConnection.java
index 9b5d0d3..5b15c0d 100644
--- a/core/java/android/hardware/usb/UsbDeviceConnection.java
+++ b/core/java/android/hardware/usb/UsbDeviceConnection.java
@@ -18,6 +18,7 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.annotation.SuppressLint;
 import android.annotation.SystemApi;
 import android.content.Context;
 import android.os.Build;
@@ -257,6 +258,7 @@
      * @hide
      */
     @SystemApi
+    @SuppressLint("Doclava125")
     public boolean resetDevice() {
         return native_reset_device();
     }
diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java
index 2979cd8..48123fe 100644
--- a/core/java/android/net/ConnectivityManager.java
+++ b/core/java/android/net/ConnectivityManager.java
@@ -835,6 +835,29 @@
     }
 
     /**
+     * Checks if a VPN app supports always-on mode.
+     *
+     * In order to support the always-on feature, an app has to
+     * <ul>
+     *     <li>target {@link VERSION_CODES#N API 24} or above, and
+     *     <li>not opt out through the {@link VpnService#METADATA_SUPPORTS_ALWAYS_ON} meta-data
+     *         field.
+     * </ul>
+     *
+     * @param userId The identifier of the user for whom the VPN app is installed.
+     * @param vpnPackage The canonical package name of the VPN app.
+     * @return {@code true} if and only if the VPN app exists and supports always-on mode.
+     * @hide
+     */
+    public boolean isAlwaysOnVpnPackageSupportedForUser(int userId, @Nullable String vpnPackage) {
+        try {
+            return mService.isAlwaysOnVpnPackageSupported(userId, vpnPackage);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
      * Configures an always-on VPN connection through a specific application.
      * This connection is automatically granted and persisted after a reboot.
      *
@@ -1720,14 +1743,8 @@
         // ignored
     }
 
-    /**
-     * Return quota status for the current active network, or {@code null} if no
-     * network is active. Quota status can change rapidly, so these values
-     * shouldn't be cached.
-     *
-     * @hide
-     */
-    @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE)
+    /** {@hide} */
+    @Deprecated
     public NetworkQuotaInfo getActiveNetworkQuotaInfo() {
         try {
             return mService.getActiveNetworkQuotaInfo();
diff --git a/core/java/android/net/IConnectivityManager.aidl b/core/java/android/net/IConnectivityManager.aidl
index 14cee36..a6fe738 100644
--- a/core/java/android/net/IConnectivityManager.aidl
+++ b/core/java/android/net/IConnectivityManager.aidl
@@ -123,6 +123,7 @@
     VpnInfo[] getAllVpnInfo();
 
     boolean updateLockdownVpn();
+    boolean isAlwaysOnVpnPackageSupported(int userId, String packageName);
     boolean setAlwaysOnVpnPackage(int userId, String packageName, boolean lockdown);
     String getAlwaysOnVpnPackage(int userId);
 
diff --git a/core/java/android/net/INetworkPolicyManager.aidl b/core/java/android/net/INetworkPolicyManager.aidl
index 63bbd96..7b948a7 100644
--- a/core/java/android/net/INetworkPolicyManager.aidl
+++ b/core/java/android/net/INetworkPolicyManager.aidl
@@ -21,6 +21,7 @@
 import android.net.NetworkQuotaInfo;
 import android.net.NetworkState;
 import android.net.NetworkTemplate;
+import android.telephony.SubscriptionPlan;
 
 /**
  * Interface that creates and modifies network policy rules.
@@ -63,9 +64,14 @@
     int getRestrictBackgroundByCaller();
 
     void setDeviceIdleMode(boolean enabled);
+    void setWifiMeteredOverride(String networkId, int meteredOverride);
 
     NetworkQuotaInfo getNetworkQuotaInfo(in NetworkState state);
-    boolean isNetworkMetered(in NetworkState state);
+
+    SubscriptionPlan[] getSubscriptionPlans(int subId, String callingPackage);
+    void setSubscriptionPlans(int subId, in SubscriptionPlan[] plans, String callingPackage);
+
+    String getSubscriptionPlanOwner(int subId);
 
     void factoryReset(String subscriber);
 }
diff --git a/core/java/android/net/ITetheringStatsProvider.aidl b/core/java/android/net/ITetheringStatsProvider.aidl
new file mode 100644
index 0000000..769086d
--- /dev/null
+++ b/core/java/android/net/ITetheringStatsProvider.aidl
@@ -0,0 +1,33 @@
+/*
+ * 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.net;
+
+import android.net.NetworkStats;
+
+/**
+ * Interface that allows NetworkManagementService to query for tethering statistics.
+ *
+ * TODO: this does not really need to be an interface since Tethering runs in the same process
+ * as NetworkManagementService. Consider refactoring Tethering to use direct access to
+ * NetworkManagementService instead of using INetworkManagementService, and then deleting this
+ * interface.
+ *
+ * @hide
+ */
+interface ITetheringStatsProvider {
+    NetworkStats getTetherStats();
+}
diff --git a/core/java/android/net/LinkAddress.java b/core/java/android/net/LinkAddress.java
index 6e74f14..62de991 100644
--- a/core/java/android/net/LinkAddress.java
+++ b/core/java/android/net/LinkAddress.java
@@ -16,6 +16,15 @@
 
 package android.net;
 
+import static android.system.OsConstants.IFA_F_DADFAILED;
+import static android.system.OsConstants.IFA_F_DEPRECATED;
+import static android.system.OsConstants.IFA_F_OPTIMISTIC;
+import static android.system.OsConstants.IFA_F_TENTATIVE;
+import static android.system.OsConstants.RT_SCOPE_HOST;
+import static android.system.OsConstants.RT_SCOPE_LINK;
+import static android.system.OsConstants.RT_SCOPE_SITE;
+import static android.system.OsConstants.RT_SCOPE_UNIVERSE;
+
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.util.Pair;
@@ -26,15 +35,6 @@
 import java.net.InterfaceAddress;
 import java.net.UnknownHostException;
 
-import static android.system.OsConstants.IFA_F_DADFAILED;
-import static android.system.OsConstants.IFA_F_DEPRECATED;
-import static android.system.OsConstants.IFA_F_OPTIMISTIC;
-import static android.system.OsConstants.IFA_F_TENTATIVE;
-import static android.system.OsConstants.RT_SCOPE_HOST;
-import static android.system.OsConstants.RT_SCOPE_LINK;
-import static android.system.OsConstants.RT_SCOPE_SITE;
-import static android.system.OsConstants.RT_SCOPE_UNIVERSE;
-
 /**
  * Identifies an IP address on a network link.
  *
@@ -101,7 +101,7 @@
      * Per RFC 4193 section 8, fc00::/7 identifies these addresses.
      */
     private boolean isIPv6ULA() {
-        if (address != null && address instanceof Inet6Address) {
+        if (address instanceof Inet6Address) {
             byte[] bytes = address.getAddress();
             return ((bytes[0] & (byte)0xfe) == (byte)0xfc);
         }
diff --git a/core/java/android/net/NetworkCapabilities.java b/core/java/android/net/NetworkCapabilities.java
index 76646b8..305cf76 100644
--- a/core/java/android/net/NetworkCapabilities.java
+++ b/core/java/android/net/NetworkCapabilities.java
@@ -421,7 +421,6 @@
 
     /**
      * Indicates this network uses a LoWPAN transport.
-     * @hide
      */
     public static final int TRANSPORT_LOWPAN = 6;
 
diff --git a/core/java/android/net/NetworkInfo.java b/core/java/android/net/NetworkInfo.java
index 84c32be..818aa21 100644
--- a/core/java/android/net/NetworkInfo.java
+++ b/core/java/android/net/NetworkInfo.java
@@ -16,8 +16,8 @@
 
 package android.net;
 
-import android.os.Parcelable;
 import android.os.Parcel;
+import android.os.Parcelable;
 
 import com.android.internal.annotations.VisibleForTesting;
 
@@ -121,7 +121,6 @@
     private boolean mIsFailover;
     private boolean mIsAvailable;
     private boolean mIsRoaming;
-    private boolean mIsMetered;
 
     /**
      * @hide
@@ -154,7 +153,6 @@
                 mIsFailover = source.mIsFailover;
                 mIsAvailable = source.mIsAvailable;
                 mIsRoaming = source.mIsRoaming;
-                mIsMetered = source.mIsMetered;
             }
         }
     }
@@ -327,31 +325,6 @@
     }
 
     /**
-     * Returns if this network is metered. A network is classified as metered
-     * when the user is sensitive to heavy data usage on that connection due to
-     * monetary costs, data limitations or battery/performance issues. You
-     * should check this before doing large data transfers, and warn the user or
-     * delay the operation until another network is available.
-     *
-     * @return {@code true} if large transfers should be avoided, otherwise
-     *         {@code false}.
-     * @hide
-     */
-    public boolean isMetered() {
-        synchronized (this) {
-            return mIsMetered;
-        }
-    }
-
-    /** {@hide} */
-    @VisibleForTesting
-    public void setMetered(boolean isMetered) {
-        synchronized (this) {
-            mIsMetered = isMetered;
-        }
-    }
-
-    /**
      * Reports the current coarse-grained state of the network.
      * @return the coarse-grained state
      */
@@ -434,7 +407,6 @@
             append(", failover: ").append(mIsFailover).
             append(", available: ").append(mIsAvailable).
             append(", roaming: ").append(mIsRoaming).
-            append(", metered: ").append(mIsMetered).
             append("]");
             return builder.toString();
         }
@@ -457,7 +429,6 @@
             dest.writeInt(mIsFailover ? 1 : 0);
             dest.writeInt(mIsAvailable ? 1 : 0);
             dest.writeInt(mIsRoaming ? 1 : 0);
-            dest.writeInt(mIsMetered ? 1 : 0);
             dest.writeString(mReason);
             dest.writeString(mExtraInfo);
         }
@@ -476,7 +447,6 @@
             netInfo.mIsFailover = in.readInt() != 0;
             netInfo.mIsAvailable = in.readInt() != 0;
             netInfo.mIsRoaming = in.readInt() != 0;
-            netInfo.mIsMetered = in.readInt() != 0;
             netInfo.mReason = in.readString();
             netInfo.mExtraInfo = in.readString();
             return netInfo;
diff --git a/core/java/android/net/NetworkPolicy.java b/core/java/android/net/NetworkPolicy.java
index 9870e7b..edf9a28 100644
--- a/core/java/android/net/NetworkPolicy.java
+++ b/core/java/android/net/NetworkPolicy.java
@@ -46,17 +46,20 @@
     public static final long SNOOZE_NEVER = -1;
 
     public NetworkTemplate template;
-    public int cycleDay;
-    public String cycleTimezone;
-    public long warningBytes;
-    public long limitBytes;
-    public long lastWarningSnooze;
-    public long lastLimitSnooze;
-    public boolean metered;
-    public boolean inferred;
+    @Deprecated public int cycleDay = CYCLE_NONE;
+    @Deprecated public String cycleTimezone = "UTC";
+    public long warningBytes = WARNING_DISABLED;
+    public long limitBytes = LIMIT_DISABLED;
+    public long lastWarningSnooze = SNOOZE_NEVER;
+    public long lastLimitSnooze = SNOOZE_NEVER;
+    @Deprecated public boolean metered = true;
+    public boolean inferred = false;
 
     private static final long DEFAULT_MTU = 1500;
 
+    public NetworkPolicy() {
+    }
+
     @Deprecated
     public NetworkPolicy(NetworkTemplate template, int cycleDay, String cycleTimezone,
             long warningBytes, long limitBytes, boolean metered) {
diff --git a/core/java/android/net/NetworkPolicyManager.java b/core/java/android/net/NetworkPolicyManager.java
index 4d94a55..3fe9b0d 100644
--- a/core/java/android/net/NetworkPolicyManager.java
+++ b/core/java/android/net/NetworkPolicyManager.java
@@ -17,7 +17,6 @@
 package android.net;
 
 import static android.content.pm.PackageManager.GET_SIGNATURES;
-import static android.net.NetworkPolicy.CYCLE_NONE;
 
 import android.annotation.SystemService;
 import android.app.ActivityManager;
@@ -26,15 +25,19 @@
 import android.content.pm.PackageManager;
 import android.content.pm.PackageManager.NameNotFoundException;
 import android.content.pm.Signature;
+import android.net.wifi.WifiConfiguration;
+import android.net.wifi.WifiInfo;
 import android.os.RemoteException;
 import android.os.UserHandle;
+import android.telephony.SubscriptionPlan;
 import android.util.DebugUtils;
+import android.util.Pair;
 
 import com.google.android.collect.Sets;
 
-import java.util.Calendar;
+import java.time.ZonedDateTime;
 import java.util.HashSet;
-import java.util.TimeZone;
+import java.util.Iterator;
 
 /**
  * Manager for creating and modifying network policy rules.
@@ -249,73 +252,9 @@
         }
     }
 
-    /**
-     * Compute the last cycle boundary for the given {@link NetworkPolicy}. For
-     * example, if cycle day is 20th, and today is June 15th, it will return May
-     * 20th. When cycle day doesn't exist in current month, it snaps to the 1st
-     * of following month.
-     *
-     * @hide
-     */
-    public static long computeLastCycleBoundary(long currentTime, NetworkPolicy policy) {
-        if (policy.cycleDay == CYCLE_NONE) {
-            throw new IllegalArgumentException("Unable to compute boundary without cycleDay");
-        }
-
-        final Calendar cal = Calendar.getInstance(TimeZone.getTimeZone(policy.cycleTimezone));
-        cal.setTimeInMillis(currentTime);
-        snapToCycleDay(cal, policy.cycleDay);
-
-        if (cal.getTimeInMillis() >= currentTime) {
-            // Cycle boundary is beyond now, use last cycle boundary
-            cal.set(Calendar.DAY_OF_MONTH, 1);
-            cal.add(Calendar.MONTH, -1);
-            snapToCycleDay(cal, policy.cycleDay);
-        }
-
-        return cal.getTimeInMillis();
-    }
-
     /** {@hide} */
-    public static long computeNextCycleBoundary(long currentTime, NetworkPolicy policy) {
-        if (policy.cycleDay == CYCLE_NONE) {
-            throw new IllegalArgumentException("Unable to compute boundary without cycleDay");
-        }
-
-        final Calendar cal = Calendar.getInstance(TimeZone.getTimeZone(policy.cycleTimezone));
-        cal.setTimeInMillis(currentTime);
-        snapToCycleDay(cal, policy.cycleDay);
-
-        if (cal.getTimeInMillis() <= currentTime) {
-            // Cycle boundary is before now, use next cycle boundary
-            cal.set(Calendar.DAY_OF_MONTH, 1);
-            cal.add(Calendar.MONTH, 1);
-            snapToCycleDay(cal, policy.cycleDay);
-        }
-
-        return cal.getTimeInMillis();
-    }
-
-    /**
-     * Snap to the cycle day for the current month given; when cycle day doesn't
-     * exist, it snaps to last second of current month.
-     *
-     * @hide
-     */
-    public static void snapToCycleDay(Calendar cal, int cycleDay) {
-        cal.set(Calendar.HOUR_OF_DAY, 0);
-        cal.set(Calendar.MINUTE, 0);
-        cal.set(Calendar.SECOND, 0);
-        if (cycleDay > cal.getActualMaximum(Calendar.DAY_OF_MONTH)) {
-            cal.add(Calendar.MONTH, 1);
-            cal.set(Calendar.DAY_OF_MONTH, 1);
-            cal.set(Calendar.HOUR_OF_DAY, 0);
-            cal.set(Calendar.MINUTE, 0);
-            cal.set(Calendar.SECOND, 0);
-            cal.add(Calendar.SECOND, -1);
-        } else {
-            cal.set(Calendar.DAY_OF_MONTH, cycleDay);
-        }
+    public static Iterator<Pair<ZonedDateTime, ZonedDateTime>> cycleIterator(NetworkPolicy policy) {
+        return SubscriptionPlan.convert(policy).cycleIterator();
     }
 
     /**
@@ -400,4 +339,13 @@
     public static boolean isProcStateAllowedWhileOnRestrictBackground(int procState) {
         return procState <= ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE;
     }
+
+    public static String resolveNetworkId(WifiConfiguration config) {
+        return WifiInfo.removeDoubleQuotes(config.isPasspoint()
+                ? config.providerFriendlyName : config.SSID);
+    }
+
+    public static String resolveNetworkId(String ssid) {
+        return WifiInfo.removeDoubleQuotes(ssid);
+    }
 }
diff --git a/core/java/android/net/NetworkQuotaInfo.java b/core/java/android/net/NetworkQuotaInfo.java
index 1725ed7..b95f1d9 100644
--- a/core/java/android/net/NetworkQuotaInfo.java
+++ b/core/java/android/net/NetworkQuotaInfo.java
@@ -20,41 +20,32 @@
 import android.os.Parcelable;
 
 /**
- * Information about quota status on a specific network.
- *
+ * @deprecated nobody should be using this, but keep it around returning stub
+ *             values to prevent app crashes.
  * @hide
  */
+@Deprecated
 public class NetworkQuotaInfo implements Parcelable {
-    private final long mEstimatedBytes;
-    private final long mSoftLimitBytes;
-    private final long mHardLimitBytes;
-
     public static final long NO_LIMIT = -1;
 
     /** {@hide} */
-    public NetworkQuotaInfo(long estimatedBytes, long softLimitBytes, long hardLimitBytes) {
-        mEstimatedBytes = estimatedBytes;
-        mSoftLimitBytes = softLimitBytes;
-        mHardLimitBytes = hardLimitBytes;
+    public NetworkQuotaInfo() {
     }
 
     /** {@hide} */
     public NetworkQuotaInfo(Parcel in) {
-        mEstimatedBytes = in.readLong();
-        mSoftLimitBytes = in.readLong();
-        mHardLimitBytes = in.readLong();
     }
 
     public long getEstimatedBytes() {
-        return mEstimatedBytes;
+        return 0;
     }
 
     public long getSoftLimitBytes() {
-        return mSoftLimitBytes;
+        return NO_LIMIT;
     }
 
     public long getHardLimitBytes() {
-        return mHardLimitBytes;
+        return NO_LIMIT;
     }
 
     @Override
@@ -64,9 +55,6 @@
 
     @Override
     public void writeToParcel(Parcel out, int flags) {
-        out.writeLong(mEstimatedBytes);
-        out.writeLong(mSoftLimitBytes);
-        out.writeLong(mHardLimitBytes);
     }
 
     public static final Creator<NetworkQuotaInfo> CREATOR = new Creator<NetworkQuotaInfo>() {
diff --git a/core/java/android/net/VpnService.java b/core/java/android/net/VpnService.java
index 2d9860c..7fb0c47 100644
--- a/core/java/android/net/VpnService.java
+++ b/core/java/android/net/VpnService.java
@@ -28,8 +28,6 @@
 import android.content.Intent;
 import android.content.pm.IPackageManager;
 import android.content.pm.PackageManager;
-import android.net.Network;
-import android.net.NetworkUtils;
 import android.os.Binder;
 import android.os.IBinder;
 import android.os.Parcel;
@@ -99,7 +97,7 @@
  *       shut down the tunnel gracefully.</li>
  * </ol>
  *
- * <p>Services extended this class need to be declared with appropriate
+ * <p>Services extending this class need to be declared with an appropriate
  * permission and intent filter. Their access must be secured by
  * {@link android.Manifest.permission#BIND_VPN_SERVICE} permission, and
  * their intent filter must match {@link #SERVICE_INTERFACE} action. Here
@@ -112,6 +110,13 @@
  *     &lt;/intent-filter&gt;
  * &lt;/service&gt;</pre>
  *
+ * <p> The Android system starts a VPN in the background by calling
+ * {@link android.content.Context#startService startService()}. In Android 8.0
+ * (API level 26) and higher, the system places VPN apps on the temporary
+ * whitelist for a short period so the app can start in the background. The VPN
+ * app must promote itself to the foreground after it's launched or the system
+ * will shut down the app.
+ *
  * @see Builder
  */
 public class VpnService extends Service {
@@ -124,6 +129,35 @@
     public static final String SERVICE_INTERFACE = VpnConfig.SERVICE_INTERFACE;
 
     /**
+     * Key for boolean meta-data field indicating whether this VpnService supports always-on mode.
+     *
+     * <p>For a VPN app targeting {@link android.os.Build.VERSION_CODES#N API 24} or above, Android
+     * provides users with the ability to set it as always-on, so that VPN connection is
+     * persisted after device reboot and app upgrade. Always-on VPN can also be enabled by device
+     * owner and profile owner apps through
+     * {@link android.app.admin.DevicePolicyManager#setAlwaysOnVpnPackage}.
+     *
+     * <p>VPN apps not supporting this feature should opt out by adding this meta-data field to the
+     * {@code VpnService} component of {@code AndroidManifest.xml}. In case there is more than one
+     * {@code VpnService} component defined in {@code AndroidManifest.xml}, opting out any one of
+     * them will opt out the entire app. For example,
+     * <pre> {@code
+     * <service android:name=".ExampleVpnService"
+     *         android:permission="android.permission.BIND_VPN_SERVICE">
+     *     <intent-filter>
+     *         <action android:name="android.net.VpnService"/>
+     *     </intent-filter>
+     *     <meta-data android:name="android.net.VpnService.SUPPORTS_ALWAYS_ON"
+     *             android:value=false/>
+     * </service>
+     * } </pre>
+     *
+     * <p>This meta-data field defaults to {@code true} if absent.
+     */
+    public static final String METADATA_SUPPORTS_ALWAYS_ON =
+            "android.net.VpnService.SUPPORTS_ALWAYS_ON";
+
+    /**
      * Use IConnectivityManager since those methods are hidden and not
      * available in ConnectivityManager.
      */
diff --git a/core/java/android/net/metrics/IpManagerEvent.java b/core/java/android/net/metrics/IpManagerEvent.java
index f5aea73..a94b928 100644
--- a/core/java/android/net/metrics/IpManagerEvent.java
+++ b/core/java/android/net/metrics/IpManagerEvent.java
@@ -39,10 +39,12 @@
     public static final int ERROR_STARTING_IPV4                   = 4;
     public static final int ERROR_STARTING_IPV6                   = 5;
     public static final int ERROR_STARTING_IPREACHABILITYMONITOR  = 6;
+    public static final int ERROR_INVALID_PROVISIONING            = 7;
 
     @IntDef(value = {
             PROVISIONING_OK, PROVISIONING_FAIL, COMPLETE_LIFECYCLE,
             ERROR_STARTING_IPV4, ERROR_STARTING_IPV6, ERROR_STARTING_IPREACHABILITYMONITOR,
+            ERROR_INVALID_PROVISIONING,
     })
     @Retention(RetentionPolicy.SOURCE)
     public @interface EventType {}
diff --git a/core/java/android/os/BatteryStats.java b/core/java/android/os/BatteryStats.java
index a34668c..41f090a 100644
--- a/core/java/android/os/BatteryStats.java
+++ b/core/java/android/os/BatteryStats.java
@@ -16,15 +16,6 @@
 
 package android.os;
 
-import java.io.PrintWriter;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.Comparator;
-import java.util.Formatter;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-
 import android.app.job.JobParameters;
 import android.content.Context;
 import android.content.pm.ApplicationInfo;
@@ -43,6 +34,15 @@
 import com.android.internal.os.BatterySipper;
 import com.android.internal.os.BatteryStatsHelper;
 
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.Formatter;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
 /**
  * A class providing access to battery usage statistics, including information on
  * wakelocks, processes, packages, and services.  All times are represented in microseconds
@@ -168,6 +168,11 @@
     public static final int BLUETOOTH_UNOPTIMIZED_SCAN_ON = 21;
 
     /**
+     * A constant indicating a foreground service timer
+     */
+    public static final int FOREGROUND_SERVICE = 22;
+
+    /**
      * Include all of the data in the stats, including previously saved data.
      */
     public static final int STATS_SINCE_CHARGED = 0;
@@ -223,7 +228,11 @@
     private static final String CPU_TIMES_AT_FREQ_DATA = "ctf";
     private static final String SENSOR_DATA = "sr";
     private static final String VIBRATOR_DATA = "vib";
-    private static final String FOREGROUND_DATA = "fg";
+    private static final String FOREGROUND_ACTIVITY_DATA = "fg";
+    // fgs line is:
+    // BATTERY_STATS_CHECKIN_VERSION, uid, category, "fgs",
+    // foreground service time, count
+    private static final String FOREGROUND_SERVICE_DATA = "fgs";
     private static final String STATE_TIME_DATA = "st";
     // wl line is:
     // BATTERY_STATS_CHECKIN_VERSION, uid, which, "wl", name,
@@ -582,6 +591,11 @@
         public abstract Timer getFlashlightTurnedOnTimer();
         public abstract Timer getCameraTurnedOnTimer();
         public abstract Timer getForegroundActivityTimer();
+
+        /**
+         * Returns the timer keeping track of Foreground Service time
+         */
+        public abstract Timer getForegroundServiceTimer();
         public abstract Timer getBluetoothScanTimer();
         public abstract Timer getBluetoothScanBackgroundTimer();
         public abstract Timer getBluetoothUnoptimizedScanTimer();
@@ -3616,7 +3630,10 @@
             dumpTimer(pw, uid, category, VIBRATOR_DATA, u.getVibratorOnTimer(),
                     rawRealtime, which);
 
-            dumpTimer(pw, uid, category, FOREGROUND_DATA, u.getForegroundActivityTimer(),
+            dumpTimer(pw, uid, category, FOREGROUND_ACTIVITY_DATA, u.getForegroundActivityTimer(),
+                    rawRealtime, which);
+
+            dumpTimer(pw, uid, category, FOREGROUND_SERVICE_DATA, u.getForegroundServiceTimer(),
                     rawRealtime, which);
 
             final Object[] stateTimes = new Object[Uid.NUM_PROCESS_STATE];
@@ -5093,6 +5110,8 @@
                     "Vibrator");
             uidActivity |= printTimer(pw, sb, u.getForegroundActivityTimer(), rawRealtime, which,
                     prefix, "Foreground activities");
+            uidActivity |= printTimer(pw, sb, u.getForegroundServiceTimer(), rawRealtime, which,
+                    prefix, "Foreground services");
 
             long totalStateTime = 0;
             for (int ips=0; ips<Uid.NUM_PROCESS_STATE; ips++) {
diff --git a/core/java/android/os/Debug.java b/core/java/android/os/Debug.java
index f243f37..b46c6b1 100644
--- a/core/java/android/os/Debug.java
+++ b/core/java/android/os/Debug.java
@@ -223,28 +223,69 @@
         /** @hide */
         public static final int OTHER_OTHER_MEMTRACK = 16;
 
+        // Needs to be declared here for the DVK_STAT ranges below.
+        /** @hide */
+        public static final int NUM_OTHER_STATS = 17;
+
+        // Dalvik subsections.
         /** @hide */
         public static final int OTHER_DALVIK_NORMAL = 17;
         /** @hide */
         public static final int OTHER_DALVIK_LARGE = 18;
         /** @hide */
-        public static final int OTHER_DALVIK_LINEARALLOC = 19;
+        public static final int OTHER_DALVIK_ZYGOTE = 19;
         /** @hide */
-        public static final int OTHER_DALVIK_ACCOUNTING = 20;
+        public static final int OTHER_DALVIK_NON_MOVING = 20;
+        // Section begins and ends for dumpsys, relative to the DALVIK categories.
         /** @hide */
-        public static final int OTHER_DALVIK_CODE_CACHE = 21;
+        public static final int OTHER_DVK_STAT_DALVIK_START =
+                OTHER_DALVIK_NORMAL - NUM_OTHER_STATS;
         /** @hide */
-        public static final int OTHER_DALVIK_ZYGOTE = 22;
+        public static final int OTHER_DVK_STAT_DALVIK_END =
+                OTHER_DALVIK_NON_MOVING - NUM_OTHER_STATS;
+
+        // Dalvik Other subsections.
         /** @hide */
-        public static final int OTHER_DALVIK_NON_MOVING = 23;
+        public static final int OTHER_DALVIK_OTHER_LINEARALLOC = 21;
         /** @hide */
-        public static final int OTHER_DALVIK_INDIRECT_REFERENCE_TABLE = 24;
+        public static final int OTHER_DALVIK_OTHER_ACCOUNTING = 22;
+        /** @hide */
+        public static final int OTHER_DALVIK_OTHER_CODE_CACHE = 23;
+        /** @hide */
+        public static final int OTHER_DALVIK_OTHER_COMPILER_METADATA = 24;
+        /** @hide */
+        public static final int OTHER_DALVIK_OTHER_INDIRECT_REFERENCE_TABLE = 25;
+        /** @hide */
+        public static final int OTHER_DVK_STAT_DALVIK_OTHER_START =
+                OTHER_DALVIK_OTHER_LINEARALLOC - NUM_OTHER_STATS;
+        /** @hide */
+        public static final int OTHER_DVK_STAT_DALVIK_OTHER_END =
+                OTHER_DALVIK_OTHER_INDIRECT_REFERENCE_TABLE - NUM_OTHER_STATS;
+
+        // Dex subsections (Boot vdex, App dex, and App vdex).
+        /** @hide */
+        public static final int OTHER_DEX_BOOT_VDEX = 26;
+        /** @hide */
+        public static final int OTHER_DEX_APP_DEX = 27;
+        /** @hide */
+        public static final int OTHER_DEX_APP_VDEX = 28;
+        /** @hide */
+        public static final int OTHER_DVK_STAT_DEX_START = OTHER_DEX_BOOT_VDEX - NUM_OTHER_STATS;
+        /** @hide */
+        public static final int OTHER_DVK_STAT_DEX_END = OTHER_DEX_APP_VDEX - NUM_OTHER_STATS;
+
+        // Art subsections (App image, boot image).
+        /** @hide */
+        public static final int OTHER_ART_APP = 29;
+        /** @hide */
+        public static final int OTHER_ART_BOOT = 30;
+        /** @hide */
+        public static final int OTHER_DVK_STAT_ART_START = OTHER_ART_APP - NUM_OTHER_STATS;
+        /** @hide */
+        public static final int OTHER_DVK_STAT_ART_END = OTHER_ART_BOOT - NUM_OTHER_STATS;
 
         /** @hide */
-        public static final int NUM_OTHER_STATS = 17;
-
-        /** @hide */
-        public static final int NUM_DVK_STATS = 8;
+        public static final int NUM_DVK_STATS = 14;
 
         /** @hide */
         public static final int NUM_CATEGORIES = 8;
@@ -408,12 +449,18 @@
                 case OTHER_OTHER_MEMTRACK: return "Other mtrack";
                 case OTHER_DALVIK_NORMAL: return ".Heap";
                 case OTHER_DALVIK_LARGE: return ".LOS";
-                case OTHER_DALVIK_LINEARALLOC: return ".LinearAlloc";
-                case OTHER_DALVIK_ACCOUNTING: return ".GC";
-                case OTHER_DALVIK_CODE_CACHE: return ".JITCache";
                 case OTHER_DALVIK_ZYGOTE: return ".Zygote";
                 case OTHER_DALVIK_NON_MOVING: return ".NonMoving";
-                case OTHER_DALVIK_INDIRECT_REFERENCE_TABLE: return ".IndirectRef";
+                case OTHER_DALVIK_OTHER_LINEARALLOC: return ".LinearAlloc";
+                case OTHER_DALVIK_OTHER_ACCOUNTING: return ".GC";
+                case OTHER_DALVIK_OTHER_CODE_CACHE: return ".JITCache";
+                case OTHER_DALVIK_OTHER_COMPILER_METADATA: return ".CompilerMetadata";
+                case OTHER_DALVIK_OTHER_INDIRECT_REFERENCE_TABLE: return ".IndirectRef";
+                case OTHER_DEX_BOOT_VDEX: return ".Boot vdex";
+                case OTHER_DEX_APP_DEX: return ".App dex";
+                case OTHER_DEX_APP_VDEX: return ".App vdex";
+                case OTHER_ART_APP: return ".App art";
+                case OTHER_ART_BOOT: return ".Boot art";
                 default: return "????";
             }
         }
@@ -1814,6 +1861,13 @@
     public static native void dumpNativeHeap(FileDescriptor fd);
 
     /**
+     * Writes malloc info data to the specified file descriptor.
+     *
+     * @hide
+     */
+    public static native void dumpNativeMallocInfo(FileDescriptor fd);
+
+    /**
       * Returns a count of the extant instances of a class.
      *
      * @hide
diff --git a/core/java/android/os/HwBinder.java b/core/java/android/os/HwBinder.java
index b09c51c..866e20c 100644
--- a/core/java/android/os/HwBinder.java
+++ b/core/java/android/os/HwBinder.java
@@ -51,6 +51,11 @@
             String serviceName)
         throws RemoteException, NoSuchElementException;
 
+    public static native final void configureRpcThreadpool(
+            long maxThreads, boolean callerWillJoin);
+
+    public static native final void joinRpcThreadpool();
+
     // Returns address of the "freeFunction".
     private static native final long native_init();
 
diff --git a/core/java/android/os/INetworkManagementService.aidl b/core/java/android/os/INetworkManagementService.aidl
index 92e78bc..3de2174 100644
--- a/core/java/android/os/INetworkManagementService.aidl
+++ b/core/java/android/os/INetworkManagementService.aidl
@@ -20,6 +20,7 @@
 import android.net.InterfaceConfiguration;
 import android.net.INetd;
 import android.net.INetworkManagementEventObserver;
+import android.net.ITetheringStatsProvider;
 import android.net.Network;
 import android.net.NetworkStats;
 import android.net.RouteInfo;
@@ -207,6 +208,18 @@
     void disableNat(String internalInterface, String externalInterface);
 
     /**
+     * Registers a {@code ITetheringStatsProvider} to provide tethering statistics.
+     * All registered providers will be called in order, and their results will be added together.
+     * Netd is always registered as a tethering stats provider.
+     */
+    void registerTetheringStatsProvider(ITetheringStatsProvider provider, String name);
+
+    /**
+     * Unregisters a previously-registered {@code ITetheringStatsProvider}.
+     */
+    void unregisterTetheringStatsProvider(ITetheringStatsProvider provider);
+
+    /**
      ** PPPD
      **/
 
diff --git a/core/java/android/os/RecoverySystem.java b/core/java/android/os/RecoverySystem.java
index 2c6c7f9..7fa1c5a 100644
--- a/core/java/android/os/RecoverySystem.java
+++ b/core/java/android/os/RecoverySystem.java
@@ -751,7 +751,9 @@
         // Block until the ordered broadcast has completed.
         condition.block();
 
-        wipeEuiccData(context, wipeEuicc);
+        // TODO(b/63693573): Uncomment this once the pSIM slot is restored as needed
+        // after the ensuing boot. Currently you end up stuck on the eSIM.
+        // wipeEuiccData(context, wipeEuicc);
 
         String shutdownArg = null;
         if (shutdown) {
@@ -808,7 +810,8 @@
             HandlerThread euiccHandlerThread = new HandlerThread("euiccWipeFinishReceiverThread");
             euiccHandlerThread.start();
             Handler euiccHandler = new Handler(euiccHandlerThread.getLooper());
-            context.registerReceiver(euiccWipeFinishReceiver, filterConsent, null, euiccHandler);
+            context.getApplicationContext()
+                    .registerReceiver(euiccWipeFinishReceiver, filterConsent, null, euiccHandler);
             if (isWipeEuicc) {
                 euiccManager.eraseSubscriptions(callbackIntent);
             } else {
@@ -831,7 +834,7 @@
                         Log.e(TAG, "Timeout retaining eUICC data.");
                     }
                 }
-                context.unregisterReceiver(euiccWipeFinishReceiver);
+                context.getApplicationContext().unregisterReceiver(euiccWipeFinishReceiver);
             } catch (InterruptedException e) {
                 Thread.currentThread().interrupt();
                 if (isWipeEuicc) {
diff --git a/core/java/android/os/storage/StorageManager.java b/core/java/android/os/storage/StorageManager.java
index 71f5ff7..6372113 100644
--- a/core/java/android/os/storage/StorageManager.java
+++ b/core/java/android/os/storage/StorageManager.java
@@ -739,7 +739,7 @@
      * {@link Environment#getDataDirectory()}, the returned value will be
      * {@link #UUID_DEFAULT}.
      *
-     * @throws IOException when the storage device at the given path isn't
+     * @throws IOException when the storage device hosting the given path isn't
      *             present, or when it doesn't have a valid UUID.
      */
     public @NonNull UUID getUuidForPath(@NonNull File path) throws IOException {
@@ -771,6 +771,19 @@
         throw new FileNotFoundException("Failed to find a storage device for " + volumeUuid);
     }
 
+    /**
+     * Test if the given file descriptor supports allocation of disk space using
+     * {@link #allocateBytes(FileDescriptor, long)}.
+     */
+    public boolean isAllocationSupported(@NonNull FileDescriptor fd) {
+        try {
+            getUuidForPath(ParcelFileDescriptor.getFile(fd));
+            return true;
+        } catch (IOException e) {
+            return false;
+        }
+    }
+
     /** {@hide} */
     public @NonNull List<VolumeInfo> getVolumes() {
         try {
@@ -1562,12 +1575,6 @@
         }
     }
 
-    /** @removed */
-    @Deprecated
-    public long getCacheQuotaBytes(@NonNull File path) throws IOException {
-        return getCacheQuotaBytes(getUuidForPath(path));
-    }
-
     /**
      * Return total size in bytes of all cached data belonging to the calling
      * app on the given storage volume.
@@ -1603,36 +1610,6 @@
         }
     }
 
-    /** @removed */
-    @Deprecated
-    public long getCacheSizeBytes(@NonNull File path) throws IOException {
-        return getCacheSizeBytes(getUuidForPath(path));
-    }
-
-    /** @removed */
-    @Deprecated
-    public long getCacheQuotaBytes() throws IOException {
-        return getCacheQuotaBytes(mContext.getCacheDir());
-    }
-
-    /** @removed */
-    @Deprecated
-    public long getCacheSizeBytes() throws IOException {
-        return getCacheSizeBytes(mContext.getCacheDir());
-    }
-
-    /** @removed */
-    @Deprecated
-    public long getExternalCacheQuotaBytes() throws IOException {
-        return getCacheQuotaBytes(mContext.getExternalCacheDir());
-    }
-
-    /** @removed */
-    @Deprecated
-    public long getExternalCacheSizeBytes() throws IOException {
-        return getCacheSizeBytes(mContext.getExternalCacheDir());
-    }
-
     /**
      * Flag indicating that a disk space allocation request should operate in an
      * aggressive mode. This flag should only be rarely used in situations that
@@ -1741,15 +1718,6 @@
         }
     }
 
-    /** @removed */
-    @Deprecated
-    @WorkerThread
-    @SuppressLint("Doclava125")
-    public long getAllocatableBytes(@NonNull File path,
-            @RequiresPermission @AllocateFlags int flags) throws IOException {
-        return getAllocatableBytes(getUuidForPath(path), flags);
-    }
-
     /**
      * Allocate the requested number of bytes for your application to use on the
      * given storage volume. This will cause the system to delete any cached
@@ -1798,15 +1766,6 @@
         }
     }
 
-    /** @removed */
-    @Deprecated
-    @WorkerThread
-    @SuppressLint("Doclava125")
-    public void allocateBytes(@NonNull File path, @BytesLong long bytes,
-            @RequiresPermission @AllocateFlags int flags) throws IOException {
-        allocateBytes(getUuidForPath(path), bytes, flags);
-    }
-
     /**
      * Allocate the requested number of bytes for your application to use in the
      * given open file. This will cause the system to delete any cached files
@@ -1834,6 +1793,7 @@
      *             doesn't support allocating space, or if the device had
      *             trouble allocating the requested space.
      * @see #getAllocatableBytes(UUID, int)
+     * @see #isAllocationSupported(FileDescriptor)
      * @see Environment#isExternalStorageEmulated(File)
      */
     @WorkerThread
@@ -1848,17 +1808,28 @@
     public void allocateBytes(FileDescriptor fd, @BytesLong long bytes,
             @RequiresPermission @AllocateFlags int flags) throws IOException {
         final File file = ParcelFileDescriptor.getFile(fd);
+        final UUID uuid = getUuidForPath(file);
         for (int i = 0; i < 3; i++) {
             try {
                 final long haveBytes = Os.fstat(fd).st_blocks * 512;
                 final long needBytes = bytes - haveBytes;
 
                 if (needBytes > 0) {
-                    allocateBytes(file, needBytes, flags);
+                    allocateBytes(uuid, needBytes, flags);
                 }
 
-                Os.posix_fallocate(fd, 0, bytes);
-                return;
+                try {
+                    Os.posix_fallocate(fd, 0, bytes);
+                    return;
+                } catch (ErrnoException e) {
+                    if (e.errno == OsConstants.ENOSYS || e.errno == OsConstants.ENOTSUP) {
+                        Log.w(TAG, "fallocate() not supported; falling back to ftruncate()");
+                        Os.ftruncate(fd, bytes);
+                        return;
+                    } else {
+                        throw e;
+                    }
+                }
             } catch (ErrnoException e) {
                 if (e.errno == OsConstants.ENOSPC) {
                     Log.w(TAG, "Odd, not enough space; let's try again?");
@@ -1941,18 +1912,6 @@
         return isCacheBehavior(path, XATTR_CACHE_GROUP);
     }
 
-    /** @removed */
-    @Deprecated
-    public void setCacheBehaviorAtomic(File path, boolean atomic) throws IOException {
-        setCacheBehaviorGroup(path, atomic);
-    }
-
-    /** @removed */
-    @Deprecated
-    public boolean isCacheBehaviorAtomic(File path) throws IOException {
-        return isCacheBehaviorGroup(path);
-    }
-
     /**
      * Enable or disable special cache behavior that leaves deleted cache files
      * intact as tombstones.
diff --git a/core/java/android/print/PrintManager.java b/core/java/android/print/PrintManager.java
index 52dccb4..51b7798 100644
--- a/core/java/android/print/PrintManager.java
+++ b/core/java/android/print/PrintManager.java
@@ -18,9 +18,9 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.annotation.RequiresPermission;
 import android.annotation.SystemApi;
 import android.annotation.SystemService;
-import android.annotation.TestApi;
 import android.app.Activity;
 import android.app.Application.ActivityLifecycleCallbacks;
 import android.content.ComponentName;
@@ -142,7 +142,6 @@
      * @see #getPrintServices
      * @hide
      */
-    @TestApi
     public static final int ALL_SERVICES = ENABLED_SERVICES | DISABLED_SERVICES;
 
     /**
@@ -554,6 +553,7 @@
      * @hide
      */
     @SystemApi
+    @RequiresPermission(android.Manifest.permission.READ_PRINT_SERVICES)
     public void addPrintServicesChangeListener(@NonNull PrintServicesChangeListener listener,
             @Nullable Handler handler) {
         Preconditions.checkNotNull(listener);
@@ -589,6 +589,7 @@
      * @hide
      */
     @SystemApi
+    @RequiresPermission(android.Manifest.permission.READ_PRINT_SERVICES)
     public void removePrintServicesChangeListener(@NonNull PrintServicesChangeListener listener) {
         Preconditions.checkNotNull(listener);
 
@@ -629,8 +630,8 @@
      *
      * @hide
      */
-    @TestApi
     @SystemApi
+    @RequiresPermission(android.Manifest.permission.READ_PRINT_SERVICES)
     public @NonNull List<PrintServiceInfo> getPrintServices(int selectionFlags) {
         Preconditions.checkFlagsArgument(selectionFlags, ALL_SERVICES);
 
@@ -656,6 +657,7 @@
      * @hide
      */
     @SystemApi
+    @RequiresPermission(android.Manifest.permission.READ_PRINT_SERVICE_RECOMMENDATIONS)
     public void addPrintServiceRecommendationsChangeListener(
             @NonNull PrintServiceRecommendationsChangeListener listener,
             @Nullable Handler handler) {
@@ -692,6 +694,7 @@
      * @hide
      */
     @SystemApi
+    @RequiresPermission(android.Manifest.permission.READ_PRINT_SERVICE_RECOMMENDATIONS)
     public void removePrintServiceRecommendationsChangeListener(
             @NonNull PrintServiceRecommendationsChangeListener listener) {
         Preconditions.checkNotNull(listener);
@@ -731,6 +734,7 @@
      * @hide
      */
     @SystemApi
+    @RequiresPermission(android.Manifest.permission.READ_PRINT_SERVICE_RECOMMENDATIONS)
     public @NonNull List<RecommendationInfo> getPrintServiceRecommendations() {
         try {
             List<RecommendationInfo> recommendations =
diff --git a/core/java/android/printservice/PrintServiceInfo.java b/core/java/android/printservice/PrintServiceInfo.java
index 5ef9319..57f1229 100644
--- a/core/java/android/printservice/PrintServiceInfo.java
+++ b/core/java/android/printservice/PrintServiceInfo.java
@@ -18,7 +18,6 @@
 
 import android.annotation.NonNull;
 import android.annotation.SystemApi;
-import android.annotation.TestApi;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.pm.PackageManager;
@@ -49,7 +48,6 @@
  *
  * @hide
  */
-@TestApi
 @SystemApi
 public final class PrintServiceInfo implements Parcelable {
 
diff --git a/core/java/android/provider/Downloads.java b/core/java/android/provider/Downloads.java
index 9d83bd7..a2c5a92 100644
--- a/core/java/android/provider/Downloads.java
+++ b/core/java/android/provider/Downloads.java
@@ -485,6 +485,7 @@
          * partition. This option is only used by system apps and so it requires
          * android.permission.ACCESS_CACHE_FILESYSTEM permission.
          */
+        @Deprecated
         public static final int DESTINATION_SYSTEMCACHE_PARTITION = 5;
 
         /**
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 8e3e378..f5597b8 100755
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -1453,10 +1453,10 @@
      * to the caller package.
      *
      * <p>
-     * <b>NOTE: </b> applications should call
+     * <b>NOTE: </b> Applications should call
      * {@link android.view.autofill.AutofillManager#hasEnabledAutofillServices()} and
-     * {@link android.view.autofill.AutofillManager#isAutofillSupported()} first, and only
-     * broadcast this intent if they return {@code false} and {@code true} respectively.
+     * {@link android.view.autofill.AutofillManager#isAutofillSupported()}, and only use this action
+     * to start an activity if they return {@code false} and {@code true} respectively.
      */
     @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
     public static final String ACTION_REQUEST_SET_AUTOFILL_SERVICE =
@@ -6541,6 +6541,12 @@
         public static final String DOZE_PULSE_ON_PICK_UP = "doze_pulse_on_pick_up";
 
         /**
+         * Whether the device should pulse on long press gesture.
+         * @hide
+         */
+        public static final String DOZE_PULSE_ON_LONG_PRESS = "doze_pulse_on_long_press";
+
+        /**
          * Whether the device should pulse on double tap gesture.
          * @hide
          */
@@ -7738,6 +7744,16 @@
          */
         public static final String CDMA_SUBSCRIPTION_MODE = "subscription_mode";
 
+        /**
+         * The default value for whether background data is enabled or not.
+         *
+         * Used by {@code NetworkPolicyManagerService}.
+         *
+         * @hide
+         */
+        public static final String DEFAULT_RESTRICT_BACKGROUND_DATA =
+                "default_restrict_background_data";
+
         /** Inactivity timeout to track mobile data activity.
         *
         * If set to a positive integer, it indicates the inactivity timeout value in seconds to
diff --git a/core/java/android/provider/TimeZoneRulesDataContract.java b/core/java/android/provider/TimeZoneRulesDataContract.java
index a607563..33d2588 100644
--- a/core/java/android/provider/TimeZoneRulesDataContract.java
+++ b/core/java/android/provider/TimeZoneRulesDataContract.java
@@ -16,6 +16,7 @@
 
 package android.provider;
 
+import android.annotation.SystemApi;
 import android.net.Uri;
 
 /**
@@ -24,7 +25,7 @@
  *
  * @hide
  */
-// TODO(nfuller): Expose necessary APIs for OEMs with @SystemApi. http://b/31008728
+@SystemApi
 public final class TimeZoneRulesDataContract {
 
     private TimeZoneRulesDataContract() {}
diff --git a/core/java/android/security/IKeystoreService.aidl b/core/java/android/security/IKeystoreService.aidl
index bfc8636c..42282ac 100644
--- a/core/java/android/security/IKeystoreService.aidl
+++ b/core/java/android/security/IKeystoreService.aidl
@@ -48,7 +48,7 @@
     byte[] sign(String name, in byte[] data);
     int verify(String name, in byte[] data, in byte[] signature);
     byte[] get_pubkey(String name);
-    int grant(String name, int granteeUid);
+    String grant(String name, int granteeUid);
     int ungrant(String name, int granteeUid);
     long getmtime(String name, int uid);
     int duplicate(String srcKey, int srcUid, String destKey, int destUid);
diff --git a/core/java/android/service/autofill/CharSequenceTransformation.java b/core/java/android/service/autofill/CharSequenceTransformation.java
index 9d8345c..77555fe 100644
--- a/core/java/android/service/autofill/CharSequenceTransformation.java
+++ b/core/java/android/service/autofill/CharSequenceTransformation.java
@@ -44,18 +44,21 @@
  * be:
  *
  * <pre class="prettyprint">
- * new CharSequenceTransformation.Builder(ccNumberId, "^.*(\\d\\d\\d\\d)$", "...$1").build();
+ * new CharSequenceTransformation
+ *     .Builder(ccNumberId, Pattern.compile("^.*(\\d\\d\\d\\d)$"), "...$1")
+ *     .build();
  * </pre>
  *
  * <p>But a transformation that generates a {@code Exp: MM / YYYY} credit expiration date from two
  * fields (month and year) would be:
  *
  * <pre class="prettyprint">
- * new CharSequenceTransformation.Builder(ccExpMonthId, "^(\\d\\d)$", "Exp: $1")
- *   .addField(ccExpYearId, "^(\\d\\d\\d\\d)$", " / $1");
+ * new CharSequenceTransformation.Builder(ccExpMonthId, Pattern.compile("^(\\d\\d)$"), "Exp: $1")
+ *   .addField(ccExpYearId, Pattern.compile("^(\\d\\d\\d\\d)$"), " / $1");
  * </pre>
  */
-public final class CharSequenceTransformation extends InternalTransformation implements Parcelable {
+public final class CharSequenceTransformation extends InternalTransformation implements
+        Transformation, Parcelable {
     private static final String TAG = "CharSequenceTransformation";
     @NonNull private final ArrayMap<AutofillId, Pair<Pattern, String>> mFields;
 
@@ -106,12 +109,11 @@
          *
          * @param id id of the screen field.
          * @param regex regular expression with groups (delimited by {@code (} and {@code (}) that
-         * are used to substitute parts of the value. The pattern will be {@link Pattern#compile
-         * compiled} without setting any flags.
+         * are used to substitute parts of the value.
          * @param subst the string that substitutes the matched regex, using {@code $} for
          * group substitution ({@code $1} for 1st group match, {@code $2} for 2nd, etc).
          */
-        public Builder(@NonNull AutofillId id, @NonNull String regex, @NonNull String subst) {
+        public Builder(@NonNull AutofillId id, @NonNull Pattern regex, @NonNull String subst) {
             addField(id, regex, subst);
         }
 
@@ -120,24 +122,20 @@
          *
          * @param id id of the screen field.
          * @param regex regular expression with groups (delimited by {@code (} and {@code (}) that
-         * are used to substitute parts of the value. The pattern will be {@link Pattern#compile
-         * compiled} without setting any flags.
+         * are used to substitute parts of the value.
          * @param subst the string that substitutes the matched regex, using {@code $} for
          * group substitution ({@code $1} for 1st group match, {@code $2} for 2nd, etc).
          *
          * @return this builder.
          */
-        public Builder addField(@NonNull AutofillId id, @NonNull String regex,
+        public Builder addField(@NonNull AutofillId id, @NonNull Pattern regex,
                 @NonNull String subst) {
             throwIfDestroyed();
             Preconditions.checkNotNull(id);
             Preconditions.checkNotNull(regex);
             Preconditions.checkNotNull(subst);
 
-            // Check if the regex is valid
-            Pattern pattern = Pattern.compile(regex);
-
-            mFields.put(id, new Pair<>(pattern, subst));
+            mFields.put(id, new Pair<>(regex, subst));
             return this;
         }
 
@@ -177,17 +175,17 @@
     public void writeToParcel(Parcel parcel, int flags) {
         final int size = mFields.size();
         final AutofillId[] ids = new AutofillId[size];
-        final String[] regexs = new String[size];
+        final Pattern[] regexs = new Pattern[size];
         final String[] substs = new String[size];
         Pair<Pattern, String> pair;
         for (int i = 0; i < size; i++) {
             ids[i] = mFields.keyAt(i);
             pair = mFields.valueAt(i);
-            regexs[i] = pair.first.pattern();
+            regexs[i] = pair.first;
             substs[i] = pair.second;
         }
         parcel.writeParcelableArray(ids, flags);
-        parcel.writeStringArray(regexs);
+        parcel.writeSerializable(regexs);
         parcel.writeStringArray(substs);
     }
 
@@ -196,7 +194,7 @@
         @Override
         public CharSequenceTransformation createFromParcel(Parcel parcel) {
             final AutofillId[] ids = parcel.readParcelableArray(null, AutofillId.class);
-            final String[] regexs = parcel.createStringArray();
+            final Pattern[] regexs = (Pattern[]) parcel.readSerializable();
             final String[] substs = parcel.createStringArray();
 
             // Always go through the builder to ensure the data ingested by
diff --git a/core/java/android/service/autofill/CustomDescription.java b/core/java/android/service/autofill/CustomDescription.java
index 51530d6..e97c364 100644
--- a/core/java/android/service/autofill/CustomDescription.java
+++ b/core/java/android/service/autofill/CustomDescription.java
@@ -78,7 +78,6 @@
  * <p>See {@link ImageTransformation}, {@link CharSequenceTransformation} for more info about these
  * transformations.
  */
-// TODO(b/62534917): add integration tests
 public final class CustomDescription implements Parcelable {
 
     private static final String TAG = "CustomDescription";
@@ -93,7 +92,6 @@
 
     /** @hide */
     public RemoteViews getPresentation(ValueFinder finder) {
-        // TODO(b/62534917): need to handler errors, like not finding the ID
         if (mTransformations != null) {
             final int size = mTransformations.size();
             if (sDebug) Log.d(TAG, "getPresentation(): applying " + size + " transformations");
@@ -101,7 +99,13 @@
                 final int id = mTransformations.keyAt(i);
                 final InternalTransformation transformation = mTransformations.valueAt(i);
                 if (sDebug) Log.d(TAG, "#" + i + ": " + transformation);
-                transformation.apply(finder, mPresentation, id);
+
+                try {
+                    transformation.apply(finder, mPresentation, id);
+                } catch (Exception e) {
+                    Log.e(TAG, "Could not apply transformation " + transformation + ". "
+                            + e.getClass());
+                }
             }
         }
         return mPresentation;
diff --git a/core/java/android/service/autofill/ImageTransformation.java b/core/java/android/service/autofill/ImageTransformation.java
index 5407ad0..9eb52f6 100644
--- a/core/java/android/service/autofill/ImageTransformation.java
+++ b/core/java/android/service/autofill/ImageTransformation.java
@@ -42,16 +42,20 @@
  * <p>Typically used to display credit card logos. Example:
  *
  * <pre class="prettyprint">
- *   new ImageTransformation.Builder(ccNumberId, "^4815.*$", R.drawable.ic_credit_card_logo1)
- *     .addOption("^1623.*$", R.drawable.ic_credit_card_logo2)
- *     .addOption("^42.*$", R.drawable.ic_credit_card_logo3)
+ *   new ImageTransformation.Builder(ccNumberId, Pattern.compile("^4815.*$"),
+ *                                   R.drawable.ic_credit_card_logo1)
+ *     .addOption(Pattern.compile("^1623.*$"), R.drawable.ic_credit_card_logo2)
+ *     .addOption(Pattern.compile("^42.*$"), R.drawable.ic_credit_card_logo3)
  *     .build();
  * </pre>
  *
  * <p>There is no imposed limit in the number of options, but keep in mind that regexs are
- * expensive to evaluate, so use the minimum number of regexs.
+ * expensive to evaluate, so use the minimum number of regexs and add the most common first
+ * (for example, if this is a tranformation for a credit card logo and the most common credit card
+ * issuers are banks X and Y, add the regexes that resolves these 2 banks first).
  */
-public final class ImageTransformation extends InternalTransformation implements Parcelable {
+public final class ImageTransformation extends InternalTransformation implements Transformation,
+        Parcelable {
     private static final String TAG = "ImageTransformation";
 
     private final AutofillId mId;
@@ -106,7 +110,7 @@
          * @param resId resource id of the image (in the autofill service's package). The
          * {@link RemoteViews presentation} must contain a {@link ImageView} child with that id.
          */
-        public Builder(@NonNull AutofillId id, @NonNull String regex, @DrawableRes int resId) {
+        public Builder(@NonNull AutofillId id, @NonNull Pattern regex, @DrawableRes int resId) {
             mId = Preconditions.checkNotNull(id);
 
             addOption(regex, resId);
@@ -121,27 +125,21 @@
          *
          * @return this build
          */
-        public Builder addOption(@NonNull String regex, @DrawableRes int resId) {
+        public Builder addOption(@NonNull Pattern regex, @DrawableRes int resId) {
             throwIfDestroyed();
 
+            Preconditions.checkNotNull(regex);
             Preconditions.checkArgument(resId != 0);
 
-            // Check regex
-            Pattern pattern = Pattern.compile(regex);
-
-            mOptions.add(new Pair<>(pattern, resId));
+            mOptions.add(new Pair<>(regex, resId));
             return this;
         }
 
         /**
          * Creates a new {@link ImageTransformation} instance.
-         *
-         * @throws IllegalStateException if no call to {@link #addOption(String, int)} was made.
          */
         public ImageTransformation build() {
             throwIfDestroyed();
-            Preconditions.checkState(mOptions != null && !mOptions.isEmpty(),
-                    "Must add at least one option");
             mDestroyed = true;
             return new ImageTransformation(this);
         }
@@ -173,14 +171,14 @@
         parcel.writeParcelable(mId, flags);
 
         final int size = mOptions.size();
-        final String[] regexs = new String[size];
+        final Pattern[] regexs = new Pattern[size];
         final int[] resIds = new int[size];
         for (int i = 0; i < size; i++) {
             Pair<Pattern, Integer> regex = mOptions.get(i);
-            regexs[i] = regex.first.pattern();
+            regexs[i] = regex.first;
             resIds[i] = regex.second;
         }
-        parcel.writeStringArray(regexs);
+        parcel.writeSerializable(regexs);
         parcel.writeIntArray(resIds);
     }
 
@@ -190,7 +188,7 @@
         public ImageTransformation createFromParcel(Parcel parcel) {
             final AutofillId id = parcel.readParcelable(null);
 
-            final String[] regexs = parcel.createStringArray();
+            final Pattern[] regexs = (Pattern[]) parcel.readSerializable();
             final int[] resIds = parcel.createIntArray();
 
             // Always go through the builder to ensure the data ingested by the system obeys the
diff --git a/core/java/android/service/autofill/InternalTransformation.java b/core/java/android/service/autofill/InternalTransformation.java
index 3e51f87..e8ac14a 100644
--- a/core/java/android/service/autofill/InternalTransformation.java
+++ b/core/java/android/service/autofill/InternalTransformation.java
@@ -19,11 +19,17 @@
 import android.os.Parcelable;
 import android.widget.RemoteViews;
 
-/** @hide */
+/**
+ * Superclass of all transformation the system understands. As this is not public all
+ * subclasses have to implement {@link Transformation} again.
+ *
+ * @hide
+ */
 abstract class InternalTransformation implements Transformation, Parcelable {
 
     /**
-     * Applies this transformation to a child view of a {@link RemoteViews presentation template}.
+     * Applies this transformation to a child view of a {@link android.widget.RemoteViews
+     * presentation template}.
      *
      * @param finder object used to find the value of a field in the screen.
      * @param template the {@link RemoteViews presentation template}.
diff --git a/core/java/android/service/autofill/InternalValidator.java b/core/java/android/service/autofill/InternalValidator.java
index 37ef96f..e11cf6a 100644
--- a/core/java/android/service/autofill/InternalValidator.java
+++ b/core/java/android/service/autofill/InternalValidator.java
@@ -18,7 +18,12 @@
 import android.annotation.NonNull;
 import android.os.Parcelable;
 
-/** @hide */
+/**
+ * Superclass of all validators the system understands. As this is not public all public subclasses
+ * have to implement {@link Validator} again.
+ *
+ * @hide
+ */
 public abstract class InternalValidator implements Validator, Parcelable {
 
     /**
diff --git a/core/java/android/service/autofill/LuhnChecksumValidator.java b/core/java/android/service/autofill/LuhnChecksumValidator.java
index f959f3b9..0b5930d 100644
--- a/core/java/android/service/autofill/LuhnChecksumValidator.java
+++ b/core/java/android/service/autofill/LuhnChecksumValidator.java
@@ -33,7 +33,8 @@
  *
  * <p>See {@link SaveInfo.Builder#setValidator(Validator)} for examples.
  */
-public final class LuhnChecksumValidator extends InternalValidator implements Parcelable {
+public final class LuhnChecksumValidator extends InternalValidator implements Validator,
+        Parcelable {
     private static final String TAG = "LuhnChecksumValidator";
 
     private final AutofillId[] mIds;
diff --git a/core/java/android/service/autofill/OptionalValidators.java b/core/java/android/service/autofill/OptionalValidators.java
index c9dd1d4..f7edd6e 100644
--- a/core/java/android/service/autofill/OptionalValidators.java
+++ b/core/java/android/service/autofill/OptionalValidators.java
@@ -34,7 +34,7 @@
  */
 final class OptionalValidators extends InternalValidator {
 
-    private final InternalValidator[] mValidators;
+    @NonNull private final InternalValidator[] mValidators;
 
     OptionalValidators(@NonNull InternalValidator[] validators) {
         mValidators = Preconditions.checkArrayElementsNotNull(validators, "validators");
@@ -42,11 +42,6 @@
 
     @Override
     public boolean isValid(@NonNull ValueFinder finder) {
-        if (mValidators == null) {
-            return true;
-        }
-        // TODO(b/62534917): handle errors, like not finding the ID
-
         for (InternalValidator validator : mValidators) {
             final boolean valid = validator.isValid(finder);
             if (valid) return true;
diff --git a/core/java/android/service/autofill/RequiredValidators.java b/core/java/android/service/autofill/RequiredValidators.java
index f2b7db8..ac85c28 100644
--- a/core/java/android/service/autofill/RequiredValidators.java
+++ b/core/java/android/service/autofill/RequiredValidators.java
@@ -34,7 +34,7 @@
  */
 final class RequiredValidators extends InternalValidator {
 
-    private final InternalValidator[] mValidators;
+    @NonNull private final InternalValidator[] mValidators;
 
     RequiredValidators(@NonNull InternalValidator[] validators) {
         mValidators = Preconditions.checkArrayElementsNotNull(validators, "validators");
@@ -42,10 +42,6 @@
 
     @Override
     public boolean isValid(@NonNull ValueFinder finder) {
-        if (mValidators == null) {
-            return true;
-        }
-        // TODO(b/62534917): handle errors, like not finding the ID
         for (InternalValidator validator : mValidators) {
             final boolean valid = validator.isValid(finder);
             if (!valid) return false;
diff --git a/core/java/android/service/autofill/SaveInfo.java b/core/java/android/service/autofill/SaveInfo.java
index 4149173..389341b 100644
--- a/core/java/android/service/autofill/SaveInfo.java
+++ b/core/java/android/service/autofill/SaveInfo.java
@@ -30,6 +30,7 @@
 import android.view.autofill.AutofillManager;
 import android.view.autofill.AutofillValue;
 
+import com.android.internal.util.ArrayUtils;
 import com.android.internal.util.Preconditions;
 
 import java.lang.annotation.Retention;
@@ -252,7 +253,7 @@
     }
 
     /** @hide */
-    public AutofillId[] getRequiredIds() {
+    public @Nullable AutofillId[] getRequiredIds() {
         return mRequiredIds;
     }
 
@@ -321,11 +322,30 @@
          * it contains any {@code null} entry.
          */
         public Builder(@SaveDataType int type, @NonNull AutofillId[] requiredIds) {
-            // TODO: add CTS unit tests (not integration) to assert the null cases
             mType = type;
             mRequiredIds = assertValid(requiredIds);
         }
 
+        /**
+         * Creates a new builder when no id is required.
+         *
+         * <p>When using this builder, caller must call {@link #setOptionalIds(AutofillId[])} before
+         * calling {@link #build()}.
+         *
+         * @param type the type of information the associated {@link FillResponse} represents. It
+         * can be any combination of {@link SaveInfo#SAVE_DATA_TYPE_GENERIC},
+         * {@link SaveInfo#SAVE_DATA_TYPE_PASSWORD},
+         * {@link SaveInfo#SAVE_DATA_TYPE_ADDRESS}, {@link SaveInfo#SAVE_DATA_TYPE_CREDIT_CARD},
+         * {@link SaveInfo#SAVE_DATA_TYPE_USERNAME}, or
+         * {@link SaveInfo#SAVE_DATA_TYPE_EMAIL_ADDRESS}.
+         *
+         * <p>See {@link SaveInfo} for more info.
+         */
+        public Builder(@SaveDataType int type) {
+            mType = type;
+            mRequiredIds = null;
+        }
+
         private AutofillId[] assertValid(AutofillId[] ids) {
             Preconditions.checkArgument(ids != null && ids.length > 0,
                     "must have at least one id: " + Arrays.toString(ids));
@@ -362,7 +382,6 @@
          * it contains any {@code null} entry.
          */
         public @NonNull Builder setOptionalIds(@NonNull AutofillId[] ids) {
-            // TODO: add CTS unit tests (not integration) to assert the null cases
             throwIfDestroyed();
             mOptionalIds = assertValid(ids);
             return this;
@@ -486,10 +505,10 @@
          *
          * Validator validator =
          *   and(
-         *     new SimpleRegexValidator.(ccNumberId1, "^\\d{4}$"),
-         *     new SimpleRegexValidator.(ccNumberId2, "^\\d{4}$"),
-         *     new SimpleRegexValidator.(ccNumberId3, "^\\d{4}$"),
-         *     new SimpleRegexValidator.(ccNumberId4, "^\\d{4}$")
+         *     new SimpleRegexValidator(ccNumberId1, "^\\d{4}$"),
+         *     new SimpleRegexValidator(ccNumberId2, "^\\d{4}$"),
+         *     new SimpleRegexValidator(ccNumberId3, "^\\d{4}$"),
+         *     new SimpleRegexValidator(ccNumberId4, "^\\d{4}$")
          *   );
          * </pre>
          *
@@ -509,9 +528,16 @@
 
         /**
          * Builds a new {@link SaveInfo} instance.
+         *
+         * @throws IllegalStateException if no
+         * {@link #SaveInfo.Builder(int, AutofillId[]) required ids}
+         * or {@link #setOptionalIds(AutofillId[]) optional ids} were set
          */
         public SaveInfo build() {
             throwIfDestroyed();
+            Preconditions.checkState(
+                    !ArrayUtils.isEmpty(mRequiredIds) || !ArrayUtils.isEmpty(mOptionalIds),
+                    "must have at least one required or optional id");
             mDestroyed = true;
             return new SaveInfo(this);
         }
@@ -556,9 +582,9 @@
     public void writeToParcel(Parcel parcel, int flags) {
         parcel.writeInt(mType);
         parcel.writeParcelableArray(mRequiredIds, flags);
+        parcel.writeParcelableArray(mOptionalIds, flags);
         parcel.writeInt(mNegativeButtonStyle);
         parcel.writeParcelable(mNegativeActionListener, flags);
-        parcel.writeParcelableArray(mOptionalIds, flags);
         parcel.writeCharSequence(mDescription);
         parcel.writeParcelable(mCustomDescription, flags);
         parcel.writeParcelable(mValidator, flags);
@@ -568,16 +594,21 @@
     public static final Parcelable.Creator<SaveInfo> CREATOR = new Parcelable.Creator<SaveInfo>() {
         @Override
         public SaveInfo createFromParcel(Parcel parcel) {
+
             // Always go through the builder to ensure the data ingested by
             // the system obeys the contract of the builder to avoid attacks
             // using specially crafted parcels.
-            final Builder builder = new Builder(parcel.readInt(),
-                    parcel.readParcelableArray(null, AutofillId.class));
-            builder.setNegativeAction(parcel.readInt(), parcel.readParcelable(null));
+            final int type = parcel.readInt();
+            final AutofillId[] requiredIds = parcel.readParcelableArray(null, AutofillId.class);
+            final Builder builder = requiredIds != null
+                    ? new Builder(type, requiredIds)
+                    : new Builder(type);
             final AutofillId[] optionalIds = parcel.readParcelableArray(null, AutofillId.class);
             if (optionalIds != null) {
                 builder.setOptionalIds(optionalIds);
             }
+
+            builder.setNegativeAction(parcel.readInt(), parcel.readParcelable(null));
             builder.setDescription(parcel.readCharSequence());
             final CustomDescription customDescripton = parcel.readParcelable(null);
             if (customDescripton != null) {
diff --git a/core/java/android/service/autofill/SimpleRegexValidator.java b/core/java/android/service/autofill/SimpleRegexValidator.java
index ffe0076..ef8c52c9 100644
--- a/core/java/android/service/autofill/SimpleRegexValidator.java
+++ b/core/java/android/service/autofill/SimpleRegexValidator.java
@@ -19,6 +19,7 @@
 import static android.view.autofill.Helper.sDebug;
 
 import android.annotation.NonNull;
+import android.annotation.TestApi;
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.util.Log;
@@ -26,42 +27,44 @@
 
 import com.android.internal.util.Preconditions;
 
+import java.util.regex.Pattern;
+
 /**
  * Defines if a field is valid based on a regular expression (regex).
  *
  * <p>See {@link SaveInfo.Builder#setValidator(Validator)} for examples.
  */
-public final class SimpleRegexValidator extends InternalValidator implements Parcelable {
+public final class SimpleRegexValidator extends InternalValidator implements Validator, Parcelable {
 
     private static final String TAG = "SimpleRegexValidator";
 
     private final AutofillId mId;
-    private final String mRegex;
+    private final Pattern mRegex;
 
     /**
-      * Default constructor.
-      *
-      * @param id id of the field whose regex is applied to.
-      * @param regex regular expression that defines the result
-      * of the validator: if the regex matches the contents of
-      * the field identified by {@code id}, it returns {@code true}; otherwise, it
-      * returns {@code false}.
+     * Default constructor.
+     *
+     * @param id id of the field whose regex is applied to.
+     * @param regex regular expression that defines the result of the validator: if the regex
+     * matches the contents of the field identified by {@code id}, it returns {@code true};
+     * otherwise, it returns {@code false}.
       */
-    public SimpleRegexValidator(@NonNull AutofillId id, @NonNull String regex) {
+    public SimpleRegexValidator(@NonNull AutofillId id, @NonNull Pattern regex) {
         mId = Preconditions.checkNotNull(id);
-        //TODO(b/62534917): throw exception if regex is invalid
         mRegex = Preconditions.checkNotNull(regex);
     }
 
     /** @hide */
     @Override
+    @TestApi
     public boolean isValid(@NonNull ValueFinder finder) {
         final String value = finder.findByAutofillId(mId);
         if (value == null) {
             Log.w(TAG, "No view for id " + mId);
             return false;
         }
-        final boolean valid = value.matches(mRegex);
+
+        final boolean valid = mRegex.matcher(value).matches();
         if (sDebug) Log.d(TAG, "isValid(): " + valid);
         return valid;
     }
@@ -87,14 +90,15 @@
     @Override
     public void writeToParcel(Parcel parcel, int flags) {
         parcel.writeParcelable(mId, flags);
-        parcel.writeString(mRegex);
+        parcel.writeSerializable(mRegex);
     }
 
     public static final Parcelable.Creator<SimpleRegexValidator> CREATOR =
             new Parcelable.Creator<SimpleRegexValidator>() {
         @Override
         public SimpleRegexValidator createFromParcel(Parcel parcel) {
-            return new SimpleRegexValidator(parcel.readParcelable(null), parcel.readString());
+            return new SimpleRegexValidator(parcel.readParcelable(null),
+                    (Pattern) parcel.readSerializable());
         }
 
         @Override
diff --git a/core/java/android/service/autofill/Transformation.java b/core/java/android/service/autofill/Transformation.java
index 63b679d..4cef261 100644
--- a/core/java/android/service/autofill/Transformation.java
+++ b/core/java/android/service/autofill/Transformation.java
@@ -16,8 +16,8 @@
 package android.service.autofill;
 
 /**
- * Helper class used to change a child view of a {@link RemoteViews presentation template} at
- * runtime, using the values of fields contained in the screen.
+ * Helper class used to change a child view of a {@link android.widget.RemoteViews presentation
+ * template} at runtime, using the values of fields contained in the screen.
  *
  * <p>Typically used by {@link CustomDescription} to provide a customized Save UI affordance.
  */
diff --git a/core/java/android/service/notification/Adjustment.java b/core/java/android/service/notification/Adjustment.java
index 137cf57..ce678fc 100644
--- a/core/java/android/service/notification/Adjustment.java
+++ b/core/java/android/service/notification/Adjustment.java
@@ -17,7 +17,7 @@
 
 import android.annotation.SystemApi;
 import android.annotation.TestApi;
-import android.app.NotificationChannel;
+import android.app.Notification;
 import android.os.Bundle;
 import android.os.Parcel;
 import android.os.Parcelable;
@@ -48,6 +48,12 @@
      * {@link NotificationAssistantService#onNotificationSnoozedUntilContext}.
      */
     public static final String KEY_SNOOZE_CRITERIA = "key_snooze_criteria";
+    /**
+     * Data type: String. Used to change what {@link Notification#getGroup() group} a notification
+     * belongs to.
+     * @hide
+     */
+    public static final String KEY_GROUP_KEY = "key_group_key";
 
     /**
      * Create a notification adjustment.
@@ -146,4 +152,11 @@
         dest.writeBundle(mSignals);
         dest.writeInt(mUser);
     }
+
+    @Override
+    public String toString() {
+        return "Adjustment{"
+                + "mSignals=" + mSignals
+                + '}';
+    }
 }
diff --git a/core/java/android/text/FontConfig.java b/core/java/android/text/FontConfig.java
index ed58390..029f66e 100644
--- a/core/java/android/text/FontConfig.java
+++ b/core/java/android/text/FontConfig.java
@@ -64,17 +64,19 @@
         private final int mWeight;
         private final boolean mIsItalic;
         private Uri mUri;
+        private final String mFallbackFor;
 
         /**
          * @hide
          */
         public Font(@NonNull String fontName, int ttcIndex, @NonNull FontVariationAxis[] axes,
-                int weight, boolean isItalic) {
+                int weight, boolean isItalic, String fallbackFor) {
             mFontName = fontName;
             mTtcIndex = ttcIndex;
             mAxes = axes;
             mWeight = weight;
             mIsItalic = isItalic;
+            mFallbackFor = fallbackFor;
         }
 
         /**
@@ -125,6 +127,10 @@
         public void setUri(@NonNull Uri uri) {
             mUri = uri;
         }
+
+        public String getFallbackFor() {
+            return mFallbackFor;
+        }
     }
 
     /**
diff --git a/core/java/android/text/TextLine.java b/core/java/android/text/TextLine.java
index 1aaf73e..2dbff10 100644
--- a/core/java/android/text/TextLine.java
+++ b/core/java/android/text/TextLine.java
@@ -72,8 +72,8 @@
     private final SpanSet<ReplacementSpan> mReplacementSpanSpanSet =
             new SpanSet<ReplacementSpan>(ReplacementSpan.class);
 
-    private final UnderlineInfo mUnderlineInfo = new UnderlineInfo();
-    private final ArrayList<UnderlineInfo> mUnderlines = new ArrayList();
+    private final DecorationInfo mDecorationInfo = new DecorationInfo();
+    private final ArrayList<DecorationInfo> mDecorations = new ArrayList();
 
     private static final TextLine[] sCached = new TextLine[3];
 
@@ -704,9 +704,9 @@
         fmi.leading = Math.max(fmi.leading, previousLeading);
     }
 
-    private static void drawUnderline(TextPaint wp, Canvas c, int color, float thickness,
-            float xleft, float xright, float baseline) {
-        final float underlineTop = baseline + wp.baselineShift + wp.getUnderlinePosition();
+    private static void drawStroke(TextPaint wp, Canvas c, int color, float position,
+            float thickness, float xleft, float xright, float baseline) {
+        final float strokeTop = baseline + wp.baselineShift + position;
 
         final int previousColor = wp.getColor();
         final Paint.Style previousStyle = wp.getStyle();
@@ -716,7 +716,7 @@
         wp.setAntiAlias(true);
 
         wp.setColor(color);
-        c.drawRect(xleft, underlineTop, xright, underlineTop + thickness, wp);
+        c.drawRect(xleft, strokeTop, xright, strokeTop + thickness, wp);
 
         wp.setStyle(previousStyle);
         wp.setColor(previousColor);
@@ -750,7 +750,7 @@
      * @param fmi receives metrics information, can be null
      * @param needWidth true if the width of the run is needed
      * @param offset the offset for the purpose of measuring
-     * @param underlines the list of locations and paremeters for drawing underlines
+     * @param decorations the list of locations and paremeters for drawing decorations
      * @return the signed width of the run based on the run direction; only
      * valid if needWidth is true
      */
@@ -758,7 +758,7 @@
             int contextStart, int contextEnd, boolean runIsRtl,
             Canvas c, float x, int top, int y, int bottom,
             FontMetricsInt fmi, boolean needWidth, int offset,
-            @Nullable ArrayList<UnderlineInfo> underlines) {
+            @Nullable ArrayList<DecorationInfo> decorations) {
 
         wp.setWordSpacing(mAddedWidth);
         // Get metrics first (even for empty strings or "0" width runs)
@@ -773,8 +773,8 @@
 
         float totalWidth = 0;
 
-        final int numUnderlines = underlines == null ? 0 : underlines.size();
-        if (needWidth || (c != null && (wp.bgColor != 0 || numUnderlines != 0 || runIsRtl))) {
+        final int numDecorations = decorations == null ? 0 : decorations.size();
+        if (needWidth || (c != null && (wp.bgColor != 0 || numDecorations != 0 || runIsRtl))) {
             totalWidth = getRunAdvance(wp, start, end, contextStart, contextEnd, runIsRtl, offset);
         }
 
@@ -800,37 +800,44 @@
                 wp.setColor(previousColor);
             }
 
-            if (numUnderlines != 0) {
-                for (int i = 0; i < numUnderlines; i++) {
-                    final UnderlineInfo info = underlines.get(i);
+            if (numDecorations != 0) {
+                for (int i = 0; i < numDecorations; i++) {
+                    final DecorationInfo info = decorations.get(i);
 
-                    final int underlineStart = Math.max(info.start, start);
-                    final int underlineEnd = Math.min(info.end, offset);
-                    float underlineStartAdvance = getRunAdvance(
-                            wp, start, end, contextStart, contextEnd, runIsRtl, underlineStart);
-                    float underlineEndAdvance = getRunAdvance(
-                            wp, start, end, contextStart, contextEnd, runIsRtl, underlineEnd);
-                    final float underlineXLeft, underlineXRight;
+                    final int decorationStart = Math.max(info.start, start);
+                    final int decorationEnd = Math.min(info.end, offset);
+                    float decorationStartAdvance = getRunAdvance(
+                            wp, start, end, contextStart, contextEnd, runIsRtl, decorationStart);
+                    float decorationEndAdvance = getRunAdvance(
+                            wp, start, end, contextStart, contextEnd, runIsRtl, decorationEnd);
+                    final float decorationXLeft, decorationXRight;
                     if (runIsRtl) {
-                        underlineXLeft = rightX - underlineEndAdvance;
-                        underlineXRight = rightX - underlineStartAdvance;
+                        decorationXLeft = rightX - decorationEndAdvance;
+                        decorationXRight = rightX - decorationStartAdvance;
                     } else {
-                        underlineXLeft = leftX + underlineStartAdvance;
-                        underlineXRight = leftX + underlineEndAdvance;
+                        decorationXLeft = leftX + decorationStartAdvance;
+                        decorationXRight = leftX + decorationEndAdvance;
                     }
 
                     // Theoretically, there could be cases where both Paint's and TextPaint's
                     // setUnderLineText() are called. For backward compatibility, we need to draw
                     // both underlines, the one with custom color first.
                     if (info.underlineColor != 0) {
-                        drawUnderline(wp, c, info.underlineColor, info.underlineThickness,
-                                underlineXLeft, underlineXRight, y);
+                        drawStroke(wp, c, info.underlineColor, wp.getUnderlinePosition(),
+                                info.underlineThickness, decorationXLeft, decorationXRight, y);
                     }
                     if (info.isUnderlineText) {
                         final float thickness =
                                 Math.max(((Paint) wp).getUnderlineThickness(), 1.0f);
-                        drawUnderline(wp, c, wp.getColor(), thickness,
-                                underlineXLeft, underlineXRight, y);
+                        drawStroke(wp, c, wp.getColor(), wp.getUnderlinePosition(), thickness,
+                                decorationXLeft, decorationXRight, y);
+                    }
+
+                    if (info.isStrikeThruText) {
+                        final float thickness =
+                                Math.max(((Paint) wp).getStrikeThruThickness(), 1.0f);
+                        drawStroke(wp, c, wp.getColor(), wp.getStrikeThruPosition(), thickness,
+                                decorationXLeft, decorationXRight, y);
                     }
                 }
             }
@@ -919,20 +926,22 @@
         return result;
     }
 
-    private static final class UnderlineInfo {
+    private static final class DecorationInfo {
+        public boolean isStrikeThruText;
         public boolean isUnderlineText;
         public int underlineColor;
         public float underlineThickness;
         public int start = -1;
         public int end = -1;
 
-        public boolean hasUnderline() {
-            return isUnderlineText || underlineColor != 0;
+        public boolean hasDecoration() {
+            return isStrikeThruText || isUnderlineText || underlineColor != 0;
         }
 
         // Copies the info, but not the start and end range.
-        public UnderlineInfo copyInfo() {
-            final UnderlineInfo copy = new UnderlineInfo();
+        public DecorationInfo copyInfo() {
+            final DecorationInfo copy = new DecorationInfo();
+            copy.isStrikeThruText = isStrikeThruText;
             copy.isUnderlineText = isUnderlineText;
             copy.underlineColor = underlineColor;
             copy.underlineThickness = underlineThickness;
@@ -940,7 +949,11 @@
         }
     }
 
-    private void extractUnderlineInfo(@NonNull TextPaint paint, @NonNull UnderlineInfo info) {
+    private void extractDecorationInfo(@NonNull TextPaint paint, @NonNull DecorationInfo info) {
+        info.isStrikeThruText = paint.isStrikeThruText();
+        if (info.isStrikeThruText) {
+            paint.setStrikeThruText(false);
+        }
         info.isUnderlineText = paint.isUnderlineText();
         if (info.isUnderlineText) {
             paint.setUnderlineText(false);
@@ -1047,8 +1060,8 @@
             activePaint.set(mPaint);
             int activeStart = i;
             int activeEnd = mlimit;
-            final UnderlineInfo underlineInfo = mUnderlineInfo;
-            mUnderlines.clear();
+            final DecorationInfo decorationInfo = mDecorationInfo;
+            mDecorations.clear();
             for (int j = i, jnext; j < mlimit; j = jnext) {
                 jnext = mCharacterStyleSpanSet.getNextTransition(mStart + j, mStart + inext) -
                         mStart;
@@ -1064,7 +1077,7 @@
                     span.updateDrawState(wp);
                 }
 
-                extractUnderlineInfo(wp, underlineInfo);
+                extractDecorationInfo(wp, decorationInfo);
 
                 if (j == i) {
                     // First chunk of text. We can't handle it yet, since we may need to merge it
@@ -1079,24 +1092,24 @@
                             activeStart, activeEnd, mPaint.getHyphenEdit()));
                     x += handleText(activePaint, activeStart, activeEnd, i, inext, runIsRtl, c, x,
                             top, y, bottom, fmi, needWidth || activeEnd < measureLimit,
-                            Math.min(activeEnd, mlimit), mUnderlines);
+                            Math.min(activeEnd, mlimit), mDecorations);
 
                     activeStart = j;
                     activePaint.set(wp);
-                    mUnderlines.clear();
+                    mDecorations.clear();
                 } else {
                     // The present TextPaint is substantially equal to the last TextPaint except
-                    // perhaps for underlines. We just need to expand the active piece of text to
+                    // perhaps for decorations. We just need to expand the active piece of text to
                     // include the present chunk, which we always do anyway. We don't need to save
                     // wp to activePaint, since they are already equal.
                 }
 
                 activeEnd = jnext;
-                if (underlineInfo.hasUnderline()) {
-                    final UnderlineInfo copy = underlineInfo.copyInfo();
+                if (decorationInfo.hasDecoration()) {
+                    final DecorationInfo copy = decorationInfo.copyInfo();
                     copy.start = j;
                     copy.end = jnext;
-                    mUnderlines.add(copy);
+                    mDecorations.add(copy);
                 }
             }
             // Handle the final piece of text.
@@ -1104,7 +1117,7 @@
                     activeStart, activeEnd, mPaint.getHyphenEdit()));
             x += handleText(activePaint, activeStart, activeEnd, i, inext, runIsRtl, c, x,
                     top, y, bottom, fmi, needWidth || activeEnd < measureLimit,
-                    Math.min(activeEnd, mlimit), mUnderlines);
+                    Math.min(activeEnd, mlimit), mDecorations);
         }
 
         return x - originalX;
diff --git a/core/java/android/text/format/Formatter.java b/core/java/android/text/format/Formatter.java
index e5bc32bb..fc56455 100644
--- a/core/java/android/text/format/Formatter.java
+++ b/core/java/android/text/format/Formatter.java
@@ -20,7 +20,11 @@
 import android.annotation.Nullable;
 import android.content.Context;
 import android.content.res.Resources;
+import android.icu.text.DecimalFormat;
 import android.icu.text.MeasureFormat;
+import android.icu.text.NumberFormat;
+import android.icu.text.UnicodeSet;
+import android.icu.text.UnicodeSetSpanner;
 import android.icu.util.Measure;
 import android.icu.util.MeasureUnit;
 import android.net.NetworkUtils;
@@ -28,6 +32,7 @@
 import android.text.TextUtils;
 import android.view.View;
 
+import java.math.BigDecimal;
 import java.util.Locale;
 
 /**
@@ -37,6 +42,8 @@
 public final class Formatter {
 
     /** {@hide} */
+    public static final int FLAG_DEFAULT = 0;
+    /** {@hide} */
     public static final int FLAG_SHORTER = 1 << 0;
     /** {@hide} */
     public static final int FLAG_CALCULATE_ROUNDED = 1 << 1;
@@ -58,7 +65,9 @@
         return context.getResources().getConfiguration().getLocales().get(0);
     }
 
-    /* Wraps the source string in bidi formatting characters in RTL locales */
+    /**
+     * Wraps the source string in bidi formatting characters in RTL locales.
+     */
     private static String bidiWrap(@NonNull Context context, String source) {
         final Locale locale = localeFromContext(context);
         if (TextUtils.getLayoutDirectionFromLocale(locale) == View.LAYOUT_DIRECTION_RTL) {
@@ -87,12 +96,7 @@
      * @return formatted string with the number
      */
     public static String formatFileSize(@Nullable Context context, long sizeBytes) {
-        if (context == null) {
-            return "";
-        }
-        final BytesResult res = formatBytes(context.getResources(), sizeBytes, 0);
-        return bidiWrap(context, context.getString(com.android.internal.R.string.fileSizeSuffix,
-                res.value, res.units));
+        return formatFileSize(context, sizeBytes, FLAG_DEFAULT);
     }
 
     /**
@@ -100,88 +104,191 @@
      * (showing fewer digits of precision).
      */
     public static String formatShortFileSize(@Nullable Context context, long sizeBytes) {
+        return formatFileSize(context, sizeBytes, FLAG_SHORTER);
+    }
+
+    private static String formatFileSize(@Nullable Context context, long sizeBytes, int flags) {
         if (context == null) {
             return "";
         }
-        final BytesResult res = formatBytes(context.getResources(), sizeBytes, FLAG_SHORTER);
-        return bidiWrap(context, context.getString(com.android.internal.R.string.fileSizeSuffix,
-                res.value, res.units));
+        final RoundedBytesResult res = RoundedBytesResult.roundBytes(sizeBytes, flags);
+        return bidiWrap(context, formatRoundedBytesResult(context, res));
+    }
+
+    private static String getSuffixOverride(@NonNull Resources res, MeasureUnit unit) {
+        if (unit == MeasureUnit.BYTE) {
+            return res.getString(com.android.internal.R.string.byteShort);
+        } else { // unit == PETABYTE
+            return res.getString(com.android.internal.R.string.petabyteShort);
+        }
+    }
+
+    private static NumberFormat getNumberFormatter(Locale locale, int fractionDigits) {
+        final NumberFormat numberFormatter = NumberFormat.getInstance(locale);
+        numberFormatter.setMinimumFractionDigits(fractionDigits);
+        numberFormatter.setMaximumFractionDigits(fractionDigits);
+        numberFormatter.setGroupingUsed(false);
+        if (numberFormatter instanceof DecimalFormat) {
+            // We do this only for DecimalFormat, since in the general NumberFormat case, calling
+            // setRoundingMode may throw an exception.
+            numberFormatter.setRoundingMode(BigDecimal.ROUND_HALF_UP);
+        }
+        return numberFormatter;
+    }
+
+    private static String deleteFirstFromString(String source, String toDelete) {
+        final int location = source.indexOf(toDelete);
+        if (location == -1) {
+            return source;
+        } else {
+            return source.substring(0, location)
+                    + source.substring(location + toDelete.length(), source.length());
+        }
+    }
+
+    private static String formatMeasureShort(Locale locale, NumberFormat numberFormatter,
+            float value, MeasureUnit units) {
+        final MeasureFormat measureFormatter = MeasureFormat.getInstance(
+                locale, MeasureFormat.FormatWidth.SHORT, numberFormatter);
+        return measureFormatter.format(new Measure(value, units));
+    }
+
+    private static final UnicodeSetSpanner SPACES_AND_CONTROLS =
+            new UnicodeSetSpanner(new UnicodeSet("[[:Zs:][:Cf:]]").freeze());
+
+    private static String formatRoundedBytesResult(
+            @NonNull Context context, @NonNull RoundedBytesResult input) {
+        final Locale locale = localeFromContext(context);
+        final NumberFormat numberFormatter = getNumberFormatter(locale, input.fractionDigits);
+        if (input.units == MeasureUnit.BYTE || input.units == PETABYTE) {
+            // ICU spells out "byte" instead of "B", and can't format petabytes yet.
+            final String formattedNumber = numberFormatter.format(input.value);
+            return context.getString(com.android.internal.R.string.fileSizeSuffix,
+                    formattedNumber, getSuffixOverride(context.getResources(), input.units));
+        } else {
+            return formatMeasureShort(locale, numberFormatter, input.value, input.units);
+        }
     }
 
     /** {@hide} */
     public static BytesResult formatBytes(Resources res, long sizeBytes, int flags) {
-        final boolean isNegative = (sizeBytes < 0);
-        float result = isNegative ? -sizeBytes : sizeBytes;
-        int suffix = com.android.internal.R.string.byteShort;
-        long mult = 1;
-        if (result > 900) {
-            suffix = com.android.internal.R.string.kilobyteShort;
-            mult = 1000;
-            result = result / 1000;
+        final RoundedBytesResult rounded = RoundedBytesResult.roundBytes(sizeBytes, flags);
+        final Locale locale = res.getConfiguration().getLocales().get(0);
+        final NumberFormat numberFormatter = getNumberFormatter(locale, rounded.fractionDigits);
+        final String formattedNumber = numberFormatter.format(rounded.value);
+        final String units;
+        if (rounded.units == MeasureUnit.BYTE || rounded.units == PETABYTE) {
+            // ICU spells out "byte" instead of "B", and can't format petabytes yet.
+            units = getSuffixOverride(res, rounded.units);
+        } else {
+            // Since ICU does not give us access to the pattern, we need to extract the unit string
+            // from ICU, which we do by taking out the formatted number out of the formatted string
+            // and trimming the result of spaces and controls.
+            final String formattedMeasure = formatMeasureShort(
+                    locale, numberFormatter, rounded.value, rounded.units);
+            final String numberRemoved = deleteFirstFromString(formattedMeasure, formattedNumber);
+            units = SPACES_AND_CONTROLS.trim(numberRemoved).toString();
         }
-        if (result > 900) {
-            suffix = com.android.internal.R.string.megabyteShort;
-            mult *= 1000;
-            result = result / 1000;
+        return new BytesResult(formattedNumber, units, rounded.roundedBytes);
+    }
+
+    /**
+     * ICU doesn't support PETABYTE yet. Fake it so that we can treat all units the same way.
+     * {@hide}
+     */
+    public static final MeasureUnit PETABYTE = MeasureUnit.internalGetInstance(
+            "digital", "petabyte");
+
+    /** {@hide} */
+    public static class RoundedBytesResult {
+        public final float value;
+        public final MeasureUnit units;
+        public final int fractionDigits;
+        public final long roundedBytes;
+
+        private RoundedBytesResult(
+                float value, MeasureUnit units, int fractionDigits, long roundedBytes) {
+            this.value = value;
+            this.units = units;
+            this.fractionDigits = fractionDigits;
+            this.roundedBytes = roundedBytes;
         }
-        if (result > 900) {
-            suffix = com.android.internal.R.string.gigabyteShort;
-            mult *= 1000;
-            result = result / 1000;
-        }
-        if (result > 900) {
-            suffix = com.android.internal.R.string.terabyteShort;
-            mult *= 1000;
-            result = result / 1000;
-        }
-        if (result > 900) {
-            suffix = com.android.internal.R.string.petabyteShort;
-            mult *= 1000;
-            result = result / 1000;
-        }
-        // Note we calculate the rounded long by ourselves, but still let String.format()
-        // compute the rounded value. String.format("%f", 0.1) might not return "0.1" due to
-        // floating point errors.
-        final int roundFactor;
-        final String roundFormat;
-        if (mult == 1 || result >= 100) {
-            roundFactor = 1;
-            roundFormat = "%.0f";
-        } else if (result < 1) {
-            roundFactor = 100;
-            roundFormat = "%.2f";
-        } else if (result < 10) {
-            if ((flags & FLAG_SHORTER) != 0) {
-                roundFactor = 10;
-                roundFormat = "%.1f";
-            } else {
-                roundFactor = 100;
-                roundFormat = "%.2f";
+
+        /**
+         * Returns a RoundedBytesResult object based on the input size in bytes and the rounding
+         * flags. The result can be used for formatting.
+         */
+        public static RoundedBytesResult roundBytes(long sizeBytes, int flags) {
+            final boolean isNegative = (sizeBytes < 0);
+            float result = isNegative ? -sizeBytes : sizeBytes;
+            MeasureUnit units = MeasureUnit.BYTE;
+            long mult = 1;
+            if (result > 900) {
+                units = MeasureUnit.KILOBYTE;
+                mult = 1000;
+                result = result / 1000;
             }
-        } else { // 10 <= result < 100
-            if ((flags & FLAG_SHORTER) != 0) {
+            if (result > 900) {
+                units = MeasureUnit.MEGABYTE;
+                mult *= 1000;
+                result = result / 1000;
+            }
+            if (result > 900) {
+                units = MeasureUnit.GIGABYTE;
+                mult *= 1000;
+                result = result / 1000;
+            }
+            if (result > 900) {
+                units = MeasureUnit.TERABYTE;
+                mult *= 1000;
+                result = result / 1000;
+            }
+            if (result > 900) {
+                units = PETABYTE;
+                mult *= 1000;
+                result = result / 1000;
+            }
+            // Note we calculate the rounded long by ourselves, but still let NumberFormat compute
+            // the rounded value. NumberFormat.format(0.1) might not return "0.1" due to floating
+            // point errors.
+            final int roundFactor;
+            final int roundDigits;
+            if (mult == 1 || result >= 100) {
                 roundFactor = 1;
-                roundFormat = "%.0f";
-            } else {
+                roundDigits = 0;
+            } else if (result < 1) {
                 roundFactor = 100;
-                roundFormat = "%.2f";
+                roundDigits = 2;
+            } else if (result < 10) {
+                if ((flags & FLAG_SHORTER) != 0) {
+                    roundFactor = 10;
+                    roundDigits = 1;
+                } else {
+                    roundFactor = 100;
+                    roundDigits = 2;
+                }
+            } else { // 10 <= result < 100
+                if ((flags & FLAG_SHORTER) != 0) {
+                    roundFactor = 1;
+                    roundDigits = 0;
+                } else {
+                    roundFactor = 100;
+                    roundDigits = 2;
+                }
             }
+
+            if (isNegative) {
+                result = -result;
+            }
+
+            // Note this might overflow if abs(result) >= Long.MAX_VALUE / 100, but that's like
+            // 80PB so it's okay (for now)...
+            final long roundedBytes =
+                    (flags & FLAG_CALCULATE_ROUNDED) == 0 ? 0
+                    : (((long) Math.round(result * roundFactor)) * mult / roundFactor);
+
+            return new RoundedBytesResult(result, units, roundDigits, roundedBytes);
         }
-
-        if (isNegative) {
-            result = -result;
-        }
-        final String roundedString = String.format(roundFormat, result);
-
-        // Note this might overflow if abs(result) >= Long.MAX_VALUE / 100, but that's like 80PB so
-        // it's okay (for now)...
-        final long roundedBytes =
-                (flags & FLAG_CALCULATE_ROUNDED) == 0 ? 0
-                : (((long) Math.round(result * roundFactor)) * mult / roundFactor);
-
-        final String units = res.getString(suffix);
-
-        return new BytesResult(roundedString, units, roundedBytes);
     }
 
     /**
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 5225121..6af01f66 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -10180,6 +10180,7 @@
      *
      * @hide
      */
+    @TestApi
     public final void setFocusedInCluster() {
         setFocusedInCluster(findKeyboardNavigationCluster());
     }
diff --git a/core/java/android/view/ViewConfiguration.java b/core/java/android/view/ViewConfiguration.java
index 4def0d0..574137b 100644
--- a/core/java/android/view/ViewConfiguration.java
+++ b/core/java/android/view/ViewConfiguration.java
@@ -35,7 +35,7 @@
      * Defines the width of the horizontal scrollbar and the height of the vertical scrollbar in
      * dips
      */
-    private static final int SCROLL_BAR_SIZE = 10;
+    private static final int SCROLL_BAR_SIZE = 4;
 
     /**
      * Duration of the fade when scrollbars fade away in milliseconds
@@ -354,7 +354,8 @@
 
         mEdgeSlop = (int) (sizeAndDensity * EDGE_SLOP + 0.5f);
         mFadingEdgeLength = (int) (sizeAndDensity * FADING_EDGE_LENGTH + 0.5f);
-        mScrollbarSize = (int) (density * SCROLL_BAR_SIZE + 0.5f);
+        mScrollbarSize = res.getDimensionPixelSize(
+                com.android.internal.R.dimen.config_scrollbarSize);
         mDoubleTapSlop = (int) (sizeAndDensity * DOUBLE_TAP_SLOP + 0.5f);
         mWindowTouchSlop = (int) (sizeAndDensity * WINDOW_TOUCH_SLOP + 0.5f);
 
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index e38a55f..05f9da5 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -28,7 +28,6 @@
 import android.Manifest;
 import android.animation.LayoutTransition;
 import android.annotation.NonNull;
-import android.annotation.TestApi;
 import android.app.ActivityManager;
 import android.app.ActivityThread;
 import android.app.ResourcesManager;
@@ -214,11 +213,8 @@
 
     /**
      * Always assign focus if a focusable View is available.
-     *
-     * @hide
      */
-    @TestApi
-    public static boolean sAlwaysAssignFocus;
+    private static boolean sAlwaysAssignFocus;
 
     /**
      * This list must only be modified by the main thread, so a lock is only needed when changing
diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java
index 050aa4d..8e550dc 100644
--- a/core/java/android/view/WindowManager.java
+++ b/core/java/android/view/WindowManager.java
@@ -1751,6 +1751,13 @@
         public float buttonBrightness = BRIGHTNESS_OVERRIDE_NONE;
 
         /**
+         * Unspecified value for {@link #rotationAnimation} indicating
+         * a lack of preference.
+         * @hide
+         */
+        public static final int ROTATION_ANIMATION_UNSPECIFIED = -1;
+
+        /**
          * Value for {@link #rotationAnimation} which specifies that this
          * window will visually rotate in or out following a rotation.
          */
diff --git a/core/java/android/view/WindowManagerPolicy.java b/core/java/android/view/WindowManagerPolicy.java
index 7538f65..ba9e05c 100644
--- a/core/java/android/view/WindowManagerPolicy.java
+++ b/core/java/android/view/WindowManagerPolicy.java
@@ -150,6 +150,11 @@
     public final static int PRESENCE_INTERNAL = 1 << 0;
     public final static int PRESENCE_EXTERNAL = 1 << 1;
 
+    // Navigation bar position values
+    int NAV_BAR_LEFT = 1 << 0;
+    int NAV_BAR_RIGHT = 1 << 1;
+    int NAV_BAR_BOTTOM = 1 << 2;
+
     public final static boolean WATCH_POINTER = false;
 
     /**
@@ -1676,6 +1681,14 @@
     public boolean isNavBarForcedShownLw(WindowState win);
 
     /**
+     * @return The side of the screen where navigation bar is positioned.
+     * @see #NAV_BAR_LEFT
+     * @see #NAV_BAR_RIGHT
+     * @see #NAV_BAR_BOTTOM
+     */
+    int getNavBarPosition();
+
+    /**
      * Calculates the insets for the areas that could never be removed in Honeycomb, i.e. system
      * bar or button bar. See {@link #getNonDecorDisplayWidth}.
      *
diff --git a/core/java/android/view/textclassifier/TextClassification.java b/core/java/android/view/textclassifier/TextClassification.java
index b6dd0b9..d1e0ae5 100644
--- a/core/java/android/view/textclassifier/TextClassification.java
+++ b/core/java/android/view/textclassifier/TextClassification.java
@@ -50,11 +50,11 @@
     private int mLogType;
 
     private TextClassification(
-            @NonNull String text,
-            Drawable icon,
-            String label,
-            Intent intent,
-            OnClickListener onClickListener,
+            @Nullable String text,
+            @Nullable Drawable icon,
+            @Nullable String label,
+            @Nullable Intent intent,
+            @Nullable OnClickListener onClickListener,
             @NonNull EntityConfidence<String> entityConfidence,
             int logType) {
         mText = text;
@@ -70,7 +70,7 @@
     /**
      * Gets the classified text.
      */
-    @NonNull
+    @Nullable
     public String getText() {
         return mText;
     }
@@ -183,8 +183,8 @@
         /**
          * Sets the classified text.
          */
-        public Builder setText(@NonNull String text) {
-            mText = Preconditions.checkNotNull(text);
+        public Builder setText(@Nullable String text) {
+            mText = text;
             return this;
         }
 
@@ -197,7 +197,7 @@
          */
         public Builder setEntityType(
                 @NonNull @EntityType String type,
-                @FloatRange(from = 0.0, to = 1.0)float confidenceScore) {
+                @FloatRange(from = 0.0, to = 1.0) float confidenceScore) {
             mEntityConfidence.setEntityType(type, confidenceScore);
             return this;
         }
diff --git a/core/java/android/webkit/SafeBrowsingResponse.java b/core/java/android/webkit/SafeBrowsingResponse.java
index dc29d42..3540f80 100644
--- a/core/java/android/webkit/SafeBrowsingResponse.java
+++ b/core/java/android/webkit/SafeBrowsingResponse.java
@@ -29,19 +29,19 @@
      *
      * @param allowReporting True if the interstitial should show a reporting checkbox.
      */
-    abstract void showInterstitial(boolean allowReporting);
+    public abstract void showInterstitial(boolean allowReporting);
 
     /**
      * Act as if the user clicked "visit this unsafe site."
      *
      * @param report True to enable Safe Browsing reporting.
      */
-    abstract void proceed(boolean report);
+    public abstract void proceed(boolean report);
 
     /**
      * Act as if the user clicked "back to safety."
      *
      * @param report True to enable Safe Browsing reporting.
      */
-    abstract void backToSafety(boolean report);
+    public abstract void backToSafety(boolean report);
 }
diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java
index 5fed925..333c44c 100644
--- a/core/java/android/webkit/WebView.java
+++ b/core/java/android/webkit/WebView.java
@@ -67,6 +67,7 @@
 import java.io.File;
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
+import java.util.List;
 import java.util.Map;
 
 /**
@@ -1621,8 +1622,9 @@
     }
 
     /**
-     * Starts Safe Browsing initialization. This should only be called once.
-     * @param context is the activity context the WebView will be used in.
+     * Starts Safe Browsing initialization. This should only be called once. This does not require
+     * an Activity Context, and will always use the application Context to do its work.
+     * @param context Application Context.
      * @param callback will be called with the value true if initialization is
      * successful. The callback will be run on the UI thread.
      */
@@ -1643,9 +1645,13 @@
      * TODO: Add documentation for the format of the urls.
      *
      * @param urls the list of URLs
+     * @param callback will be called with true if URLs are successfully added to the whitelist. It
+     * will be called with false if any URLs are malformed. The callback will be run on the UI
+     * thread.
      */
-    public static void setSafeBrowsingWhiteList(@Nullable String[] urls) {
-        getFactory().getStatics().setSafeBrowsingWhiteList(urls);
+    public static void setSafeBrowsingWhitelist(@NonNull List<String> urls,
+            @Nullable ValueCallback<Boolean> callback) {
+        getFactory().getStatics().setSafeBrowsingWhitelist(urls, callback);
     }
 
     /**
diff --git a/core/java/android/webkit/WebViewClient.java b/core/java/android/webkit/WebViewClient.java
index da064d4..e7e539c 100644
--- a/core/java/android/webkit/WebViewClient.java
+++ b/core/java/android/webkit/WebViewClient.java
@@ -16,6 +16,7 @@
 
 package android.webkit;
 
+import android.annotation.IntDef;
 import android.graphics.Bitmap;
 import android.net.http.SslError;
 import android.os.Message;
@@ -23,6 +24,9 @@
 import android.view.KeyEvent;
 import android.view.ViewRootImpl;
 
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
 public class WebViewClient {
 
     /**
@@ -237,6 +241,16 @@
     /** Resource load was cancelled by Safe Browsing */
     public static final int ERROR_UNSAFE_RESOURCE = -16;
 
+    /** @hide */
+    @IntDef({
+        SAFE_BROWSING_THREAT_UNKNOWN,
+        SAFE_BROWSING_THREAT_MALWARE,
+        SAFE_BROWSING_THREAT_PHISHING,
+        SAFE_BROWSING_THREAT_UNWANTED_SOFTWARE
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface SafeBrowsingThreat {}
+
     /** The resource was blocked for an unknown reason */
     public static final int SAFE_BROWSING_THREAT_UNKNOWN = 0;
     /** The resource was blocked because it contains malware */
@@ -521,8 +535,8 @@
      *                   SAFE_BROWSING_THREAT_* value.
      * @param callback Applications must invoke one of the callback methods.
      */
-    public void onSafeBrowsingHit(WebView view, WebResourceRequest request, int threatType,
-            SafeBrowsingResponse callback) {
+    public void onSafeBrowsingHit(WebView view, WebResourceRequest request,
+            @SafeBrowsingThreat int threatType, SafeBrowsingResponse callback) {
         callback.showInterstitial(/* allowReporting */ true);
     }
 }
diff --git a/core/java/android/webkit/WebViewFactoryProvider.java b/core/java/android/webkit/WebViewFactoryProvider.java
index 7c938ae..613eb72 100644
--- a/core/java/android/webkit/WebViewFactoryProvider.java
+++ b/core/java/android/webkit/WebViewFactoryProvider.java
@@ -21,6 +21,8 @@
 import android.content.Intent;
 import android.net.Uri;
 
+import java.util.List;
+
 /**
  * This is the main entry-point into the WebView back end implementations, which the WebView
  * proxy class uses to instantiate all the other objects as needed. The backend must provide an
@@ -89,9 +91,10 @@
 
         /**
         * Implement the API method
-        * {@link android.webkit.WebView#setSafeBrowsingWhiteList(String[])}
+        * {@link android.webkit.WebView#setSafeBrowsingWhitelist(List<String>,
+        * ValueCallback<Boolean>)}
         */
-        void setSafeBrowsingWhiteList(String[] urls);
+        void setSafeBrowsingWhitelist(List<String> urls, ValueCallback<Boolean> callback);
     }
 
     Statics getStatics();
diff --git a/core/java/android/widget/AbsListView.java b/core/java/android/widget/AbsListView.java
index 5476ab2..91e2f7d 100644
--- a/core/java/android/widget/AbsListView.java
+++ b/core/java/android/widget/AbsListView.java
@@ -913,14 +913,7 @@
                 mCheckedIdStates = new LongSparseArray<Integer>();
             }
         }
-
-        if (mCheckStates != null) {
-            mCheckStates.clear();
-        }
-
-        if (mCheckedIdStates != null) {
-            mCheckedIdStates.clear();
-        }
+        clearChoices();
     }
 
     /**
diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java
index f21545f..04a8265 100644
--- a/core/java/android/widget/Editor.java
+++ b/core/java/android/widget/Editor.java
@@ -1387,7 +1387,7 @@
         if (mTextActionMode != null) {
             switch (event.getActionMasked()) {
                 case MotionEvent.ACTION_MOVE:
-                    hideFloatingToolbar();
+                    hideFloatingToolbar(ActionMode.DEFAULT_HIDE_DURATION);
                     break;
                 case MotionEvent.ACTION_UP:  // fall through
                 case MotionEvent.ACTION_CANCEL:
@@ -1396,10 +1396,10 @@
         }
     }
 
-    private void hideFloatingToolbar() {
+    void hideFloatingToolbar(int duration) {
         if (mTextActionMode != null) {
             mTextView.removeCallbacks(mShowFloatingToolbar);
-            mTextActionMode.hide(ActionMode.DEFAULT_HIDE_DURATION);
+            mTextActionMode.hide(duration);
         }
     }
 
diff --git a/core/java/android/widget/RelativeLayout.java b/core/java/android/widget/RelativeLayout.java
index 33e6521..75fc538 100644
--- a/core/java/android/widget/RelativeLayout.java
+++ b/core/java/android/widget/RelativeLayout.java
@@ -486,7 +486,7 @@
                         if (targetSdkVersion < Build.VERSION_CODES.KITKAT) {
                             width = Math.max(width, myWidth - params.mLeft);
                         } else {
-                            width = Math.max(width, myWidth - params.mLeft - params.leftMargin);
+                            width = Math.max(width, myWidth - params.mLeft + params.leftMargin);
                         }
                     } else {
                         if (targetSdkVersion < Build.VERSION_CODES.KITKAT) {
diff --git a/core/java/android/widget/SelectionActionModeHelper.java b/core/java/android/widget/SelectionActionModeHelper.java
index 142412a..3f4ce44 100644
--- a/core/java/android/widget/SelectionActionModeHelper.java
+++ b/core/java/android/widget/SelectionActionModeHelper.java
@@ -68,9 +68,7 @@
 
     public void startActionModeAsync(boolean adjustSelection) {
         cancelAsyncTask();
-        if (isNoOpTextClassifier() || !hasSelection()) {
-            // No need to make an async call for a no-op TextClassifier.
-            // Do not call the TextClassifier if there is no selection.
+        if (skipTextClassification()) {
             startActionMode(null);
         } else {
             resetTextClassificationHelper(true /* resetSelectionTag */);
@@ -88,9 +86,7 @@
 
     public void invalidateActionModeAsync() {
         cancelAsyncTask();
-        if (isNoOpTextClassifier() || !hasSelection()) {
-            // No need to make an async call for a no-op TextClassifier.
-            // Do not call the TextClassifier if there is no selection.
+        if (skipTextClassification()) {
             invalidateActionMode(null);
         } else {
             resetTextClassificationHelper(false /* resetSelectionTag */);
@@ -132,13 +128,16 @@
         mTextClassification = null;
     }
 
-    private boolean isNoOpTextClassifier() {
-        return mEditor.getTextView().getTextClassifier() == TextClassifier.NO_OP;
-    }
-
-    private boolean hasSelection() {
+    private boolean skipTextClassification() {
         final TextView textView = mEditor.getTextView();
-        return textView.getSelectionEnd() > textView.getSelectionStart();
+        // No need to make an async call for a no-op TextClassifier.
+        final boolean noOpTextClassifier = textView.getTextClassifier() == TextClassifier.NO_OP;
+        // Do not call the TextClassifier if there is no selection.
+        final boolean noSelection = textView.getSelectionEnd() == textView.getSelectionStart();
+        // Do not call the TextClassifier if this is a password field.
+        final boolean password = textView.hasPasswordTransformationMethod()
+                || TextView.isPasswordInputType(textView.getInputType());
+        return noOpTextClassifier || noSelection || password;
     }
 
     private void startActionMode(@Nullable SelectionResult result) {
diff --git a/core/java/android/widget/TextInputTimePickerView.java b/core/java/android/widget/TextInputTimePickerView.java
index 11b7514d..0cf8faa 100644
--- a/core/java/android/widget/TextInputTimePickerView.java
+++ b/core/java/android/widget/TextInputTimePickerView.java
@@ -17,6 +17,7 @@
 package android.widget;
 
 import android.content.Context;
+import android.os.LocaleList;
 import android.text.Editable;
 import android.text.InputFilter;
 import android.text.TextWatcher;
@@ -141,6 +142,9 @@
                 new InputFilter.LengthFilter(maxCharLength)});
         mMinuteEditText.setFilters(new InputFilter[] {
                 new InputFilter.LengthFilter(maxCharLength)});
+        final LocaleList locales = mContext.getResources().getConfiguration().getLocales();
+        mHourEditText.setImeHintLocales(locales);
+        mMinuteEditText.setImeHintLocales(locales);
     }
 
     boolean validateInput() {
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index 9a92489..69edbbb 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -374,6 +374,8 @@
     private static final int KEY_DOWN_HANDLED_BY_KEY_LISTENER = 1;
     private static final int KEY_DOWN_HANDLED_BY_MOVEMENT_METHOD = 2;
 
+    private static final int FLOATING_TOOLBAR_SELECT_ALL_REFRESH_DELAY = 500;
+
     // System wide time for last cut, copy or text changed action.
     static long sLastCutCopyOrTextChangedTime;
 
@@ -5674,7 +5676,7 @@
         return mTransformation instanceof PasswordTransformationMethod;
     }
 
-    private static boolean isPasswordInputType(int inputType) {
+    static boolean isPasswordInputType(int inputType) {
         final int variation =
                 inputType & (EditorInfo.TYPE_MASK_CLASS | EditorInfo.TYPE_MASK_VARIATION);
         return variation
@@ -11138,6 +11140,10 @@
     }
 
     boolean selectAllText() {
+        if (mEditor != null) {
+            // Hide the toolbar before changing the selection to avoid flickering.
+            mEditor.hideFloatingToolbar(FLOATING_TOOLBAR_SELECT_ALL_REFRESH_DELAY);
+        }
         final int length = mText.length();
         Selection.setSelection((Spannable) mText, 0, length);
         return length > 0;
diff --git a/core/java/com/android/internal/app/MediaRouteControllerDialog.java b/core/java/com/android/internal/app/MediaRouteControllerDialog.java
index 5ce3e54..3341df9 100644
--- a/core/java/com/android/internal/app/MediaRouteControllerDialog.java
+++ b/core/java/com/android/internal/app/MediaRouteControllerDialog.java
@@ -72,6 +72,7 @@
     private boolean mVolumeSliderTouched;
 
     private View mControlView;
+    private boolean mAttachedToWindow;
 
     public MediaRouteControllerDialog(Context context, int theme) {
         super(context, theme);
@@ -211,6 +212,7 @@
     @Override
     public void onAttachedToWindow() {
         super.onAttachedToWindow();
+        mAttachedToWindow = true;
 
         mRouter.addCallback(0, mCallback, MediaRouter.CALLBACK_FLAG_UNFILTERED_EVENTS);
         update();
@@ -219,6 +221,7 @@
     @Override
     public void onDetachedFromWindow() {
         mRouter.removeCallback(mCallback);
+        mAttachedToWindow = false;
 
         super.onDetachedFromWindow();
     }
@@ -256,7 +259,14 @@
             mCurrentIconDrawable = icon;
             if (icon instanceof AnimationDrawable) {
                 AnimationDrawable animDrawable = (AnimationDrawable) icon;
-                if (!animDrawable.isRunning()) {
+                if (!mAttachedToWindow && !mRoute.isConnecting()) {
+                    // When the route is already connected before the view is attached, show the
+                    // last frame of the connected animation immediately.
+                    if (animDrawable.isRunning()) {
+                        animDrawable.stop();
+                    }
+                    icon = animDrawable.getFrame(animDrawable.getNumberOfFrames() - 1);
+                } else if (!animDrawable.isRunning()) {
                     animDrawable.start();
                 }
             }
diff --git a/core/java/com/android/internal/app/ResolverComparator.java b/core/java/com/android/internal/app/ResolverComparator.java
index a0f58a9..378826d 100644
--- a/core/java/com/android/internal/app/ResolverComparator.java
+++ b/core/java/com/android/internal/app/ResolverComparator.java
@@ -30,6 +30,7 @@
 import android.content.pm.ResolveInfo;
 import android.content.SharedPreferences;
 import android.content.ServiceConnection;
+import android.metrics.LogMaker;
 import android.os.Environment;
 import android.os.Handler;
 import android.os.IBinder;
@@ -46,6 +47,8 @@
 import android.util.ArrayMap;
 import android.util.Log;
 import com.android.internal.app.ResolverActivity.ResolvedComponentInfo;
+import com.android.internal.logging.MetricsLogger;
+import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
 
 import java.io.File;
 import java.lang.InterruptedException;
@@ -99,6 +102,8 @@
     private String mContentType;
     private String[] mAnnotations;
     private String mAction;
+    private ComponentName mResolvedRankerName;
+    private ComponentName mRankerServiceName;
     private IResolverRankerService mRanker;
     private ResolverRankerServiceConnection mConnection;
     private AfterCompute mAfterCompute;
@@ -119,9 +124,17 @@
                             if (receivedTargets != null && mTargets != null
                                     && receivedTargets.size() == mTargets.size()) {
                                 final int size = mTargets.size();
+                                boolean isUpdated = false;
                                 for (int i = 0; i < size; ++i) {
-                                    mTargets.get(i).setSelectProbability(
-                                            receivedTargets.get(i).getSelectProbability());
+                                    final float predictedProb =
+                                            receivedTargets.get(i).getSelectProbability();
+                                    if (predictedProb != mTargets.get(i).getSelectProbability()) {
+                                        mTargets.get(i).setSelectProbability(predictedProb);
+                                        isUpdated = true;
+                                    }
+                                }
+                                if (isUpdated) {
+                                    mRankerServiceName = mResolvedRankerName;
                                 }
                             } else {
                                 Log.e(TAG, "Sizes of sent and received ResolverTargets diff.");
@@ -170,6 +183,7 @@
         mContentType = intent.getType();
         getContentAnnotations(intent);
         mAction = intent.getAction();
+        mRankerServiceName = new ComponentName(mContext, this.getClass());
     }
 
     // get annotations of content from intent.
@@ -361,7 +375,15 @@
                 try {
                     int selectedPos = new ArrayList<ComponentName>(mTargetsDict.keySet())
                             .indexOf(componentName);
-                    if (selectedPos > 0) {
+                    if (selectedPos >= 0 && mTargets != null) {
+                        final float selectedProbability = getScore(componentName);
+                        int order = 0;
+                        for (ResolverTarget target : mTargets) {
+                            if (target.getSelectProbability() > selectedProbability) {
+                                order++;
+                            }
+                        }
+                        logMetrics(order);
                         mRanker.train(mTargets, selectedPos);
                     } else {
                         if (DEBUG) {
@@ -392,6 +414,19 @@
         }
     }
 
+    // records metrics for evaluation.
+    private void logMetrics(int selectedPos) {
+        if (mRankerServiceName != null) {
+            MetricsLogger metricsLogger = new MetricsLogger();
+            LogMaker log = new LogMaker(MetricsEvent.ACTION_TARGET_SELECTED);
+            log.setComponentName(mRankerServiceName);
+            int isCategoryUsed = (mAnnotations == null) ? 0 : 1;
+            log.addTaggedData(MetricsEvent.FIELD_IS_CATEGORY_USED, isCategoryUsed);
+            log.addTaggedData(MetricsEvent.FIELD_RANKED_POSITION, selectedPos);
+            metricsLogger.write(log);
+        }
+    }
+
     // connect to a ranking service.
     private void initRanker(Context context) {
         synchronized (mLock) {
@@ -454,6 +489,7 @@
             if (DEBUG) {
                 Log.d(TAG, "Succeeded to retrieve a ranker: " + componentName);
             }
+            mResolvedRankerName = componentName;
             intent.setComponent(componentName);
             return intent;
         }
@@ -523,6 +559,8 @@
     private void reset() {
         mTargetsDict.clear();
         mTargets = null;
+        mRankerServiceName = new ComponentName(mContext, this.getClass());
+        mResolvedRankerName = null;
         startWatchDog(WATCHDOG_TIMEOUT_MILLIS);
         initRanker(mContext);
     }
diff --git a/core/java/com/android/internal/colorextraction/ColorExtractor.java b/core/java/com/android/internal/colorextraction/ColorExtractor.java
index 2608698..2648604 100644
--- a/core/java/com/android/internal/colorextraction/ColorExtractor.java
+++ b/core/java/com/android/internal/colorextraction/ColorExtractor.java
@@ -43,10 +43,6 @@
 
     private static final String TAG = "ColorExtractor";
 
-    public static final int FALLBACK_COLOR = 0xff83888d;
-
-    private int mMainFallbackColor = FALLBACK_COLOR;
-    private int mSecondaryFallbackColor = FALLBACK_COLOR;
     private final SparseArray<GradientColors[]> mGradientColors;
     private final ArrayList<OnColorsChangedListener> mOnColorsChangedListeners;
     private final Context mContext;
@@ -55,7 +51,7 @@
     private WallpaperColors mLockColors;
 
     public ColorExtractor(Context context) {
-        this(context, new Tonal());
+        this(context, new Tonal(context));
     }
 
     @VisibleForTesting
@@ -73,6 +69,9 @@
         }
 
         mOnColorsChangedListeners = new ArrayList<>();
+        GradientColors[] systemColors = mGradientColors.get(WallpaperManager.FLAG_SYSTEM);
+        GradientColors[] lockColors = mGradientColors.get(WallpaperManager.FLAG_LOCK);
+
         WallpaperManager wallpaperManager = mContext.getSystemService(WallpaperManager.class);
         if (wallpaperManager == null) {
             Log.w(TAG, "Can't listen to color changes!");
@@ -83,23 +82,18 @@
             Trace.beginSection("ColorExtractor#getWallpaperColors");
             mSystemColors = wallpaperManager.getWallpaperColors(WallpaperManager.FLAG_SYSTEM);
             mLockColors = wallpaperManager.getWallpaperColors(WallpaperManager.FLAG_LOCK);
-
-            GradientColors[] systemColors = mGradientColors.get(
-                    WallpaperManager.FLAG_SYSTEM);
-            extractInto(mSystemColors,
-                    systemColors[TYPE_NORMAL],
-                    systemColors[TYPE_DARK],
-                    systemColors[TYPE_EXTRA_DARK]);
-
-            GradientColors[] lockColors = mGradientColors.get(WallpaperManager.FLAG_LOCK);
-            extractInto(mLockColors,
-                    lockColors[TYPE_NORMAL],
-                    lockColors[TYPE_DARK],
-                    lockColors[TYPE_EXTRA_DARK]);
-            triggerColorsChanged(WallpaperManager.FLAG_SYSTEM
-                    | WallpaperManager.FLAG_LOCK);
             Trace.endSection();
         }
+
+        // Initialize all gradients with the current colors
+        extractInto(mSystemColors,
+                systemColors[TYPE_NORMAL],
+                systemColors[TYPE_DARK],
+                systemColors[TYPE_EXTRA_DARK]);
+        extractInto(mLockColors,
+                lockColors[TYPE_NORMAL],
+                lockColors[TYPE_DARK],
+                lockColors[TYPE_EXTRA_DARK]);
     }
 
     /**
@@ -181,25 +175,8 @@
     private void extractInto(WallpaperColors inWallpaperColors,
             GradientColors outGradientColorsNormal, GradientColors outGradientColorsDark,
             GradientColors outGradientColorsExtraDark) {
-        if (inWallpaperColors == null) {
-            applyFallback(outGradientColorsNormal);
-            applyFallback(outGradientColorsDark);
-            applyFallback(outGradientColorsExtraDark);
-            return;
-        }
-        boolean success = mExtractionType.extractInto(inWallpaperColors, outGradientColorsNormal,
+        mExtractionType.extractInto(inWallpaperColors, outGradientColorsNormal,
                 outGradientColorsDark, outGradientColorsExtraDark);
-        if (!success) {
-            applyFallback(outGradientColorsNormal);
-            applyFallback(outGradientColorsDark);
-            applyFallback(outGradientColorsExtraDark);
-        }
-    }
-
-    private void applyFallback(GradientColors outGradientColors) {
-        outGradientColors.setMainColor(mMainFallbackColor);
-        outGradientColors.setSecondaryColor(mSecondaryFallbackColor);
-        outGradientColors.setSupportsDarkText(false);
     }
 
     public void destroy() {
@@ -218,8 +195,8 @@
     }
 
     public static class GradientColors {
-        private int mMainColor = FALLBACK_COLOR;
-        private int mSecondaryColor = FALLBACK_COLOR;
+        private int mMainColor;
+        private int mSecondaryColor;
         private boolean mSupportsDarkText;
 
         public void setMainColor(int mainColor) {
diff --git a/core/java/com/android/internal/colorextraction/types/ExtractionType.java b/core/java/com/android/internal/colorextraction/types/ExtractionType.java
index 762b54f..7000e79 100644
--- a/core/java/com/android/internal/colorextraction/types/ExtractionType.java
+++ b/core/java/com/android/internal/colorextraction/types/ExtractionType.java
@@ -38,9 +38,8 @@
      * @param outGradientColorsNormal object that should receive normal colors
      * @param outGradientColorsDark object that should receive dark colors
      * @param outGradientColorsExtraDark object that should receive extra dark colors
-     * @return true if successful.
      */
-    boolean extractInto(WallpaperColors inWallpaperColors,
+    void extractInto(WallpaperColors inWallpaperColors,
             ColorExtractor.GradientColors outGradientColorsNormal,
             ColorExtractor.GradientColors outGradientColorsDark,
             ColorExtractor.GradientColors outGradientColorsExtraDark);
diff --git a/core/java/com/android/internal/colorextraction/types/Tonal.java b/core/java/com/android/internal/colorextraction/types/Tonal.java
index b8ebe30..dbc086c 100644
--- a/core/java/com/android/internal/colorextraction/types/Tonal.java
+++ b/core/java/com/android/internal/colorextraction/types/Tonal.java
@@ -19,15 +19,22 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.app.WallpaperColors;
+import android.content.Context;
 import android.graphics.Color;
 import android.util.Log;
 import android.util.MathUtils;
 import android.util.Range;
 
+import com.android.internal.R;
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.colorextraction.ColorExtractor.GradientColors;
 import com.android.internal.graphics.ColorUtils;
 
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.IOException;
+import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.List;
 
@@ -44,24 +51,68 @@
 
     private static final boolean DEBUG = true;
 
+    public static final int MAIN_COLOR_LIGHT = 0xffe0e0e0;
+    public static final int SECONDARY_COLOR_LIGHT = 0xff9e9e9e;
+    public static final int MAIN_COLOR_DARK = 0xff212121;
+    public static final int SECONDARY_COLOR_DARK = 0xff000000;
+
+    private final TonalPalette mGreyPalette;
+    private final ArrayList<TonalPalette> mTonalPalettes;
+    private final ArrayList<ColorRange> mBlacklistedColors;
+
     // Temporary variable to avoid allocations
     private float[] mTmpHSL = new float[3];
 
+    public Tonal(Context context) {
+
+        ConfigParser parser = new ConfigParser(context);
+        mTonalPalettes = parser.getTonalPalettes();
+        mBlacklistedColors = parser.getBlacklistedColors();
+
+        mGreyPalette = mTonalPalettes.get(0);
+        mTonalPalettes.remove(0);
+    }
+
     /**
-     * Grab colors from WallpaperColors as set them into GradientColors
+     * Grab colors from WallpaperColors and set them into GradientColors.
+     * Also applies the default gradient in case extraction fails.
      *
-     * @param inWallpaperColors input
-     * @param outColorsNormal colors for normal theme
-     * @param outColorsDark colors for dar theme
-     * @param outColorsExtraDark colors for extra dark theme
-     * @return true if successful
+     * @param inWallpaperColors Input.
+     * @param outColorsNormal Colors for normal theme.
+     * @param outColorsDark Colors for dar theme.
+     * @param outColorsExtraDark Colors for extra dark theme.
      */
-    public boolean extractInto(@NonNull WallpaperColors inWallpaperColors,
+    public void extractInto(@Nullable WallpaperColors inWallpaperColors,
+            @NonNull GradientColors outColorsNormal, @NonNull GradientColors outColorsDark,
+            @NonNull GradientColors outColorsExtraDark) {
+        boolean success = runTonalExtraction(inWallpaperColors, outColorsNormal, outColorsDark,
+                outColorsExtraDark);
+        if (!success) {
+            applyFallback(inWallpaperColors, outColorsNormal, outColorsDark, outColorsExtraDark);
+        }
+    }
+
+    /**
+     * Grab colors from WallpaperColors and set them into GradientColors.
+     *
+     * @param inWallpaperColors Input.
+     * @param outColorsNormal Colors for normal theme.
+     * @param outColorsDark Colors for dar theme.
+     * @param outColorsExtraDark Colors for extra dark theme.
+     * @return True if succeeded or false if failed.
+     */
+    private boolean runTonalExtraction(@Nullable WallpaperColors inWallpaperColors,
             @NonNull GradientColors outColorsNormal, @NonNull GradientColors outColorsDark,
             @NonNull GradientColors outColorsExtraDark) {
 
+        if (inWallpaperColors == null) {
+            return false;
+        }
+
         final List<Color> mainColors = inWallpaperColors.getMainColors();
         final int mainColorsSize = mainColors.size();
+        final boolean supportsDarkText = (inWallpaperColors.getColorHints() &
+                WallpaperColors.HINT_SUPPORTS_DARK_TEXT) != 0;
 
         if (mainColorsSize == 0) {
             return false;
@@ -120,7 +171,6 @@
         float[] s = fit(palette.s, hsl[1], fitIndex, 0.0f, 1.0f);
         float[] l = fit(palette.l, hsl[2], fitIndex, 0.0f, 1.0f);
 
-        final int textInversionIndex = h.length - 3;
         if (DEBUG) {
             StringBuilder builder = new StringBuilder("Tonal Palette - index: " + fitIndex +
                     ". Main color: " + Integer.toHexString(getColorInt(fitIndex, h, s, l)) +
@@ -135,21 +185,38 @@
             Log.d(TAG, builder.toString());
         }
 
+        int primaryIndex = fitIndex;
+        int mainColor = getColorInt(primaryIndex, h, s, l);
+
+        // We might want use the fallback in case the extracted color is brighter than our
+        // light fallback or darker than our dark fallback.
+        ColorUtils.colorToHSL(mainColor, mTmpHSL);
+        final float mainLuminosity = mTmpHSL[2];
+        ColorUtils.colorToHSL(MAIN_COLOR_LIGHT, mTmpHSL);
+        final float lightLuminosity = mTmpHSL[2];
+        if (mainLuminosity > lightLuminosity) {
+            return false;
+        }
+        ColorUtils.colorToHSL(MAIN_COLOR_DARK, mTmpHSL);
+        final float darkLuminosity = mTmpHSL[2];
+        if (mainLuminosity < darkLuminosity) {
+            return false;
+        }
+
         // Normal colors:
         // best fit + a 2 colors offset
-        int primaryIndex = fitIndex;
+        outColorsNormal.setMainColor(mainColor);
         int secondaryIndex = primaryIndex + (primaryIndex >= 2 ? -2 : 2);
-        outColorsNormal.setMainColor(getColorInt(primaryIndex, h, s, l));
         outColorsNormal.setSecondaryColor(getColorInt(secondaryIndex, h, s, l));
 
         // Dark colors:
         // Stops at 4th color, only lighter if dark text is supported
-        if (fitIndex < 2) {
-            primaryIndex = 0;
-        } else if (fitIndex < textInversionIndex) {
-            primaryIndex = Math.min(fitIndex, 3);
-        } else {
+        if (supportsDarkText) {
             primaryIndex = h.length - 1;
+        } else if (fitIndex < 2) {
+            primaryIndex = 0;
+        } else {
+            primaryIndex = Math.min(fitIndex, 3);
         }
         secondaryIndex = primaryIndex + (primaryIndex >= 2 ? -2 : 2);
         outColorsDark.setMainColor(getColorInt(primaryIndex, h, s, l));
@@ -157,30 +224,56 @@
 
         // Extra Dark:
         // Stay close to dark colors until dark text is supported
-        if (fitIndex < 2) {
-            primaryIndex = 0;
-        } else if (fitIndex < textInversionIndex) {
-            primaryIndex = 2;
-        } else {
+        if (supportsDarkText) {
             primaryIndex = h.length - 1;
+        } else if (fitIndex < 2) {
+            primaryIndex = 0;
+        } else {
+            primaryIndex = 2;
         }
         secondaryIndex = primaryIndex + (primaryIndex >= 2 ? -2 : 2);
         outColorsExtraDark.setMainColor(getColorInt(primaryIndex, h, s, l));
         outColorsExtraDark.setSecondaryColor(getColorInt(secondaryIndex, h, s, l));
 
-        final boolean supportsDarkText = fitIndex >= textInversionIndex;
         outColorsNormal.setSupportsDarkText(supportsDarkText);
         outColorsDark.setSupportsDarkText(supportsDarkText);
         outColorsExtraDark.setSupportsDarkText(supportsDarkText);
 
         if (DEBUG) {
             Log.d(TAG, "Gradients: \n\tNormal " + outColorsNormal + "\n\tDark " + outColorsDark
-            + "\n\tExtra dark: " + outColorsExtraDark);
+                    + "\n\tExtra dark: " + outColorsExtraDark);
         }
 
         return true;
     }
 
+    private void applyFallback(@Nullable WallpaperColors inWallpaperColors,
+            GradientColors outColorsNormal, GradientColors outColorsDark,
+            GradientColors outColorsExtraDark) {
+        applyFallback(inWallpaperColors, outColorsNormal);
+        applyFallback(inWallpaperColors, outColorsDark);
+        applyFallback(inWallpaperColors, outColorsExtraDark);
+    }
+
+    /**
+     * Sets the gradient to the light or dark fallbacks based on the current wallpaper colors.
+     *
+     * @param inWallpaperColors Colors to read.
+     * @param outGradientColors Destination.
+     */
+    public static void applyFallback(@Nullable WallpaperColors inWallpaperColors,
+            @NonNull GradientColors outGradientColors) {
+        boolean light = inWallpaperColors != null
+                && (inWallpaperColors.getColorHints() & WallpaperColors.HINT_SUPPORTS_DARK_TEXT)
+                != 0;
+        int innerColor = light ? MAIN_COLOR_LIGHT : MAIN_COLOR_DARK;
+        int outerColor = light ? SECONDARY_COLOR_LIGHT : SECONDARY_COLOR_DARK;
+
+        outGradientColors.setMainColor(innerColor);
+        outGradientColors.setSecondaryColor(outerColor);
+        outGradientColors.setSupportsDarkText(light);
+    }
+
     private int getColorInt(int fitIndex, float[] h, float[] s, float[] l) {
         mTmpHSL[0] = fract(h[fitIndex]) * 360.0f;
         mTmpHSL[1] = s[fitIndex];
@@ -194,7 +287,8 @@
      * @return true if color should be avoided
      */
     private boolean isBlacklisted(float[] hsl) {
-        for (ColorRange badRange: BLACKLISTED_COLORS) {
+        for (int i = mBlacklistedColors.size() - 1; i >= 0; i--) {
+            ColorRange badRange = mBlacklistedColors.get(i);
             if (badRange.containsColor(hsl[0], hsl[1], hsl[2])) {
                 return true;
             }
@@ -250,19 +344,25 @@
         return minErrorIndex;
     }
 
+    @VisibleForTesting
+    public List<ColorRange> getBlacklistedColors() {
+        return mBlacklistedColors;
+    }
+
     @Nullable
-    private static TonalPalette findTonalPalette(float h, float s) {
+    private TonalPalette findTonalPalette(float h, float s) {
         // Fallback to a grey palette if the color is too desaturated.
         // This avoids hue shifts.
         if (s < 0.05f) {
-            return GREY_PALETTE;
+            return mGreyPalette;
         }
 
         TonalPalette best = null;
         float error = Float.POSITIVE_INFINITY;
 
-        for (int i = 0; i < TONAL_PALETTES.length; i++) {
-            final TonalPalette candidate = TONAL_PALETTES[i];
+        final int tonalPalettesCount = mTonalPalettes.size();
+        for (int i = 0; i < tonalPalettesCount; i++) {
+            final TonalPalette candidate = mTonalPalettes.get(i);
 
             if (h >= candidate.minHue && h <= candidate.maxHue) {
                 best = candidate;
@@ -316,7 +416,6 @@
                         + Arrays.toString(h) + " s: " + Arrays.toString(s) + " l: "
                         + Arrays.toString(l));
             }
-
             this.h = h;
             this.s = s;
             this.l = l;
@@ -334,430 +433,6 @@
         }
     }
 
-    // Data definition of Material Design tonal palettes
-    // When the sort type is set to TONAL, these palettes are used to find
-    // a best fit. Each palette is defined as 22 HSL colors
-    private static final TonalPalette[] TONAL_PALETTES = {
-            new TonalPalette(
-                    new float[] {1f, 1f, 0.991f, 0.991f, 0.9833333333333333f, 0f, 0f, 0f,
-                            0.01134380453752181f, 0.015625000000000003f, 0.024193548387096798f,
-                            0.027397260273972573f, 0.017543859649122865f},
-                    new float[] {1f, 1f, 1f, 1f, 1f, 1f, 1f, 0.8434782608695652f, 1f, 1f, 1f, 1f,
-                            1f},
-                    new float[] {0.04f, 0.09f, 0.14f, 0.2f, 0.27450980392156865f,
-                            0.34901960784313724f, 0.4235294117647059f, 0.5490196078431373f,
-                            0.6254901960784314f, 0.6862745098039216f, 0.7568627450980392f,
-                            0.8568627450980393f, 0.9254901960784314f}
-            ),
-            new TonalPalette(
-                    new float[] {0.638f, 0.638f, 0.6385767790262171f, 0.6301169590643275f,
-                            0.6223958333333334f, 0.6151079136690647f, 0.6065400843881856f,
-                            0.5986964618249534f, 0.5910746812386157f, 0.5833333333333334f,
-                            0.5748031496062993f, 0.5582010582010583f},
-                    new float[] {1f, 1f, 1f, 1f, 0.9014084507042253f, 0.8128654970760234f,
-                            0.7979797979797981f, 0.7816593886462883f, 0.778723404255319f, 1f, 1f,
-                            1f},
-                    new float[] {0.05f, 0.12f, 0.17450980392156862f, 0.2235294117647059f,
-                            0.2784313725490196f, 0.3352941176470588f, 0.388235294117647f,
-                            0.44901960784313727f, 0.5392156862745098f, 0.6509803921568628f,
-                            0.7509803921568627f, 0.8764705882352941f}
-            ),
-            new TonalPalette(
-                    new float[] {0.563f, 0.569f, 0.5666f, 0.5669934640522876f, 0.5748031496062993f,
-                            0.5595238095238095f, 0.5473118279569893f, 0.5393258426966292f,
-                            0.5315955766192734f, 0.524031007751938f, 0.5154711673699016f,
-                            0.508080808080808f, 0.5f},
-                    new float[] {1f, 1f, 1f, 1f, 1f, 1f, 1f, 1f, 1f, 0.8847736625514403f, 1f, 1f,
-                            1f},
-                    new float[] {0.07f, 0.12f, 0.16f, 0.2f, 0.24901960784313726f,
-                            0.27450980392156865f, 0.30392156862745096f, 0.34901960784313724f,
-                            0.4137254901960784f, 0.47647058823529415f, 0.5352941176470588f,
-                            0.6764705882352942f, 0.8f}
-            ),
-            new TonalPalette(
-                    new float[] {0.508f, 0.511f, 0.508f, 0.508f, 0.5082304526748972f,
-                            0.5069444444444444f, 0.5f, 0.5f, 0.5f, 0.48724954462659376f,
-                            0.4800347222222222f, 0.4755134281200632f, 0.4724409448818897f,
-                            0.4671052631578947f},
-                    new float[] {1f, 1f, 1f, 1f, 1f, 0.8888888888888887f, 0.9242424242424242f, 1f,
-                            1f, 0.8133333333333332f, 0.7868852459016393f, 1f, 1f, 1f},
-                    new float[] {0.04f, 0.06f, 0.08f, 0.12f, 0.1588235294117647f,
-                            0.21176470588235297f, 0.25882352941176473f, 0.3f, 0.34901960784313724f,
-                            0.44117647058823534f, 0.5215686274509804f, 0.5862745098039216f,
-                            0.7509803921568627f, 0.8509803921568627f}
-            ),
-            new TonalPalette(
-                    new float[] {0.333f, 0.333f, 0.333f, 0.3333333333333333f, 0.3333333333333333f,
-                            0.34006734006734f, 0.34006734006734f, 0.34006734006734f,
-                            0.34259259259259256f, 0.3475783475783476f, 0.34767025089605735f,
-                            0.3467741935483871f, 0.3703703703703704f},
-                    new float[] {0.70f, 0.72f, 0.69f, 0.6703296703296703f, 0.728813559322034f,
-                            0.5657142857142856f, 0.5076923076923077f, 0.3944223107569721f,
-                            0.6206896551724138f, 0.8931297709923666f, 1f, 1f, 1f},
-                    new float[] {0.05f, 0.08f, 0.14f, 0.1784313725490196f, 0.23137254901960785f,
-                            0.3431372549019608f, 0.38235294117647056f, 0.49215686274509807f,
-                            0.6588235294117647f, 0.7431372549019608f, 0.8176470588235294f,
-                            0.8784313725490196f, 0.9294117647058824f}
-            ),
-            new TonalPalette(
-                    new float[] {0.161f, 0.163f, 0.163f, 0.162280701754386f, 0.15032679738562088f,
-                            0.15879265091863518f, 0.16236559139784948f, 0.17443868739205526f,
-                            0.17824074074074076f, 0.18674698795180725f, 0.18692449355432778f,
-                            0.1946778711484594f, 0.18604651162790695f},
-                    new float[] {1f, 1f, 1f, 1f, 1f, 1f, 1f, 1f, 1f, 1f, 1f, 1f, 1f},
-                    new float[] {0.05f, 0.08f, 0.11f, 0.14901960784313725f, 0.2f,
-                            0.24901960784313726f, 0.30392156862745096f, 0.3784313725490196f,
-                            0.4235294117647059f, 0.48823529411764705f, 0.6450980392156863f,
-                            0.7666666666666666f, 0.8313725490196078f}
-            ),
-            new TonalPalette(
-                    new float[] {0.108f, 0.105f, 0.105f, 0.105f, 0.10619469026548674f,
-                            0.11924686192468618f, 0.13046448087431692f, 0.14248366013071895f,
-                            0.1506024096385542f, 0.16220238095238093f, 0.16666666666666666f,
-                            0.16666666666666666f, 0.162280701754386f, 0.15686274509803924f},
-                    new float[] {1f, 1f, 1f, 1f, 1f, 1f, 1f, 1f, 1f, 1f, 1f, 1f, 1f, 1f},
-                    new float[] {0.17f, 0.22f, 0.28f, 0.35f, 0.44313725490196076f,
-                            0.46862745098039216f, 0.47843137254901963f, 0.5f, 0.5117647058823529f,
-                            0.5607843137254902f, 0.6509803921568628f, 0.7509803921568627f,
-                            0.8509803921568627f, 0.9f}
-            ),
-            new TonalPalette(
-                    new float[] {0.036f, 0.036f, 0.036f, 0.036f, 0.03561253561253561f,
-                            0.05098039215686275f, 0.07516339869281045f, 0.09477124183006536f,
-                            0.1150326797385621f, 0.134640522875817f, 0.14640522875816991f,
-                            0.1582397003745319f, 0.15773809523809523f, 0.15359477124183002f},
-                    new float[] {1f, 1f, 1f, 1f, 1f, 1f, 1f, 1f, 1f, 1f, 1f, 1f, 1f, 1f},
-                    new float[] {0.19f, 0.26f, 0.34f, 0.39f, 0.4588235294117647f, 0.5f, 0.5f, 0.5f,
-                            0.5f, 0.5f, 0.5f, 0.6509803921568628f, 0.7803921568627451f, 0.9f}
-            ),
-            new TonalPalette(
-                    new float[] {0.955f, 0.961f, 0.958f, 0.9596491228070175f, 0.9593837535014005f,
-                            0.9514767932489452f, 0.943859649122807f, 0.9396825396825397f,
-                            0.9395424836601307f, 0.9393939393939394f, 0.9362745098039216f,
-                            0.9754098360655739f, 0.9824561403508771f},
-                    new float[] {0.87f, 0.85f, 0.85f, 0.84070796460177f, 0.8206896551724138f,
-                            0.7979797979797981f, 0.7661290322580644f, 0.9051724137931036f,
-                            1f, 1f, 1f, 1f, 1f},
-                    new float[] {0.06f, 0.11f, 0.16f, 0.22156862745098038f, 0.2843137254901961f,
-                            0.388235294117647f, 0.48627450980392156f, 0.5450980392156863f,
-                            0.6f, 0.6764705882352942f, 0.8f, 0.8803921568627451f,
-                            0.9254901960784314f}
-            ),
-            new TonalPalette(
-                    new float[] {0.866f, 0.855f, 0.841025641025641f, 0.8333333333333334f,
-                            0.8285256410256411f, 0.821522309711286f, 0.8083333333333333f,
-                            0.8046594982078853f, 0.8005822416302766f, 0.7842377260981912f,
-                            0.7771084337349398f, 0.7747747747747749f},
-                    new float[] {1f, 1f, 1f, 1f, 1f, 1f, 1f, 1f, 1f,
-                            0.737142857142857f, 0.6434108527131781f, 0.46835443037974644f},
-                    new float[] {0.05f, 0.08f, 0.12745098039215685f, 0.15490196078431373f,
-                            0.20392156862745098f, 0.24901960784313726f, 0.3137254901960784f,
-                            0.36470588235294116f, 0.44901960784313727f, 0.6568627450980392f,
-                            0.7470588235294118f, 0.8450980392156863f}
-            ),
-            new TonalPalette(
-                    new float[] {0.925f, 0.93f, 0.938f, 0.947f, 0.955952380952381f,
-                            0.9681069958847737f, 0.9760479041916167f, 0.9873563218390804f, 0f, 0f,
-                            0.009057971014492771f, 0.026748971193415648f,
-                            0.041666666666666616f, 0.05303030303030304f},
-                    new float[] {1f, 1f, 1f, 1f, 1f, 0.8350515463917526f, 0.6929460580912863f,
-                            0.6387665198237885f, 0.6914893617021276f, 0.7583892617449666f,
-                            0.8070175438596495f, 0.9310344827586209f, 1f, 1f},
-                    new float[] {0.10f, 0.13f, 0.17f, 0.2f, 0.27450980392156865f,
-                            0.3803921568627451f, 0.4725490196078432f, 0.5549019607843138f,
-                            0.6313725490196078f, 0.707843137254902f, 0.7764705882352941f,
-                            0.8294117647058823f, 0.9058823529411765f, 0.9568627450980391f}
-            ),
-            new TonalPalette(
-                    new float[] {0.733f, 0.736f, 0.744f, 0.7514619883040936f, 0.7679738562091503f,
-                            0.7802083333333333f, 0.7844311377245509f, 0.796875f,
-                            0.8165618448637316f, 0.8487179487179487f, 0.8582375478927203f,
-                            0.8562091503267975f, 0.8666666666666667f},
-                    new float[] {1f, 1f, 1f, 1f, 1f, 0.8163265306122449f, 0.6653386454183268f,
-                            0.7547169811320753f, 0.929824561403509f, 0.9558823529411766f,
-                            0.9560439560439562f, 1f, 1f},
-                    new float[] {0.07f, 0.12f, 0.17f, 0.2235294117647059f, 0.3f,
-                            0.38431372549019605f, 0.492156862745098f, 0.5843137254901961f,
-                            0.6647058823529411f, 0.7333333333333334f, 0.8215686274509804f, 0.9f,
-                            0.9411764705882353f}
-            ),
-            new TonalPalette(
-                    new float[] {0.6666666666666666f, 0.6666666666666666f, 0.6666666666666666f,
-                            0.6666666666666666f, 0.6666666666666666f, 0.6666666666666666f,
-                            0.6666666666666666f, 0.6666666666666666f, 0.6666666666666666f,
-                            0.6666666666666666f, 0.6666666666666666f},
-                    new float[] {0.25f, 0.24590163934426232f, 0.17880794701986752f,
-                            0.14606741573033713f, 0.13761467889908252f, 0.14893617021276592f,
-                            0.16756756756756758f, 0.20312500000000017f, 0.26086956521739135f,
-                            0.29999999999999966f, 0.5000000000000004f},
-                    new float[] {0.18f, 0.2392156862745098f, 0.296078431372549f,
-                            0.34901960784313724f, 0.4274509803921569f, 0.5392156862745098f,
-                            0.6372549019607843f, 0.7490196078431373f, 0.8196078431372549f,
-                            0.8823529411764706f, 0.9372549019607843f}
-            ),
-            new TonalPalette(
-                    new float[] {0.938f, 0.944f, 0.952f, 0.961f, 0.9678571428571429f,
-                            0.9944812362030905f, 0f, 0f,
-                            0.0047348484848484815f, 0.00316455696202532f, 0f,
-                            0.9980392156862745f, 0.9814814814814816f, 0.9722222222222221f},
-                    new float[] {1f, 1f, 1f, 1f, 1f, 0.7023255813953488f, 0.6638655462184874f,
-                            0.6521739130434782f, 0.7719298245614035f, 0.8315789473684211f,
-                            0.6867469879518071f, 0.7264957264957265f, 0.8181818181818182f,
-                            0.8181818181818189f},
-                    new float[] {0.08f, 0.13f, 0.18f, 0.23f, 0.27450980392156865f,
-                            0.4215686274509804f,
-                            0.4666666666666667f, 0.503921568627451f, 0.5529411764705883f,
-                            0.6274509803921569f, 0.6745098039215687f, 0.7705882352941176f,
-                            0.892156862745098f, 0.9568627450980391f}
-            ),
-            new TonalPalette(
-                    new float[] {0.88f, 0.888f, 0.897f, 0.9052287581699346f, 0.9112021857923498f,
-                            0.9270152505446624f, 0.9343137254901961f, 0.9391534391534391f,
-                            0.9437984496124031f, 0.943661971830986f, 0.9438943894389439f,
-                            0.9426229508196722f, 0.9444444444444444f},
-                    new float[] {1f, 1f, 1f, 1f, 0.8133333333333332f, 0.7927461139896375f,
-                            0.7798165137614679f, 0.7777777777777779f, 0.8190476190476191f,
-                            0.8255813953488372f, 0.8211382113821142f, 0.8133333333333336f,
-                            0.8000000000000006f},
-                    new float[] {0.08f, 0.12f, 0.16f, 0.2f, 0.29411764705882354f,
-                            0.3784313725490196f, 0.42745098039215684f, 0.4764705882352941f,
-                            0.5882352941176471f, 0.6627450980392157f, 0.7588235294117647f,
-                            0.8529411764705882f, 0.9411764705882353f}
-            ),
-            new TonalPalette(
-                    new float[] {0.669f, 0.680f, 0.6884057971014492f, 0.6974789915966387f,
-                            0.7079889807162534f, 0.7154471544715447f, 0.7217741935483872f,
-                            0.7274143302180687f, 0.7272727272727273f, 0.7258064516129031f,
-                            0.7252252252252251f, 0.7333333333333333f},
-                    new float[] {0.81f, 0.81f, 0.8214285714285715f, 0.6878612716763006f,
-                            0.6080402010050251f, 0.5774647887323943f, 0.5391304347826086f,
-                            0.46724890829694316f, 0.4680851063829788f, 0.462686567164179f,
-                            0.45679012345678977f, 0.4545454545454551f},
-                    new float[] {0.12f, 0.16f, 0.2196078431372549f, 0.33921568627450976f,
-                            0.39019607843137255f, 0.4176470588235294f, 0.45098039215686275f,
-                            0.5509803921568628f, 0.6313725490196078f, 0.7372549019607844f,
-                            0.8411764705882353f, 0.9352941176470588f}
-            ),
-            new TonalPalette(
-                    new float[] {0.6470588235294118f, 0.6516666666666667f, 0.6464174454828661f,
-                            0.6441441441441442f, 0.6432748538011696f, 0.6416666666666667f,
-                            0.6402439024390243f, 0.6412429378531074f, 0.6435185185185186f,
-                            0.6428571428571429f},
-                    new float[] {0.8095238095238095f, 0.6578947368421053f, 0.5721925133689839f,
-                            0.5362318840579711f, 0.5f, 0.4424778761061947f, 0.44086021505376327f,
-                            0.44360902255639095f, 0.4499999999999997f, 0.4375000000000006f},
-                    new float[] {0.16470588235294117f, 0.2980392156862745f, 0.36666666666666664f,
-                            0.40588235294117647f, 0.44705882352941173f,
-                            0.5568627450980392f, 0.6352941176470588f, 0.7392156862745098f,
-                            0.8431372549019608f, 0.9372549019607843f}
-            ),
-            new TonalPalette(
-                    new float[] {0.469f, 0.46732026143790845f, 0.4718614718614719f,
-                            0.4793650793650794f, 0.48071625344352614f, 0.4829683698296837f,
-                            0.484375f, 0.4841269841269842f, 0.48444444444444457f,
-                            0.48518518518518516f, 0.4907407407407408f},
-                    new float[] {1f, 1f, 1f, 1f, 1f, 1f, 0.6274509803921569f, 0.41832669322709176f,
-                            0.41899441340782106f, 0.4128440366972478f, 0.4090909090909088f},
-                    new float[] {0.07f, 0.1f, 0.15098039215686274f, 0.20588235294117646f,
-                            0.2372549019607843f, 0.26862745098039215f, 0.4f, 0.5078431372549019f,
-                            0.6490196078431372f, 0.7862745098039216f, 0.9137254901960784f}
-            ),
-            new TonalPalette(
-                    new float[] {0.542f, 0.5444444444444444f, 0.5555555555555556f,
-                            0.5555555555555556f, 0.553763440860215f, 0.5526315789473684f,
-                            0.5555555555555556f, 0.5555555555555555f, 0.5555555555555556f,
-                            0.5512820512820514f, 0.5666666666666667f},
-                    new float[] {0.25f, 0.24590163934426232f, 0.19148936170212766f,
-                            0.1791044776119403f, 0.18343195266272191f, 0.18446601941747576f,
-                            0.1538461538461539f, 0.15625000000000003f, 0.15328467153284678f,
-                            0.15662650602409653f, 0.151515151515151f},
-                    new float[] {0.05f, 0.1196078431372549f, 0.1843137254901961f,
-                            0.2627450980392157f,
-                            0.33137254901960783f, 0.403921568627451f, 0.5411764705882354f,
-                            0.6235294117647059f, 0.7313725490196079f, 0.8372549019607843f,
-                            0.9352941176470588f}
-            ),
-            new TonalPalette(
-                    new float[] {0.022222222222222223f, 0.02469135802469136f, 0.031249999999999997f,
-                            0.03947368421052631f, 0.04166666666666668f,
-                            0.043650793650793655f, 0.04411764705882352f, 0.04166666666666652f,
-                            0.04444444444444459f, 0.05555555555555529f},
-                    new float[] {0.33333333333333337f, 0.2783505154639175f, 0.2580645161290323f,
-                            0.25675675675675674f, 0.2528735632183908f, 0.17500000000000002f,
-                            0.15315315315315312f, 0.15189873417721522f,
-                            0.15789473684210534f, 0.15789473684210542f},
-                    new float[] {0.08823529411764705f, 0.19019607843137254f, 0.2431372549019608f,
-                            0.2901960784313725f, 0.3411764705882353f, 0.47058823529411764f,
-                            0.5647058823529412f, 0.6901960784313725f, 0.8137254901960784f,
-                            0.9254901960784314f}
-            ),
-            new TonalPalette(
-                    new float[] {0.027f, 0.03f, 0.038f, 0.044f, 0.050884955752212385f,
-                            0.07254901960784313f, 0.0934640522875817f,
-                            0.10457516339869281f, 0.11699346405228758f,
-                            0.1255813953488372f, 0.1268939393939394f, 0.12533333333333332f,
-                            0.12500000000000003f, 0.12777777777777777f},
-                    new float[] {1f, 1f, 1f, 1f, 1f, 1f, 1f, 1f, 1f, 1f, 1f, 1f, 1f, 1f},
-                    new float[] {0.25f, 0.3f, 0.35f, 0.4f, 0.44313725490196076f, 0.5f, 0.5f, 0.5f,
-                            0.5f, 0.5784313725490196f,
-                            0.6549019607843137f, 0.7549019607843137f, 0.8509803921568627f,
-                            0.9411764705882353f}
-            )
-    };
-
-    private static final TonalPalette GREY_PALETTE = new TonalPalette(
-            new float[]{0f, 0f, 0f, 0f, 0f, 0f, 0f, 0f, 0f, 0f, 0f, 0f},
-            new float[]{0f, 0f, 0f, 0f, 0f, 0f, 0f, 0f, 0f, 0f, 0f, 0f},
-            new float[]{0.08f, 0.11f, 0.14901960784313725f, 0.2f, 0.2980392156862745f, 0.4f,
-                    0.4980392156862745f, 0.6196078431372549f, 0.7176470588235294f,
-                    0.8196078431372549f, 0.9176470588235294f, 0.9490196078431372f}
-    );
-
-    @SuppressWarnings("WeakerAccess")
-    @VisibleForTesting
-    public static final ColorRange[] BLACKLISTED_COLORS = new ColorRange[] {
-
-            // Red
-            new ColorRange(
-                    new Range<>(0f, 20f) /* H */,
-                    new Range<>(0.7f, 1f) /* S */,
-                    new Range<>(0.21f, 0.79f)) /* L */,
-            new ColorRange(
-                    new Range<>(0f, 20f),
-                    new Range<>(0.3f, 0.7f),
-                    new Range<>(0.355f, 0.653f)),
-
-            // Red Orange
-            new ColorRange(
-                    new Range<>(20f, 40f),
-                    new Range<>(0.7f, 1f),
-                    new Range<>(0.28f, 0.643f)),
-            new ColorRange(
-                    new Range<>(20f, 40f),
-                    new Range<>(0.3f, 0.7f),
-                    new Range<>(0.414f, 0.561f)),
-            new ColorRange(
-                    new Range<>(20f, 40f),
-                    new Range<>(0f, 3f),
-                    new Range<>(0.343f, 0.584f)),
-
-            // Orange
-            new ColorRange(
-                    new Range<>(40f, 60f),
-                    new Range<>(0.7f, 1f),
-                    new Range<>(0.173f, 0.349f)),
-            new ColorRange(
-                    new Range<>(40f, 60f),
-                    new Range<>(0.3f, 0.7f),
-                    new Range<>(0.233f, 0.427f)),
-            new ColorRange(
-                    new Range<>(40f, 60f),
-                    new Range<>(0f, 0.3f),
-                    new Range<>(0.231f, 0.484f)),
-
-            // Yellow 60
-            new ColorRange(
-                    new Range<>(60f, 80f),
-                    new Range<>(0.7f, 1f),
-                    new Range<>(0.488f, 0.737f)),
-            new ColorRange(
-                    new Range<>(60f, 80f),
-                    new Range<>(0.3f, 0.7f),
-                    new Range<>(0.673f, 0.837f)),
-
-            // Yellow Green 80
-            new ColorRange(
-                    new Range<>(80f, 100f),
-                    new Range<>(0.7f, 1f),
-                    new Range<>(0.469f, 0.61f)),
-
-            // Yellow green 100
-            new ColorRange(
-                    new Range<>(100f, 120f),
-                    new Range<>(0.7f, 1f),
-                    new Range<>(0.388f, 0.612f)),
-            new ColorRange(
-                    new Range<>(100f, 120f),
-                    new Range<>(0.3f, 0.7f),
-                    new Range<>(0.424f, 0.541f)),
-
-            // Green
-            new ColorRange(
-                    new Range<>(120f, 140f),
-                    new Range<>(0.7f, 1f),
-                    new Range<>(0.375f, 0.52f)),
-            new ColorRange(
-                    new Range<>(120f, 140f),
-                    new Range<>(0.3f, 0.7f),
-                    new Range<>(0.435f, 0.524f)),
-
-            // Green Blue 140
-            new ColorRange(
-                    new Range<>(140f, 160f),
-                    new Range<>(0.7f, 1f),
-                    new Range<>(0.496f, 0.641f)),
-
-            // Seafoam
-            new ColorRange(
-                    new Range<>(160f, 180f),
-                    new Range<>(0.7f, 1f),
-                    new Range<>(0.496f, 0.567f)),
-
-            // Cyan
-            new ColorRange(
-                    new Range<>(180f, 200f),
-                    new Range<>(0.7f, 1f),
-                    new Range<>(0.52f, 0.729f)),
-
-            // Blue
-            new ColorRange(
-                    new Range<>(220f, 240f),
-                    new Range<>(0.7f, 1f),
-                    new Range<>(0.396f, 0.571f)),
-            new ColorRange(
-                    new Range<>(220f, 240f),
-                    new Range<>(0.3f, 0.7f),
-                    new Range<>(0.425f, 0.551f)),
-
-            // Blue Purple 240
-            new ColorRange(
-                    new Range<>(240f, 260f),
-                    new Range<>(0.7f, 1f),
-                    new Range<>(0.418f, 0.639f)),
-            new ColorRange(
-                    new Range<>(220f, 240f),
-                    new Range<>(0.3f, 0.7f),
-                    new Range<>(0.441f, 0.576f)),
-
-            // Blue Purple 260
-            new ColorRange(
-                    new Range<>(260f, 280f),
-                    new Range<>(0.3f, 1f), // Bigger range
-                    new Range<>(0.461f, 0.553f)),
-
-            // Fuchsia
-            new ColorRange(
-                    new Range<>(300f, 320f),
-                    new Range<>(0.7f, 1f),
-                    new Range<>(0.484f, 0.588f)),
-            new ColorRange(
-                    new Range<>(300f, 320f),
-                    new Range<>(0.3f, 0.7f),
-                    new Range<>(0.48f, 0.592f)),
-
-            // Pink
-            new ColorRange(
-                    new Range<>(320f, 340f),
-                    new Range<>(0.7f, 1f),
-                    new Range<>(0.466f, 0.629f)),
-
-            // Soft red
-            new ColorRange(
-                    new Range<>(340f, 360f),
-                    new Range<>(0.7f, 1f),
-                    new Range<>(0.437f, 0.596f))
-    };
-
     /**
      * Representation of an HSL color range.
      * <ul>
@@ -802,4 +477,124 @@
             return String.format("H: %s, S: %s, L %s", mHue, mSaturation, mLightness);
         }
     }
-}
+
+    @VisibleForTesting
+    public static class ConfigParser {
+        private final ArrayList<TonalPalette> mTonalPalettes;
+        private final ArrayList<ColorRange> mBlacklistedColors;
+
+        public ConfigParser(Context context) {
+            mTonalPalettes = new ArrayList<>();
+            mBlacklistedColors = new ArrayList<>();
+
+            // Load all palettes and the blacklist from an XML.
+            try {
+                XmlPullParser parser = context.getResources().getXml(R.xml.color_extraction);
+                int eventType = parser.getEventType();
+                while (eventType != XmlPullParser.END_DOCUMENT) {
+                    if (eventType == XmlPullParser.START_DOCUMENT ||
+                            eventType == XmlPullParser.END_TAG) {
+                        // just skip
+                    } else if (eventType == XmlPullParser.START_TAG) {
+                        String tagName = parser.getName();
+                        if (tagName.equals("palettes")) {
+                            parsePalettes(parser);
+                        } else if (tagName.equals("blacklist")) {
+                            parseBlacklist(parser);
+                        }
+                    } else {
+                        throw new XmlPullParserException("Invalid XML event " + eventType + " - "
+                                + parser.getName(), parser, null);
+                    }
+                    eventType = parser.next();
+                }
+            } catch (XmlPullParserException | IOException e) {
+                throw new RuntimeException(e);
+            }
+        }
+
+        public ArrayList<TonalPalette> getTonalPalettes() {
+            return mTonalPalettes;
+        }
+
+        public ArrayList<ColorRange> getBlacklistedColors() {
+            return mBlacklistedColors;
+        }
+
+        private void parseBlacklist(XmlPullParser parser)
+                throws XmlPullParserException, IOException {
+            parser.require(XmlPullParser.START_TAG, null, "blacklist");
+            while (parser.next() != XmlPullParser.END_TAG) {
+                if (parser.getEventType() != XmlPullParser.START_TAG) {
+                    continue;
+                }
+                String name = parser.getName();
+                // Starts by looking for the entry tag
+                if (name.equals("range")) {
+                    mBlacklistedColors.add(readRange(parser));
+                    parser.next();
+                } else {
+                    throw new XmlPullParserException("Invalid tag: " + name, parser, null);
+                }
+            }
+        }
+
+        private ColorRange readRange(XmlPullParser parser)
+                throws XmlPullParserException, IOException {
+            parser.require(XmlPullParser.START_TAG, null, "range");
+            float[] h = readFloatArray(parser.getAttributeValue(null, "h"));
+            float[] s = readFloatArray(parser.getAttributeValue(null, "s"));
+            float[] l = readFloatArray(parser.getAttributeValue(null, "l"));
+
+            if (h == null || s == null || l == null) {
+                throw new XmlPullParserException("Incomplete range tag.", parser, null);
+            }
+
+            return new ColorRange(new Range<>(h[0], h[1]), new Range<>(s[0], s[1]),
+                    new Range<>(l[0], l[1]));
+        }
+
+        private void parsePalettes(XmlPullParser parser)
+                throws XmlPullParserException, IOException {
+            parser.require(XmlPullParser.START_TAG, null, "palettes");
+            while (parser.next() != XmlPullParser.END_TAG) {
+                if (parser.getEventType() != XmlPullParser.START_TAG) {
+                    continue;
+                }
+                String name = parser.getName();
+                // Starts by looking for the entry tag
+                if (name.equals("palette")) {
+                    mTonalPalettes.add(readPalette(parser));
+                    parser.next();
+                } else {
+                    throw new XmlPullParserException("Invalid tag: " + name);
+                }
+            }
+        }
+
+        private TonalPalette readPalette(XmlPullParser parser)
+                throws XmlPullParserException, IOException {
+            parser.require(XmlPullParser.START_TAG, null, "palette");
+
+            float[] h = readFloatArray(parser.getAttributeValue(null, "h"));
+            float[] s = readFloatArray(parser.getAttributeValue(null, "s"));
+            float[] l = readFloatArray(parser.getAttributeValue(null, "l"));
+
+            if (h == null || s == null || l == null) {
+                throw new XmlPullParserException("Incomplete range tag.", parser, null);
+            }
+
+            return new TonalPalette(h, s, l);
+        }
+
+        private float[] readFloatArray(String attributeValue)
+                throws IOException, XmlPullParserException {
+            String[] tokens = attributeValue.replaceAll(" ", "").replaceAll("\n", "").split(",");
+            float[] numbers = new float[tokens.length];
+            for (int i = 0; i < tokens.length; i++) {
+                numbers[i] = Float.parseFloat(tokens[i]);
+            }
+            return numbers;
+        }
+    }
+}
\ No newline at end of file
diff --git a/core/java/com/android/internal/content/FileSystemProvider.java b/core/java/com/android/internal/content/FileSystemProvider.java
index ebea1a4..04874bd 100644
--- a/core/java/com/android/internal/content/FileSystemProvider.java
+++ b/core/java/com/android/internal/content/FileSystemProvider.java
@@ -28,6 +28,7 @@
 import android.graphics.Point;
 import android.net.Uri;
 import android.os.Binder;
+import android.os.Build;
 import android.os.CancellationSignal;
 import android.os.FileObserver;
 import android.os.FileUtils;
@@ -68,6 +69,8 @@
 
     private Handler mHandler;
 
+    private static final String MIMETYPE_PDF = "application/pdf";
+
     protected abstract File getFileForDocId(String docId, boolean visible)
             throws FileNotFoundException;
 
@@ -387,6 +390,13 @@
             String documentId, Point sizeHint, CancellationSignal signal)
             throws FileNotFoundException {
         final File file = getFileForDocId(documentId);
+        if (getTypeForFile(file).equals(MIMETYPE_PDF)) {
+            try {
+                return PdfUtils.openPdfThumbnail(file, sizeHint);
+            } catch (Exception e) {
+                Log.v(TAG, "Could not load PDF's thumbnail", e);
+            }
+        }
         return DocumentsContract.openImageThumbnail(file);
     }
 
@@ -416,7 +426,10 @@
 
         final String mimeType = getTypeForFile(file);
         final String displayName = file.getName();
-        if (mimeType.startsWith("image/")) {
+        // As of right now, we aren't sure on the performance affect of loading all PDF Thumbnails
+        // Until a solution is found, it will be behind a debuggable flag.
+        if (mimeType.startsWith("image/")
+                || (mimeType.equals(MIMETYPE_PDF) && Build.IS_DEBUGGABLE)) {
             flags |= Document.FLAG_SUPPORTS_THUMBNAIL;
         }
 
diff --git a/core/java/com/android/internal/content/PackageHelper.java b/core/java/com/android/internal/content/PackageHelper.java
index 9c361b3..e923223 100644
--- a/core/java/com/android/internal/content/PackageHelper.java
+++ b/core/java/com/android/internal/content/PackageHelper.java
@@ -16,11 +16,14 @@
 
 package com.android.internal.content;
 
+import static android.net.TrafficStats.MB_IN_BYTES;
+import static android.os.storage.VolumeInfo.ID_PRIVATE_INTERNAL;
+
 import android.content.Context;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageInfo;
-import android.content.pm.PackageManager;
 import android.content.pm.PackageInstaller.SessionParams;
+import android.content.pm.PackageManager;
 import android.content.pm.PackageManager.NameNotFoundException;
 import android.content.pm.PackageParser.PackageLite;
 import android.os.Environment;
@@ -39,21 +42,19 @@
 
 import com.android.internal.annotations.VisibleForTesting;
 
+import libcore.io.IoUtils;
+
 import java.io.File;
 import java.io.FileOutputStream;
 import java.io.IOException;
 import java.io.InputStream;
 import java.util.Collections;
 import java.util.Objects;
+import java.util.UUID;
 import java.util.zip.ZipEntry;
 import java.util.zip.ZipFile;
 import java.util.zip.ZipOutputStream;
 
-import libcore.io.IoUtils;
-
-import static android.net.TrafficStats.MB_IN_BYTES;
-import static android.os.storage.VolumeInfo.ID_PRIVATE_INTERNAL;
-
 /**
  * Constants used internally between the PackageManager
  * and media container service transports.
@@ -358,7 +359,7 @@
         public boolean fitsOnInternalStorage(Context context, SessionParams params)
                 throws IOException {
             StorageManager storage = getStorageManager(context);
-            File target = getDataDirectory();
+            final UUID target = storage.getUuidForPath(getDataDirectory());
             return (params.sizeBytes <= storage.getAllocatableBytes(target,
                     translateAllocateFlags(params.installFlags)));
         }
@@ -466,7 +467,8 @@
             boolean isInternalStorage = ID_PRIVATE_INTERNAL.equals(vol.id);
             if (vol.type == VolumeInfo.TYPE_PRIVATE && vol.isMountedWritable()
                     && (!isInternalStorage || allow3rdPartyOnInternal)) {
-                final long availBytes = storageManager.getAllocatableBytes(new File(vol.path),
+                final UUID target = storageManager.getUuidForPath(new File(vol.path));
+                final long availBytes = storageManager.getAllocatableBytes(target,
                         translateAllocateFlags(params.installFlags));
                 if (availBytes >= params.sizeBytes) {
                     allCandidates.add(vol.fsUuid);
@@ -523,7 +525,7 @@
 
     public static boolean fitsOnInternal(Context context, SessionParams params) throws IOException {
         final StorageManager storage = context.getSystemService(StorageManager.class);
-        final File target = Environment.getDataDirectory();
+        final UUID target = storage.getUuidForPath(Environment.getDataDirectory());
         return (params.sizeBytes <= storage.getAllocatableBytes(target,
                 translateAllocateFlags(params.installFlags)));
     }
diff --git a/core/java/com/android/internal/content/PdfUtils.java b/core/java/com/android/internal/content/PdfUtils.java
new file mode 100644
index 0000000..1716d42
--- /dev/null
+++ b/core/java/com/android/internal/content/PdfUtils.java
@@ -0,0 +1,96 @@
+/*
+ * 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.content;
+
+import android.annotation.Nullable;
+import android.content.res.AssetFileDescriptor;
+import android.graphics.Bitmap;
+import android.graphics.Point;
+import android.graphics.Rect;
+import android.graphics.pdf.PdfRenderer;
+import android.os.AsyncTask;
+import android.os.ParcelFileDescriptor;
+
+import libcore.io.IoUtils;
+import libcore.io.Streams;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+
+/**
+ * Utils class for extracting PDF Thumbnails
+ */
+public final class PdfUtils {
+
+    private PdfUtils() {
+    }
+
+    /**
+     * Returns the front page of the pdf as a thumbnail
+     * @param file Given PDF File
+     * @param size Cropping of the front page.
+     * @return AssetFileDescriptor containing the thumbnail as a bitmap.
+     * @throws IOException if the file isn't a pdf or if the file doesn't exist.
+     */
+    public static @Nullable AssetFileDescriptor openPdfThumbnail(File file, Point size)
+            throws IOException {
+        // Create the bitmap of the PDF's first page
+        ParcelFileDescriptor pdfDescriptor =
+                ParcelFileDescriptor.open(file, ParcelFileDescriptor.MODE_READ_ONLY);
+        PdfRenderer renderer = new PdfRenderer(pdfDescriptor);
+        PdfRenderer.Page frontPage = renderer.openPage(0);
+        Bitmap thumbnail = Bitmap.createBitmap(size.x, size.y,
+                Bitmap.Config.ARGB_8888);
+        frontPage.render(thumbnail, null, null, PdfRenderer.Page.RENDER_MODE_FOR_DISPLAY);
+
+        // Create an AssetFileDescriptor that contains the Bitmap's information
+        final ByteArrayOutputStream out = new ByteArrayOutputStream();
+        // Quality is an integer that determines how much compression is used.
+        // However, this integer is ignored when using the PNG format
+        int quality = 100;
+        // The use of Bitmap.CompressFormat.JPEG leads to a black PDF background on the thumbnail
+        thumbnail.compress(Bitmap.CompressFormat.PNG, quality, out);
+
+        final ByteArrayInputStream in = new ByteArrayInputStream(out.toByteArray());
+
+        final ParcelFileDescriptor[] fds = ParcelFileDescriptor.createReliablePipe();
+        new AsyncTask<Object, Object, Object>() {
+            @Override
+            protected Object doInBackground(Object... params) {
+                final FileOutputStream fos = new FileOutputStream(fds[1].getFileDescriptor());
+                try {
+                    Streams.copy(in, fos);
+                } catch (IOException e) {
+                    throw new RuntimeException(e);
+                }
+                IoUtils.closeQuietly(fds[1]);
+                try {
+                    pdfDescriptor.close();
+                } catch (IOException e) {
+                    throw new RuntimeException(e);
+                }
+                return null;
+            }
+        }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
+        pdfDescriptor.close();
+        return new AssetFileDescriptor(fds[0], 0, AssetFileDescriptor.UNKNOWN_LENGTH);
+    }
+
+}
diff --git a/core/java/com/android/internal/hardware/AmbientDisplayConfiguration.java b/core/java/com/android/internal/hardware/AmbientDisplayConfiguration.java
index 445a82c..df9c27b 100644
--- a/core/java/com/android/internal/hardware/AmbientDisplayConfiguration.java
+++ b/core/java/com/android/internal/hardware/AmbientDisplayConfiguration.java
@@ -36,6 +36,7 @@
         return pulseOnNotificationEnabled(user)
                 || pulseOnPickupEnabled(user)
                 || pulseOnDoubleTapEnabled(user)
+                || pulseOnLongPressEnabled(user)
                 || alwaysOnEnabled(user);
     }
 
@@ -79,6 +80,19 @@
         return mContext.getResources().getString(R.string.config_dozeDoubleTapSensorType);
     }
 
+    public String longPressSensorType() {
+        return mContext.getResources().getString(R.string.config_dozeLongPressSensorType);
+    }
+
+    public boolean pulseOnLongPressEnabled(int user) {
+        return pulseOnLongPressAvailable() && boolSettingDefaultOff(
+                Settings.Secure.DOZE_PULSE_ON_LONG_PRESS, user);
+    }
+
+    private boolean pulseOnLongPressAvailable() {
+        return !TextUtils.isEmpty(longPressSensorType());
+    }
+
     public boolean alwaysOnEnabled(int user) {
         return boolSettingDefaultOn(Settings.Secure.DOZE_ALWAYS_ON, user)
                 && alwaysOnAvailable();
diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java
index fa23e40..c7f619d 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -119,7 +119,7 @@
     private static final int MAGIC = 0xBA757475; // 'BATSTATS'
 
     // Current on-disk Parcel version
-    private static final int VERSION = 160 + (USE_OLD_HISTORY ? 1000 : 0);
+    private static final int VERSION = 161 + (USE_OLD_HISTORY ? 1000 : 0);
 
     // Maximum number of items we will record in the history.
     private static final int MAX_HISTORY_ITEMS;
@@ -5637,6 +5637,7 @@
         StopwatchTimer mFlashlightTurnedOnTimer;
         StopwatchTimer mCameraTurnedOnTimer;
         StopwatchTimer mForegroundActivityTimer;
+        StopwatchTimer mForegroundServiceTimer;
         /** Total time spent by the uid holding any partial wakelocks. */
         DualTimer mAggregatedPartialWakelockTimer;
         DualTimer mBluetoothScanTimer;
@@ -5647,6 +5648,8 @@
         int mProcessState = ActivityManager.PROCESS_STATE_NONEXISTENT;
         StopwatchTimer[] mProcessStateTimer;
 
+        boolean mInForegroundService = false;
+
         BatchTimer mVibratorOnTimer;
 
         Counter[] mUserActivityCounters;
@@ -6119,6 +6122,14 @@
             return mForegroundActivityTimer;
         }
 
+        public StopwatchTimer createForegroundServiceTimerLocked() {
+            if (mForegroundServiceTimer == null) {
+                mForegroundServiceTimer = new StopwatchTimer(mBsi.mClocks, Uid.this,
+                        FOREGROUND_SERVICE, null, mBsi.mOnBatteryTimeBase);
+            }
+            return mForegroundServiceTimer;
+        }
+
         public DualTimer createAggregatedPartialWakelockTimerLocked() {
             if (mAggregatedPartialWakelockTimer == null) {
                 mAggregatedPartialWakelockTimer = new DualTimer(mBsi.mClocks, this,
@@ -6207,6 +6218,16 @@
             }
         }
 
+        public void noteForegroundServiceResumedLocked(long elapsedRealtimeMs) {
+            createForegroundServiceTimerLocked().startRunningLocked(elapsedRealtimeMs);
+        }
+
+        public void noteForegroundServicePausedLocked(long elapsedRealtimeMs) {
+            if (mForegroundServiceTimer != null) {
+                mForegroundServiceTimer.stopRunningLocked(elapsedRealtimeMs);
+            }
+        }
+
         public BatchTimer createVibratorOnTimerLocked() {
             if (mVibratorOnTimer == null) {
                 mVibratorOnTimer = new BatchTimer(mBsi.mClocks, Uid.this, VIBRATOR_ON,
@@ -6335,6 +6356,11 @@
         }
 
         @Override
+        public Timer getForegroundServiceTimer() {
+            return mForegroundServiceTimer;
+        }
+
+        @Override
         public Timer getBluetoothScanTimer() {
             return mBluetoothScanTimer;
         }
@@ -6618,6 +6644,7 @@
             active |= !resetTimerIfNotNull(mFlashlightTurnedOnTimer, false);
             active |= !resetTimerIfNotNull(mCameraTurnedOnTimer, false);
             active |= !resetTimerIfNotNull(mForegroundActivityTimer, false);
+            active |= !resetTimerIfNotNull(mForegroundServiceTimer, false);
             active |= !resetTimerIfNotNull(mAggregatedPartialWakelockTimer, false);
             active |= !resetTimerIfNotNull(mBluetoothScanTimer, false);
             active |= !resetTimerIfNotNull(mBluetoothUnoptimizedScanTimer, false);
@@ -6817,6 +6844,10 @@
                     mForegroundActivityTimer.detach();
                     mForegroundActivityTimer = null;
                 }
+                if (mForegroundServiceTimer != null) {
+                    mForegroundServiceTimer.detach();
+                    mForegroundServiceTimer = null;
+                }
                 if (mAggregatedPartialWakelockTimer != null) {
                     mAggregatedPartialWakelockTimer.detach();
                     mAggregatedPartialWakelockTimer = null;
@@ -7026,6 +7057,12 @@
             } else {
                 out.writeInt(0);
             }
+            if (mForegroundServiceTimer != null) {
+                out.writeInt(1);
+                mForegroundServiceTimer.writeToParcel(out, elapsedRealtimeUs);
+            } else {
+                out.writeInt(0);
+            }
             if (mAggregatedPartialWakelockTimer != null) {
                 out.writeInt(1);
                 mAggregatedPartialWakelockTimer.writeToParcel(out, elapsedRealtimeUs);
@@ -7305,6 +7342,12 @@
                 mForegroundActivityTimer = null;
             }
             if (in.readInt() != 0) {
+                mForegroundServiceTimer = new StopwatchTimer(mBsi.mClocks, Uid.this,
+                        FOREGROUND_SERVICE, null, mBsi.mOnBatteryTimeBase, in);
+            } else {
+                mForegroundServiceTimer = null;
+            }
+            if (in.readInt() != 0) {
                 mAggregatedPartialWakelockTimer = new DualTimer(mBsi.mClocks, this,
                         AGGREGATED_WAKE_TYPE_PARTIAL, null,
                         mBsi.mOnBatteryScreenOffTimeBase, mOnBatteryScreenOffBackgroundTimeBase,
@@ -8350,6 +8393,9 @@
 
         public void updateUidProcessStateLocked(int procState) {
             int uidRunningState;
+            // Make special note of Foreground Services
+            final boolean userAwareService =
+                    (procState == ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE);
             if (procState == ActivityManager.PROCESS_STATE_NONEXISTENT) {
                 uidRunningState = ActivityManager.PROCESS_STATE_NONEXISTENT;
             } else if (procState == ActivityManager.PROCESS_STATE_TOP) {
@@ -8368,24 +8414,37 @@
                 uidRunningState = PROCESS_STATE_CACHED;
             }
 
-            if (mProcessState == uidRunningState) return;
+            if (mProcessState == uidRunningState && userAwareService == mInForegroundService) {
+                return;
+            }
 
             final long elapsedRealtimeMs = mBsi.mClocks.elapsedRealtime();
-            final long uptimeMs = mBsi.mClocks.uptimeMillis();
+            if (mProcessState != uidRunningState) {
+                final long uptimeMs = mBsi.mClocks.uptimeMillis();
 
-            if (mProcessState != ActivityManager.PROCESS_STATE_NONEXISTENT) {
-                mProcessStateTimer[mProcessState].stopRunningLocked(elapsedRealtimeMs);
-            }
-            mProcessState = uidRunningState;
-            if (uidRunningState != ActivityManager.PROCESS_STATE_NONEXISTENT) {
-                if (mProcessStateTimer[uidRunningState] == null) {
-                    makeProcessState(uidRunningState, null);
+                if (mProcessState != ActivityManager.PROCESS_STATE_NONEXISTENT) {
+                    mProcessStateTimer[mProcessState].stopRunningLocked(elapsedRealtimeMs);
                 }
-                mProcessStateTimer[uidRunningState].startRunningLocked(elapsedRealtimeMs);
+                mProcessState = uidRunningState;
+                if (uidRunningState != ActivityManager.PROCESS_STATE_NONEXISTENT) {
+                    if (mProcessStateTimer[uidRunningState] == null) {
+                        makeProcessState(uidRunningState, null);
+                    }
+                    mProcessStateTimer[uidRunningState].startRunningLocked(elapsedRealtimeMs);
+                }
+
+                updateOnBatteryBgTimeBase(uptimeMs * 1000, elapsedRealtimeMs * 1000);
+                updateOnBatteryScreenOffBgTimeBase(uptimeMs * 1000, elapsedRealtimeMs * 1000);
             }
 
-            updateOnBatteryBgTimeBase(uptimeMs * 1000, elapsedRealtimeMs * 1000);
-            updateOnBatteryScreenOffBgTimeBase(uptimeMs * 1000, elapsedRealtimeMs * 1000);
+            if (userAwareService != mInForegroundService) {
+                if (userAwareService) {
+                    noteForegroundServiceResumedLocked(elapsedRealtimeMs);
+                } else {
+                    noteForegroundServicePausedLocked(elapsedRealtimeMs);
+                }
+                mInForegroundService = userAwareService;
+            }
         }
 
         /** Whether to consider Uid to be in the background for background timebase purposes. */
@@ -11610,6 +11669,9 @@
                 u.createForegroundActivityTimerLocked().readSummaryFromParcelLocked(in);
             }
             if (in.readInt() != 0) {
+                u.createForegroundServiceTimerLocked().readSummaryFromParcelLocked(in);
+            }
+            if (in.readInt() != 0) {
                 u.createAggregatedPartialWakelockTimerLocked().readSummaryFromParcelLocked(in);
             }
             if (in.readInt() != 0) {
@@ -12021,6 +12083,12 @@
             } else {
                 out.writeInt(0);
             }
+            if (u.mForegroundServiceTimer != null) {
+                out.writeInt(1);
+                u.mForegroundServiceTimer.writeSummaryFromParcelLocked(out, NOWREAL_SYS);
+            } else {
+                out.writeInt(0);
+            }
             if (u.mAggregatedPartialWakelockTimer != null) {
                 out.writeInt(1);
                 u.mAggregatedPartialWakelockTimer.writeSummaryFromParcelLocked(out, NOWREAL_SYS);
diff --git a/core/java/com/android/internal/os/ZygoteConnection.java b/core/java/com/android/internal/os/ZygoteConnection.java
index 33382ed..1e82054 100644
--- a/core/java/com/android/internal/os/ZygoteConnection.java
+++ b/core/java/com/android/internal/os/ZygoteConnection.java
@@ -18,6 +18,7 @@
 
 import static android.system.OsConstants.F_SETFD;
 import static android.system.OsConstants.O_CLOEXEC;
+import static android.system.OsConstants.POLLIN;
 import static android.system.OsConstants.STDERR_FILENO;
 import static android.system.OsConstants.STDIN_FILENO;
 import static android.system.OsConstants.STDOUT_FILENO;
@@ -30,14 +31,15 @@
 import android.os.Trace;
 import android.system.ErrnoException;
 import android.system.Os;
+import android.system.StructPollfd;
 import android.util.Log;
 import dalvik.system.VMRuntime;
 import java.io.BufferedReader;
+import java.io.ByteArrayInputStream;
 import java.io.DataInputStream;
 import java.io.DataOutputStream;
 import java.io.EOFException;
 import java.io.FileDescriptor;
-import java.io.FileInputStream;
 import java.io.IOException;
 import java.io.InputStreamReader;
 import java.nio.charset.StandardCharsets;
@@ -829,17 +831,63 @@
 
         boolean usingWrapper = false;
         if (pipeFd != null && pid > 0) {
-            DataInputStream is = new DataInputStream(new FileInputStream(pipeFd));
             int innerPid = -1;
             try {
-                innerPid = is.readInt();
-            } catch (IOException ex) {
-                Log.w(TAG, "Error reading pid from wrapped process, child may have died", ex);
-            } finally {
-                try {
-                    is.close();
-                } catch (IOException ex) {
+                // Do a busy loop here. We can't guarantee that a failure (and thus an exception
+                // bail) happens in a timely manner.
+                //
+                // We'll wait up to five seconds. This should give enough time for the fork to go
+                // through, but not to trigger the watchdog in the system server.
+                final int SLEEP_IN_MS = 5000;
+                final int BYTES_REQUIRED = 4;  // Bytes in an int.
+
+                StructPollfd fds[] = new StructPollfd[] {
+                        new StructPollfd()
+                };
+
+                byte data[] = new byte[BYTES_REQUIRED];
+
+                int remainingSleepTime = SLEEP_IN_MS;
+                int dataIndex = 0;
+                long startTime = System.nanoTime();
+
+                while (dataIndex < data.length && remainingSleepTime > 0) {
+                    fds[0].fd = pipeFd;
+                    fds[0].events = (short) POLLIN;
+                    fds[0].revents = 0;
+                    fds[0].userData = null;
+
+                    int res = android.system.Os.poll(fds, remainingSleepTime);
+                    long endTime = System.nanoTime();
+                    remainingSleepTime = SLEEP_IN_MS - (int)((endTime - startTime) / 1000000l);
+
+                    if (res > 0) {
+                        if ((fds[0].revents & POLLIN) != 0) {
+                            // Only read one byte, so as not to block.
+                            int readBytes = android.system.Os.read(pipeFd, data, dataIndex, 1);
+                            if (readBytes < 0) {
+                                throw new RuntimeException("Some error");
+                            }
+                            dataIndex += readBytes;
+                        } else {
+                            // Error case. revents should contain one of the error bits.
+                            break;
+                        }
+                    } else if (res == 0) {
+                        Log.w(TAG, "Timed out waiting for child.");
+                    }
                 }
+
+                if (dataIndex == data.length) {
+                    DataInputStream is = new DataInputStream(new ByteArrayInputStream(data));
+                    innerPid = is.readInt();
+                }
+
+                if (innerPid == -1) {
+                    Log.w(TAG, "Error reading pid from wrapped process, child may have died");
+                }
+            } catch (Exception ex) {
+                Log.w(TAG, "Error reading pid from wrapped process, child may have died", ex);
             }
 
             // Ensure that the pid reported by the wrapped process is either the
diff --git a/core/java/com/android/internal/os/ZygoteInit.java b/core/java/com/android/internal/os/ZygoteInit.java
index 07edc72..0dbe971 100644
--- a/core/java/com/android/internal/os/ZygoteInit.java
+++ b/core/java/com/android/internal/os/ZygoteInit.java
@@ -548,7 +548,8 @@
             int dexoptNeeded;
             try {
                 dexoptNeeded = DexFile.getDexOptNeeded(
-                    classPathElement, instructionSet, systemServerFilter, false /* newProfile */);
+                    classPathElement, instructionSet, systemServerFilter,
+                    false /* newProfile */, false /* downgrade */);
             } catch (FileNotFoundException ignored) {
                 // Do not add to the classpath.
                 Log.w(TAG, "Missing classpath element for system server: " + classPathElement);
@@ -572,7 +573,7 @@
                 try {
                     installd.dexopt(classPathElement, Process.SYSTEM_UID, packageName,
                             instructionSet, dexoptNeeded, outputPath, dexFlags, compilerFilter,
-                            uuid, sharedLibraries, seInfo);
+                            uuid, sharedLibraries, seInfo, false /* downgrade */);
                 } catch (RemoteException | ServiceSpecificException e) {
                     // Ignore (but log), we need this on the classpath for fallback mode.
                     Log.w(TAG, "Failed compiling classpath element for system server: "
diff --git a/core/java/com/android/internal/util/AsyncChannel.java b/core/java/com/android/internal/util/AsyncChannel.java
index d8be9fd..6fbfff8 100644
--- a/core/java/com/android/internal/util/AsyncChannel.java
+++ b/core/java/com/android/internal/util/AsyncChannel.java
@@ -768,9 +768,10 @@
             /** Handle of the reply message */
             @Override
             public void handleMessage(Message msg) {
-                mResultMsg = Message.obtain();
-                mResultMsg.copyFrom(msg);
+                Message msgCopy = Message.obtain();
+                msgCopy.copyFrom(msg);
                 synchronized(mLockObject) {
+                    mResultMsg = msgCopy;
                     mLockObject.notify();
                 }
             }
@@ -812,22 +813,26 @@
          */
         private static Message sendMessageSynchronously(Messenger dstMessenger, Message msg) {
             SyncMessenger sm = SyncMessenger.obtain();
+            Message resultMsg = null;
             try {
                 if (dstMessenger != null && msg != null) {
                     msg.replyTo = sm.mMessenger;
                     synchronized (sm.mHandler.mLockObject) {
+                        if (sm.mHandler.mResultMsg != null) {
+                            Slog.wtf(TAG, "mResultMsg should be null here");
+                            sm.mHandler.mResultMsg = null;
+                        }
                         dstMessenger.send(msg);
                         sm.mHandler.mLockObject.wait();
+                        resultMsg = sm.mHandler.mResultMsg;
+                        sm.mHandler.mResultMsg = null;
                     }
-                } else {
-                    sm.mHandler.mResultMsg = null;
                 }
             } catch (InterruptedException e) {
-                sm.mHandler.mResultMsg = null;
+                Slog.e(TAG, "error in sendMessageSynchronously", e);
             } catch (RemoteException e) {
-                sm.mHandler.mResultMsg = null;
+                Slog.e(TAG, "error in sendMessageSynchronously", e);
             }
-            Message resultMsg = sm.mHandler.mResultMsg;
             sm.recycle();
             return resultMsg;
         }
diff --git a/core/java/com/android/internal/util/CollectionUtils.java b/core/java/com/android/internal/util/CollectionUtils.java
index a7fdb27..dbb6e93 100644
--- a/core/java/com/android/internal/util/CollectionUtils.java
+++ b/core/java/com/android/internal/util/CollectionUtils.java
@@ -21,13 +21,16 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.util.ArraySet;
+import android.util.ExceptionUtils;
+
+import com.android.internal.util.FunctionalUtils.ThrowingConsumer;
 
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Collections;
 import java.util.List;
 import java.util.Set;
-import java.util.function.Function;
+import java.util.function.*;
 import java.util.stream.Stream;
 
 /**
@@ -58,6 +61,32 @@
     }
 
     /**
+     * @see #filter(List, java.util.function.Predicate)
+     */
+    public static @NonNull <T> Set<T> filter(@Nullable Set<T> set,
+            java.util.function.Predicate<? super T> predicate) {
+        if (set == null || set.size() == 0) return Collections.emptySet();
+        ArraySet<T> result = null;
+        if (set instanceof ArraySet) {
+            ArraySet<T> arraySet = (ArraySet<T>) set;
+            int size = arraySet.size();
+            for (int i = 0; i < size; i++) {
+                final T item = arraySet.valueAt(i);
+                if (predicate.test(item)) {
+                    result = ArrayUtils.add(result, item);
+                }
+            }
+        } else {
+            for (T item : set) {
+                if (predicate.test(item)) {
+                    result = ArrayUtils.add(result, item);
+                }
+            }
+        }
+        return emptyIfNull(result);
+    }
+
+    /**
      * Returns a list of items resulting from applying the given function to each element of the
      * provided list.
      *
@@ -77,6 +106,27 @@
     }
 
     /**
+     * @see #map(List, Function)
+     */
+    public static @NonNull <I, O> Set<O> map(@Nullable Set<I> cur,
+            Function<? super I, ? extends O> f) {
+        if (isEmpty(cur)) return Collections.emptySet();
+        ArraySet<O> result = new ArraySet<>();
+        if (cur instanceof ArraySet) {
+            ArraySet<I> arraySet = (ArraySet<I>) cur;
+            int size = arraySet.size();
+            for (int i = 0; i < size; i++) {
+                result.add(f.apply(arraySet.valueAt(i)));
+            }
+        } else {
+            for (I item : cur) {
+                result.add(f.apply(item));
+            }
+        }
+        return result;
+    }
+
+    /**
      * {@link #map(List, Function)} + {@link #filter(List, java.util.function.Predicate)}
      *
      * Calling this is equivalent (but more memory efficient) to:
@@ -180,6 +230,17 @@
     }
 
     /**
+     * @see #add(List, Object)
+     */
+    public static @NonNull <T> Set<T> add(@Nullable Set<T> cur, T val) {
+        if (cur == null || cur == Collections.emptySet()) {
+            cur = new ArraySet<>();
+        }
+        cur.add(val);
+        return cur;
+    }
+
+    /**
      * Similar to {@link List#remove}, but with support for list values of {@code null} and
      * {@link Collections#emptyList}
      */
@@ -192,9 +253,52 @@
     }
 
     /**
+     * @see #remove(List, Object)
+     */
+    public static @NonNull <T> Set<T> remove(@Nullable Set<T> cur, T val) {
+        if (isEmpty(cur)) {
+            return emptyIfNull(cur);
+        }
+        cur.remove(val);
+        return cur;
+    }
+
+    /**
      * @return a list that will not be affected by mutations to the given original list.
      */
     public static @NonNull <T> List<T> copyOf(@Nullable List<T> cur) {
         return isEmpty(cur) ? Collections.emptyList() : new ArrayList<>(cur);
     }
+
+    /**
+     * @return a list that will not be affected by mutations to the given original list.
+     */
+    public static @NonNull <T> Set<T> copyOf(@Nullable Set<T> cur) {
+        return isEmpty(cur) ? Collections.emptySet() : new ArraySet<>(cur);
+    }
+
+    /**
+     * Applies {@code action} to each element in {@code cur}
+     *
+     * This avoids creating an iterator if the given set is an {@link ArraySet}
+     */
+    public static <T> void forEach(@Nullable Set<T> cur, @Nullable ThrowingConsumer<T> action) {
+        if (cur == null || action == null) return;
+        int size = cur.size();
+        if (size == 0) return;
+        try {
+            if (cur instanceof ArraySet) {
+                ArraySet<T> arraySet = (ArraySet<T>) cur;
+                for (int i = 0; i < size; i++) {
+                    action.accept(arraySet.valueAt(i));
+                }
+            } else {
+                for (T t : cur) {
+                    action.accept(t);
+                }
+            }
+        } catch (Exception e) {
+            throw ExceptionUtils.propagate(e);
+        }
+    }
 }
diff --git a/core/java/com/android/internal/util/FunctionalUtils.java b/core/java/com/android/internal/util/FunctionalUtils.java
index 9aeb041..cdef97e 100644
--- a/core/java/com/android/internal/util/FunctionalUtils.java
+++ b/core/java/com/android/internal/util/FunctionalUtils.java
@@ -45,4 +45,15 @@
     public interface ThrowingSupplier<T> {
         T get() throws Exception;
     }
+
+    /**
+     * An equivalent of {@link java.util.function.Consumer} that allows throwing checked exceptions
+     *
+     * This can be used to specify a lambda argument without forcing all the checked exceptions
+     * to be handled within it
+     */
+    @FunctionalInterface
+    public interface ThrowingConsumer<T> {
+        void accept(T t) throws Exception;
+    }
 }
diff --git a/core/java/com/android/internal/view/FloatingActionMode.java b/core/java/com/android/internal/view/FloatingActionMode.java
index ff211b6..497e7b0 100644
--- a/core/java/com/android/internal/view/FloatingActionMode.java
+++ b/core/java/com/android/internal/view/FloatingActionMode.java
@@ -295,6 +295,8 @@
      */
     private static final class FloatingToolbarVisibilityHelper {
 
+        private static final long MIN_SHOW_DURATION_FOR_MOVE_HIDE = 500;
+
         private final FloatingToolbar mToolbar;
 
         private boolean mHideRequested;
@@ -304,6 +306,8 @@
 
         private boolean mActive;
 
+        private long mLastShowTime;
+
         public FloatingToolbarVisibilityHelper(FloatingToolbar toolbar) {
             mToolbar = Preconditions.checkNotNull(toolbar);
         }
@@ -327,7 +331,13 @@
         }
 
         public void setMoving(boolean moving) {
-            mMoving = moving;
+            // Avoid unintended flickering by allowing the toolbar to show long enough before
+            // triggering the 'moving' flag - which signals a hide.
+            final boolean showingLongEnough =
+                System.currentTimeMillis() - mLastShowTime > MIN_SHOW_DURATION_FOR_MOVE_HIDE;
+            if (!moving || showingLongEnough) {
+                mMoving = moving;
+            }
         }
 
         public void setOutOfBounds(boolean outOfBounds) {
@@ -347,6 +357,7 @@
                 mToolbar.hide();
             } else {
                 mToolbar.show();
+                mLastShowTime = System.currentTimeMillis();
             }
         }
     }
diff --git a/core/java/com/android/internal/widget/LockPatternUtils.java b/core/java/com/android/internal/widget/LockPatternUtils.java
index e3b9178..53a9654 100644
--- a/core/java/com/android/internal/widget/LockPatternUtils.java
+++ b/core/java/com/android/internal/widget/LockPatternUtils.java
@@ -648,8 +648,12 @@
         boolean disabledByDefault = mContext.getResources().getBoolean(
                 com.android.internal.R.bool.config_disableLockscreenByDefault);
         boolean isSystemUser = UserManager.isSplitSystemUser() && userId == UserHandle.USER_SYSTEM;
+        UserInfo userInfo = getUserManager().getUserInfo(userId);
+        boolean isDemoUser = UserManager.isDeviceInDemoMode(mContext) && userInfo != null
+                && userInfo.isDemo();
         return getBoolean(DISABLE_LOCKSCREEN_KEY, false, userId)
-                || (disabledByDefault && !isSystemUser);
+                || (disabledByDefault && !isSystemUser)
+                || isDemoUser;
     }
 
     /**
diff --git a/core/java/com/android/server/BootReceiver.java b/core/java/com/android/server/BootReceiver.java
index 2cf58d7..18990cf 100644
--- a/core/java/com/android/server/BootReceiver.java
+++ b/core/java/com/android/server/BootReceiver.java
@@ -143,7 +143,6 @@
         try {
             return FileUtils.readTextFile(lastHeaderFile, 0, null);
         } catch (IOException e) {
-            Slog.e(TAG, "Error reading " + lastHeaderFile, e);
             return null;
         }
     }
@@ -218,6 +217,8 @@
                     "/proc/last_kmsg", -LOG_SIZE, "SYSTEM_LAST_KMSG");
             addFileWithFootersToDropBox(db, timestamps, headers, lastKmsgFooter,
                     "/sys/fs/pstore/console-ramoops", -LOG_SIZE, "SYSTEM_LAST_KMSG");
+            addFileWithFootersToDropBox(db, timestamps, headers, lastKmsgFooter,
+                    "/sys/fs/pstore/console-ramoops-0", -LOG_SIZE, "SYSTEM_LAST_KMSG");
             addFileToDropBox(db, timestamps, headers, "/cache/recovery/log", -LOG_SIZE,
                     "SYSTEM_RECOVERY_LOG");
             addFileToDropBox(db, timestamps, headers, "/cache/recovery/last_kmsg",
@@ -303,6 +304,10 @@
         if (fileTime <= 0) {
             file = new File("/sys/fs/pstore/console-ramoops");
             fileTime = file.lastModified();
+            if (fileTime <= 0) {
+                file = new File("/sys/fs/pstore/console-ramoops-0");
+                fileTime = file.lastModified();
+            }
         }
 
         if (fileTime <= 0) return;  // File does not exist
diff --git a/core/jni/android/graphics/Bitmap.cpp b/core/jni/android/graphics/Bitmap.cpp
index 61e138e..ad05a51 100755
--- a/core/jni/android/graphics/Bitmap.cpp
+++ b/core/jni/android/graphics/Bitmap.cpp
@@ -727,7 +727,7 @@
     if (!dst->setInfo(dstInfo)) {
         return false;
     }
-    if (!dst->tryAllocPixels(alloc, nullptr)) {
+    if (!dst->tryAllocPixels(alloc)) {
         return false;
     }
 
@@ -1081,7 +1081,7 @@
 
         // Map the pixels in place and take ownership of the ashmem region.
         nativeBitmap = sk_sp<Bitmap>(GraphicsJNI::mapAshmemBitmap(env, bitmap.get(),
-                nullptr, dupFd, const_cast<void*>(blob.data()), size, !isMutable));
+                dupFd, const_cast<void*>(blob.data()), size, !isMutable));
         if (!nativeBitmap) {
             close(dupFd);
             blob.release();
diff --git a/core/jni/android/graphics/BitmapFactory.cpp b/core/jni/android/graphics/BitmapFactory.cpp
index 6ea5e9d..358c5ba 100644
--- a/core/jni/android/graphics/BitmapFactory.cpp
+++ b/core/jni/android/graphics/BitmapFactory.cpp
@@ -136,7 +136,7 @@
             : mScale(scale), mSize(size) {
     }
 
-    virtual bool allocPixelRef(SkBitmap* bitmap, SkColorTable*) {
+    virtual bool allocPixelRef(SkBitmap* bitmap) {
         // accounts for scale in final allocation, using eventual size and config
         const int bytesPerPixel = SkColorTypeBytesPerPixel(bitmap->colorType());
         const int requestedSize = bytesPerPixel *
@@ -147,7 +147,7 @@
                     mSize, requestedSize);
             return false;
         }
-        return SkBitmap::HeapAllocator::allocPixelRef(bitmap, nullptr);
+        return SkBitmap::HeapAllocator::allocPixelRef(bitmap);
     }
 private:
     const float mScale;
@@ -163,7 +163,7 @@
     ~RecyclingPixelAllocator() {
     }
 
-    virtual bool allocPixelRef(SkBitmap* bitmap, SkColorTable*) {
+    virtual bool allocPixelRef(SkBitmap* bitmap) {
         const SkImageInfo& info = bitmap->info();
         if (info.colorType() == kUnknown_SkColorType) {
             ALOGW("unable to reuse a bitmap as the target has an unknown bitmap configuration");
@@ -401,7 +401,7 @@
     }
     SkBitmap decodingBitmap;
     if (!decodingBitmap.setInfo(bitmapInfo) ||
-            !decodingBitmap.tryAllocPixels(decodeAllocator, nullptr)) {
+            !decodingBitmap.tryAllocPixels(decodeAllocator)) {
         // SkAndroidCodec should recommend a valid SkImageInfo, so setInfo()
         // should only only fail if the calculated value for rowBytes is too
         // large.
@@ -486,7 +486,7 @@
         // to/from unpremultiplied bitmaps.
         outputBitmap.setInfo(
                 bitmapInfo.makeWH(scaledWidth, scaledHeight).makeColorType(scaledColorType));
-        if (!outputBitmap.tryAllocPixels(outputAllocator, NULL)) {
+        if (!outputBitmap.tryAllocPixels(outputAllocator)) {
             // This should only fail on OOM.  The recyclingAllocator should have
             // enough memory since we check this before decoding using the
             // scaleCheckingAllocator.
diff --git a/core/jni/android/graphics/GIFMovie.cpp b/core/jni/android/graphics/GIFMovie.cpp
index e501cf8..dd99b37 100644
--- a/core/jni/android/graphics/GIFMovie.cpp
+++ b/core/jni/android/graphics/GIFMovie.cpp
@@ -391,8 +391,8 @@
     }
 
     SkColor bgColor = SkPackARGB32(0, 0, 0, 0);
-    if (gif->SColorMap != nullptr) {
-        const GifColorType& col = gif->SColorMap->Colors[fGIF->SBackGroundColor];
+    if (gif->SColorMap != nullptr && gif->SBackGroundColor < gif->SColorMap->ColorCount) {
+        const GifColorType& col = gif->SColorMap->Colors[gif->SBackGroundColor];
         bgColor = SkColorSetARGB(0xFF, col.Red, col.Green, col.Blue);
     }
 
diff --git a/core/jni/android/graphics/Graphics.cpp b/core/jni/android/graphics/Graphics.cpp
index ddd0821..b936432 100644
--- a/core/jni/android/graphics/Graphics.cpp
+++ b/core/jni/android/graphics/Graphics.cpp
@@ -433,7 +433,7 @@
 ///////////////////////////////////////////////////////////////////////////////
 
 android::Bitmap* GraphicsJNI::mapAshmemBitmap(JNIEnv* env, SkBitmap* bitmap,
-        SkColorTable*, int fd, void* addr, size_t size, bool readOnly) {
+        int fd, void* addr, size_t size, bool readOnly) {
     const SkImageInfo& info = bitmap->info();
     if (info.colorType() == kUnknown_SkColorType) {
         doThrowIAE(env, "unknown bitmap configuration");
@@ -607,7 +607,7 @@
 }
 
 ///////////////////////////////////////////////////////////////////////////////
-bool HeapAllocator::allocPixelRef(SkBitmap* bitmap, SkColorTable*) {
+bool HeapAllocator::allocPixelRef(SkBitmap* bitmap) {
     mStorage = android::Bitmap::allocateHeapBitmap(bitmap);
     return !!mStorage;
 }
@@ -624,7 +624,7 @@
 
 RecyclingClippingPixelAllocator::~RecyclingClippingPixelAllocator() {}
 
-bool RecyclingClippingPixelAllocator::allocPixelRef(SkBitmap* bitmap, SkColorTable*) {
+bool RecyclingClippingPixelAllocator::allocPixelRef(SkBitmap* bitmap) {
     // Ensure that the caller did not pass in a NULL bitmap to the constructor or this
     // function.
     LOG_ALWAYS_FATAL_IF(!mRecycledBitmap);
@@ -683,7 +683,7 @@
     // decode is complete.
     mNeedsCopy = true;
 
-    return heapAllocator.allocPixelRef(bitmap, nullptr);
+    return heapAllocator.allocPixelRef(bitmap);
 }
 
 void RecyclingClippingPixelAllocator::copyIfNecessary() {
@@ -714,7 +714,7 @@
             "env->GetJavaVM failed");
 }
 
-bool AshmemPixelAllocator::allocPixelRef(SkBitmap* bitmap, SkColorTable*) {
+bool AshmemPixelAllocator::allocPixelRef(SkBitmap* bitmap) {
     mStorage = android::Bitmap::allocateAshmemBitmap(bitmap);
     return !!mStorage;
 }
diff --git a/core/jni/android/graphics/GraphicsJNI.h b/core/jni/android/graphics/GraphicsJNI.h
index 7fbea25..7f4b384 100644
--- a/core/jni/android/graphics/GraphicsJNI.h
+++ b/core/jni/android/graphics/GraphicsJNI.h
@@ -91,7 +91,7 @@
     static jobject createBitmapRegionDecoder(JNIEnv* env, SkBitmapRegionDecoder* bitmap);
 
     static android::Bitmap* mapAshmemBitmap(JNIEnv* env, SkBitmap* bitmap,
-            SkColorTable* ctable, int fd, void* addr, size_t size, bool readOnly);
+            int fd, void* addr, size_t size, bool readOnly);
 
     /**
      * Given a bitmap we natively allocate a memory block to store the contents
@@ -99,7 +99,7 @@
      * SkPixelRef, which ensures that upon deletion the appropriate caches
      * are notified.
      */
-    static bool allocatePixels(JNIEnv* env, SkBitmap* bitmap, SkColorTable* ctable);
+    static bool allocatePixels(JNIEnv* env, SkBitmap* bitmap);
 
     /** Copy the colors in colors[] to the bitmap, convert to the correct
         format along the way.
@@ -127,7 +127,7 @@
    HeapAllocator() { };
     ~HeapAllocator() { };
 
-    virtual bool allocPixelRef(SkBitmap* bitmap, SkColorTable* ctable) override;
+    virtual bool allocPixelRef(SkBitmap* bitmap) override;
 
     /**
      * Fetches the backing allocation object. Must be called!
@@ -176,7 +176,7 @@
 
     ~RecyclingClippingPixelAllocator();
 
-    virtual bool allocPixelRef(SkBitmap* bitmap, SkColorTable* ctable) override;
+    virtual bool allocPixelRef(SkBitmap* bitmap) override;
 
     /**
      *  Must be called!
@@ -206,7 +206,7 @@
 public:
     explicit AshmemPixelAllocator(JNIEnv* env);
     ~AshmemPixelAllocator() { };
-    virtual bool allocPixelRef(SkBitmap* bitmap, SkColorTable* ctable);
+    virtual bool allocPixelRef(SkBitmap* bitmap);
     android::Bitmap* getStorageObjAndReset() {
         return mStorage.release();
     };
diff --git a/core/jni/android/graphics/Paint.cpp b/core/jni/android/graphics/Paint.cpp
index b936973..3ecbcc3 100644
--- a/core/jni/android/graphics/Paint.cpp
+++ b/core/jni/android/graphics/Paint.cpp
@@ -985,6 +985,16 @@
         }
     }
 
+    static jfloat getStrikeThruPosition(jlong paintHandle, jlong typefaceHandle) {
+        const SkScalar textSize = reinterpret_cast<Paint*>(paintHandle)->getTextSize();
+        return SkScalarToFloat(Paint::kStdStrikeThru_Top * textSize);
+    }
+
+    static jfloat getStrikeThruThickness(jlong paintHandle, jlong typefaceHandle) {
+        const SkScalar textSize = reinterpret_cast<Paint*>(paintHandle)->getTextSize();
+        return SkScalarToFloat(Paint::kStdStrikeThru_Thickness * textSize);
+    }
+
     static void setShadowLayer(jlong paintHandle, jfloat radius,
                                jfloat dx, jfloat dy, jint color) {
         Paint* paint = reinterpret_cast<Paint*>(paintHandle);
@@ -1098,6 +1108,8 @@
     {"nDescent","(JJ)F", (void*) PaintGlue::descent},
     {"nGetUnderlinePosition","(JJ)F", (void*) PaintGlue::getUnderlinePosition},
     {"nGetUnderlineThickness","(JJ)F", (void*) PaintGlue::getUnderlineThickness},
+    {"nGetStrikeThruPosition","(JJ)F", (void*) PaintGlue::getStrikeThruPosition},
+    {"nGetStrikeThruThickness","(JJ)F", (void*) PaintGlue::getStrikeThruThickness},
     {"nSetShadowLayer", "(JFFFI)V", (void*)PaintGlue::setShadowLayer},
     {"nHasShadowLayer", "(J)Z", (void*)PaintGlue::hasShadowLayer}
 };
diff --git a/core/jni/android/opengl/util.cpp b/core/jni/android/opengl/util.cpp
index d7345b0..0573937 100644
--- a/core/jni/android/opengl/util.cpp
+++ b/core/jni/android/opengl/util.cpp
@@ -713,28 +713,10 @@
     const int h = bitmap.height();
     const void* p = bitmap.getPixels();
     if (internalformat == GL_PALETTE8_RGBA8_OES) {
-        if (sizeof(SkPMColor) != sizeof(uint32_t)) {
-            err = -1;
-            goto error;
-        }
-        const size_t size = bitmap.getSize();
-        const size_t palette_size = 256*sizeof(SkPMColor);
-        const size_t imageSize = size + palette_size;
-        void* const data = malloc(imageSize);
-        if (data) {
-            void* const pixels = (char*)data + palette_size;
-            SkColorTable* ctable = bitmap.getColorTable();
-            memcpy(data, ctable->readColors(), ctable->count() * sizeof(SkPMColor));
-            memcpy(pixels, p, size);
-            glCompressedTexImage2D(target, level, internalformat, w, h, border, imageSize, data);
-            free(data);
-        } else {
-            err = -1;
-        }
+        err = -1;
     } else {
         glTexImage2D(target, level, internalformat, w, h, border, internalformat, type, p);
     }
-error:
     return err;
 }
 
diff --git a/core/jni/android_os_Debug.cpp b/core/jni/android_os_Debug.cpp
index 76140c0..6c2b460 100644
--- a/core/jni/android_os_Debug.cpp
+++ b/core/jni/android_os_Debug.cpp
@@ -48,10 +48,14 @@
 namespace android
 {
 
-using UniqueFile = std::unique_ptr<FILE, decltype(&fclose)>;
+static void safeFclose(FILE* fp) {
+  if (fp) fclose(fp);
+}
+
+using UniqueFile = std::unique_ptr<FILE, decltype(&safeFclose)>;
 
 static inline UniqueFile MakeUniqueFile(const char* path, const char* mode) {
-    return UniqueFile(fopen(path, mode), fclose);
+    return UniqueFile(fopen(path, mode), safeFclose);
 }
 
 enum {
@@ -77,14 +81,27 @@
     HEAP_GL,
     HEAP_OTHER_MEMTRACK,
 
+    // Dalvik extra sections (heap).
     HEAP_DALVIK_NORMAL,
     HEAP_DALVIK_LARGE,
-    HEAP_DALVIK_LINEARALLOC,
-    HEAP_DALVIK_ACCOUNTING,
-    HEAP_DALVIK_CODE_CACHE,
     HEAP_DALVIK_ZYGOTE,
     HEAP_DALVIK_NON_MOVING,
-    HEAP_DALVIK_INDIRECT_REFERENCE_TABLE,
+
+    // Dalvik other extra sections.
+    HEAP_DALVIK_OTHER_LINEARALLOC,
+    HEAP_DALVIK_OTHER_ACCOUNTING,
+    HEAP_DALVIK_OTHER_CODE_CACHE,
+    HEAP_DALVIK_OTHER_COMPILER_METADATA,
+    HEAP_DALVIK_OTHER_INDIRECT_REFERENCE_TABLE,
+
+    // Boot vdex / app dex / app vdex
+    HEAP_DEX_BOOT_VDEX,
+    HEAP_DEX_APP_DEX,
+    HEAP_DEX_APP_VDEX,
+
+    // App art, boot art.
+    HEAP_ART_APP,
+    HEAP_ART_BOOT,
 
     _NUM_HEAP,
     _NUM_EXCLUSIVE_HEAP = HEAP_OTHER_MEMTRACK+1,
@@ -293,15 +310,30 @@
                 whichHeap = HEAP_TTF;
                 is_swappable = true;
             } else if ((nameLen > 4 && strstr(name, ".dex") != NULL) ||
-                       (nameLen > 5 && strcmp(name+nameLen-5, ".odex") == 0) ||
-                       (nameLen > 5 && strcmp(name+nameLen-5, ".vdex") == 0)) {
+                       (nameLen > 5 && strcmp(name+nameLen-5, ".odex") == 0)) {
                 whichHeap = HEAP_DEX;
+                subHeap = HEAP_DEX_APP_DEX;
+                is_swappable = true;
+            } else if (nameLen > 5 && strcmp(name+nameLen-5, ".vdex") == 0) {
+                whichHeap = HEAP_DEX;
+                // Handle system@framework@boot* and system/framework/boot*
+                if (strstr(name, "@boot") != NULL || strstr(name, "/boot") != NULL) {
+                    subHeap = HEAP_DEX_BOOT_VDEX;
+                } else {
+                    subHeap = HEAP_DEX_APP_VDEX;
+                }
                 is_swappable = true;
             } else if (nameLen > 4 && strcmp(name+nameLen-4, ".oat") == 0) {
                 whichHeap = HEAP_OAT;
                 is_swappable = true;
             } else if (nameLen > 4 && strcmp(name+nameLen-4, ".art") == 0) {
                 whichHeap = HEAP_ART;
+                // Handle system@framework@boot* and system/framework/boot*
+                if (strstr(name, "@boot") != NULL || strstr(name, "/boot") != NULL) {
+                    subHeap = HEAP_ART_BOOT;
+                } else {
+                    subHeap = HEAP_ART_APP;
+                }
                 is_swappable = true;
             } else if (strncmp(name, "/dev/", 5) == 0) {
                 if (strncmp(name, "/dev/kgsl-3d0", 13) == 0) {
@@ -310,7 +342,7 @@
                     if (strncmp(name, "/dev/ashmem/dalvik-", 19) == 0) {
                         whichHeap = HEAP_DALVIK_OTHER;
                         if (strstr(name, "/dev/ashmem/dalvik-LinearAlloc") == name) {
-                            subHeap = HEAP_DALVIK_LINEARALLOC;
+                            subHeap = HEAP_DALVIK_OTHER_LINEARALLOC;
                         } else if ((strstr(name, "/dev/ashmem/dalvik-alloc space") == name) ||
                                    (strstr(name, "/dev/ashmem/dalvik-main space") == name)) {
                             // This is the regular Dalvik heap.
@@ -328,13 +360,14 @@
                             whichHeap = HEAP_DALVIK;
                             subHeap = HEAP_DALVIK_ZYGOTE;
                         } else if (strstr(name, "/dev/ashmem/dalvik-indirect ref") == name) {
-                            subHeap = HEAP_DALVIK_INDIRECT_REFERENCE_TABLE;
+                            subHeap = HEAP_DALVIK_OTHER_INDIRECT_REFERENCE_TABLE;
                         } else if (strstr(name, "/dev/ashmem/dalvik-jit-code-cache") == name ||
-                                   strstr(name, "/dev/ashmem/dalvik-data-code-cache") == name ||
-                                   strstr(name, "/dev/ashmem/dalvik-CompilerMetadata") == name) {
-                            subHeap = HEAP_DALVIK_CODE_CACHE;
+                                   strstr(name, "/dev/ashmem/dalvik-data-code-cache") == name) {
+                            subHeap = HEAP_DALVIK_OTHER_CODE_CACHE;
+                        } else if (strstr(name, "/dev/ashmem/dalvik-CompilerMetadata") == name) {
+                            subHeap = HEAP_DALVIK_OTHER_COMPILER_METADATA;
                         } else {
-                            subHeap = HEAP_DALVIK_ACCOUNTING;  // Default to accounting.
+                            subHeap = HEAP_DALVIK_OTHER_ACCOUNTING;  // Default to accounting.
                         }
                     } else if (strncmp(name, "/dev/ashmem/CursorWindow", 24) == 0) {
                         whichHeap = HEAP_CURSOR;
@@ -419,7 +452,8 @@
             stats[whichHeap].sharedClean += shared_clean;
             stats[whichHeap].swappedOut += swapped_out;
             stats[whichHeap].swappedOutPss += swapped_out_pss;
-            if (whichHeap == HEAP_DALVIK || whichHeap == HEAP_DALVIK_OTHER) {
+            if (whichHeap == HEAP_DALVIK || whichHeap == HEAP_DALVIK_OTHER ||
+                    whichHeap == HEAP_DEX || whichHeap == HEAP_ART) {
                 stats[subHeap].pss += pss;
                 stats[subHeap].swappablePss += swappable_pss;
                 stats[subHeap].privateDirty += private_dirty;
@@ -972,21 +1006,16 @@
     fprintf(fp, "END\n");
 }
 
-/*
- * Dump the native heap, writing human-readable output to the specified
- * file descriptor.
- */
-static void android_os_Debug_dumpNativeHeap(JNIEnv* env, jobject clazz,
-    jobject fileDescriptor)
+static bool openFile(JNIEnv* env, jobject fileDescriptor, UniqueFile& fp)
 {
     if (fileDescriptor == NULL) {
         jniThrowNullPointerException(env, "fd == null");
-        return;
+        return false;
     }
     int origFd = jniGetFDFromFileDescriptor(env, fileDescriptor);
     if (origFd < 0) {
         jniThrowRuntimeException(env, "Invalid file descriptor");
-        return;
+        return false;
     }
 
     /* dup() the descriptor so we don't close the original with fclose() */
@@ -994,14 +1023,28 @@
     if (fd < 0) {
         ALOGW("dup(%d) failed: %s\n", origFd, strerror(errno));
         jniThrowRuntimeException(env, "dup() failed");
-        return;
+        return false;
     }
 
-    UniqueFile fp(fdopen(fd, "w"), fclose);
+    fp.reset(fdopen(fd, "w"));
     if (fp == nullptr) {
         ALOGW("fdopen(%d) failed: %s\n", fd, strerror(errno));
         close(fd);
         jniThrowRuntimeException(env, "fdopen() failed");
+        return false;
+    }
+    return true;
+}
+
+/*
+ * Dump the native heap, writing human-readable output to the specified
+ * file descriptor.
+ */
+static void android_os_Debug_dumpNativeHeap(JNIEnv* env, jobject,
+    jobject fileDescriptor)
+{
+    UniqueFile fp(nullptr, safeFclose);
+    if (!openFile(env, fileDescriptor, fp)) {
         return;
     }
 
@@ -1010,6 +1053,21 @@
     ALOGD("Native heap dump complete.\n");
 }
 
+/*
+ * Dump the native malloc info, writing xml output to the specified
+ * file descriptor.
+ */
+static void android_os_Debug_dumpNativeMallocInfo(JNIEnv* env, jobject,
+    jobject fileDescriptor)
+{
+    UniqueFile fp(nullptr, safeFclose);
+    if (!openFile(env, fileDescriptor, fp)) {
+        return;
+    }
+
+    malloc_info(0, fp.get());
+}
+
 static bool dumpTraces(JNIEnv* env, jint pid, jstring fileName, jint timeoutSecs,
                        DebuggerdDumpType dumpType) {
     const ScopedUtfChars fileNameChars(env, fileName);
@@ -1070,6 +1128,8 @@
             (void*) android_os_Debug_getMemInfo },
     { "dumpNativeHeap",         "(Ljava/io/FileDescriptor;)V",
             (void*) android_os_Debug_dumpNativeHeap },
+    { "dumpNativeMallocInfo",   "(Ljava/io/FileDescriptor;)V",
+            (void*) android_os_Debug_dumpNativeMallocInfo },
     { "getBinderSentTransactions", "()I",
             (void*) android_os_Debug_getBinderSentTransactions },
     { "getBinderReceivedTransactions", "()I",
diff --git a/core/jni/android_os_HwBinder.cpp b/core/jni/android_os_HwBinder.cpp
index 6c6fa66..5663125 100644
--- a/core/jni/android_os_HwBinder.cpp
+++ b/core/jni/android_os_HwBinder.cpp
@@ -42,6 +42,8 @@
 using android::AndroidRuntime;
 using android::hardware::hidl_vec;
 using android::hardware::hidl_string;
+using android::hardware::IPCThreadState;
+using android::hardware::ProcessState;
 template<typename T>
 using Return = android::hardware::Return<T>;
 
@@ -395,6 +397,15 @@
     return JHwRemoteBinder::NewObject(env, service);
 }
 
+void JHwBinder_native_configureRpcThreadpool(jlong maxThreads, jboolean callerWillJoin) {
+    CHECK(maxThreads > 0);
+    ProcessState::self()->setThreadPoolConfiguration(maxThreads, callerWillJoin /*callerJoinsPool*/);
+}
+
+void JHwBinder_native_joinRpcThreadpool() {
+    IPCThreadState::self()->joinThreadPool();
+}
+
 static JNINativeMethod gMethods[] = {
     { "native_init", "()J", (void *)JHwBinder_native_init },
     { "native_setup", "()V", (void *)JHwBinder_native_setup },
@@ -408,6 +419,12 @@
 
     { "getService", "(Ljava/lang/String;Ljava/lang/String;)L" PACKAGE_PATH "/IHwBinder;",
         (void *)JHwBinder_native_getService },
+
+    { "configureRpcThreadpool", "(JZ)V",
+        (void *)JHwBinder_native_configureRpcThreadpool },
+
+    { "joinRpcThreadpool", "()V",
+        (void *)JHwBinder_native_joinRpcThreadpool },
 };
 
 namespace android {
diff --git a/core/jni/android_text_AndroidBidi.cpp b/core/jni/android_text_AndroidBidi.cpp
index d744b7c..8b71572 100644
--- a/core/jni/android_text_AndroidBidi.cpp
+++ b/core/jni/android_text_AndroidBidi.cpp
@@ -42,7 +42,7 @@
             // Set callbacks to override bidi classes of new emoji
             ubidi_setClassCallback(
                     bidi, minikin::emojiBidiOverride, nullptr, nullptr, nullptr, &status);
-            ubidi_setPara(bidi, chs, n, dir, NULL, &status);
+            ubidi_setPara(bidi, reinterpret_cast<const UChar*>(chs), n, dir, NULL, &status);
             if (U_SUCCESS(status)) {
                 for (int i = 0; i < n; ++i) {
                   info[i] = ubidi_getLevelAt(bidi, i);
diff --git a/core/jni/android_view_InputDevice.cpp b/core/jni/android_view_InputDevice.cpp
index 9cf6a9d..4e6e512 100644
--- a/core/jni/android_view_InputDevice.cpp
+++ b/core/jni/android_view_InputDevice.cpp
@@ -17,7 +17,7 @@
 #include <input/Input.h>
 
 #include <android_runtime/AndroidRuntime.h>
-#include <nativehelper/jni.h>
+#include <jni.h>
 #include <nativehelper/JNIHelp.h>
 
 #include <ScopedLocalRef.h>
diff --git a/core/jni/android_view_KeyCharacterMap.cpp b/core/jni/android_view_KeyCharacterMap.cpp
index e5519a7..586b26e 100644
--- a/core/jni/android_view_KeyCharacterMap.cpp
+++ b/core/jni/android_view_KeyCharacterMap.cpp
@@ -20,7 +20,7 @@
 #include <input/Input.h>
 #include <binder/Parcel.h>
 
-#include <nativehelper/jni.h>
+#include <jni.h>
 #include <nativehelper/JNIHelp.h>
 
 #include "android_os_Parcel.h"
diff --git a/core/jni/include/android_runtime/AndroidRuntime.h b/core/jni/include/android_runtime/AndroidRuntime.h
index c2189d4..3ec8b1f 100644
--- a/core/jni/include/android_runtime/AndroidRuntime.h
+++ b/core/jni/include/android_runtime/AndroidRuntime.h
@@ -26,7 +26,7 @@
 #include <utils/Vector.h>
 #include <utils/threads.h>
 #include <pthread.h>
-#include <nativehelper/jni.h>
+#include <jni.h>
 
 
 namespace android {
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 54282b9..c052e5145 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -1694,6 +1694,10 @@
     <permission android:name="android.permission.RECEIVE_STK_COMMANDS"
         android:protectionLevel="signature|privileged" />
 
+    <!-- Allows an application to send EMBMS download intents to apps-->
+    <permission android:name="android.permission.SEND_EMBMS_INTENTS"
+        android:protectionLevel="signature|privileged" />
+
     <!-- Must be required by an ImsService to ensure that only the
          system can bind to it.
          <p>Protection level: signature|privileged
@@ -2530,6 +2534,23 @@
     <permission android:name="android.permission.BIND_PRINT_RECOMMENDATION_SERVICE"
             android:protectionLevel="signature" />
 
+    <!-- Allows applications to get the installed and enabled print services.
+         @hide
+         @SystemApi
+         @TestApi
+         <p>Protection level: signature|preinstalled
+    -->
+    <permission android:name="android.permission.READ_PRINT_SERVICES"
+        android:protectionLevel="signature|preinstalled" />
+
+    <!-- Allows applications to get the currently recommended print services for printers.
+         @hide
+         @SystemApi
+         <p>Protection level: signature|preinstalled
+    -->
+    <permission android:name="android.permission.READ_PRINT_SERVICE_RECOMMENDATIONS"
+        android:protectionLevel="signature|preinstalled" />
+
     <!-- Must be required by a {@link android.nfc.cardemulation.HostApduService}
          or {@link android.nfc.cardemulation.OffHostApduService} to ensure that only
          the system can bind to it.
@@ -3135,9 +3156,13 @@
     <permission android:name="android.permission.MANAGE_NETWORK_POLICY"
         android:protectionLevel="signature" />
 
-    <!-- @SystemApi Allows an application to account its network traffic against other UIDs. Used
-         by system services like download manager and media server. Not for use by
-         third party apps. @hide -->
+    <!-- @SystemApi Allows an application to manage fallback subscription plans.
+         Note that another app providing plans for an explicit HNI will always
+         take precidence over these fallback plans. @hide -->
+    <permission android:name="android.permission.MANAGE_FALLBACK_SUBSCRIPTION_PLANS"
+        android:protectionLevel="signature|privileged" />
+
+    <!-- @SystemApi @hide @deprecated use UPDATE_DEVICE_STATS instead -->
     <permission android:name="android.permission.MODIFY_NETWORK_ACCOUNTING"
         android:protectionLevel="signature|privileged" />
 
@@ -3839,6 +3864,11 @@
         <service android:name="com.android.server.PreloadsFileCacheExpirationJobService"
                  android:permission="android.permission.BIND_JOB_SERVICE" >
         </service>
+
+        <service android:name="com.android.server.camera.CameraStatsJobService"
+                 android:permission="android.permission.BIND_JOB_SERVICE" >
+        </service>
+
     </application>
 
 </manifest>
diff --git a/core/res/res/drawable-hdpi/ic_media_route_connected_dark_00_mtrl.png b/core/res/res/drawable-hdpi/ic_media_route_connected_dark_00_mtrl.png
new file mode 100644
index 0000000..5aa4e98
--- /dev/null
+++ b/core/res/res/drawable-hdpi/ic_media_route_connected_dark_00_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_media_route_connected_dark_01_mtrl.png b/core/res/res/drawable-hdpi/ic_media_route_connected_dark_01_mtrl.png
new file mode 100644
index 0000000..2afba7f
--- /dev/null
+++ b/core/res/res/drawable-hdpi/ic_media_route_connected_dark_01_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_media_route_connected_dark_02_mtrl.png b/core/res/res/drawable-hdpi/ic_media_route_connected_dark_02_mtrl.png
new file mode 100644
index 0000000..94c696c
--- /dev/null
+++ b/core/res/res/drawable-hdpi/ic_media_route_connected_dark_02_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_media_route_connected_dark_03_mtrl.png b/core/res/res/drawable-hdpi/ic_media_route_connected_dark_03_mtrl.png
new file mode 100644
index 0000000..2bbc0e0
--- /dev/null
+++ b/core/res/res/drawable-hdpi/ic_media_route_connected_dark_03_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_media_route_connected_dark_04_mtrl.png b/core/res/res/drawable-hdpi/ic_media_route_connected_dark_04_mtrl.png
new file mode 100644
index 0000000..1554f45
--- /dev/null
+++ b/core/res/res/drawable-hdpi/ic_media_route_connected_dark_04_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_media_route_connected_dark_05_mtrl.png b/core/res/res/drawable-hdpi/ic_media_route_connected_dark_05_mtrl.png
new file mode 100644
index 0000000..f7f9bec
--- /dev/null
+++ b/core/res/res/drawable-hdpi/ic_media_route_connected_dark_05_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_media_route_connected_dark_06_mtrl.png b/core/res/res/drawable-hdpi/ic_media_route_connected_dark_06_mtrl.png
new file mode 100644
index 0000000..caf35d0
--- /dev/null
+++ b/core/res/res/drawable-hdpi/ic_media_route_connected_dark_06_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_media_route_connected_dark_07_mtrl.png b/core/res/res/drawable-hdpi/ic_media_route_connected_dark_07_mtrl.png
new file mode 100644
index 0000000..428b6cf
--- /dev/null
+++ b/core/res/res/drawable-hdpi/ic_media_route_connected_dark_07_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_media_route_connected_dark_08_mtrl.png b/core/res/res/drawable-hdpi/ic_media_route_connected_dark_08_mtrl.png
new file mode 100644
index 0000000..5bba9bb
--- /dev/null
+++ b/core/res/res/drawable-hdpi/ic_media_route_connected_dark_08_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_media_route_connected_dark_09_mtrl.png b/core/res/res/drawable-hdpi/ic_media_route_connected_dark_09_mtrl.png
new file mode 100644
index 0000000..d562375
--- /dev/null
+++ b/core/res/res/drawable-hdpi/ic_media_route_connected_dark_09_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_media_route_connected_dark_10_mtrl.png b/core/res/res/drawable-hdpi/ic_media_route_connected_dark_10_mtrl.png
new file mode 100644
index 0000000..a083bd2
--- /dev/null
+++ b/core/res/res/drawable-hdpi/ic_media_route_connected_dark_10_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_media_route_connected_dark_11_mtrl.png b/core/res/res/drawable-hdpi/ic_media_route_connected_dark_11_mtrl.png
new file mode 100644
index 0000000..cdd80c8f
--- /dev/null
+++ b/core/res/res/drawable-hdpi/ic_media_route_connected_dark_11_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_media_route_connected_dark_12_mtrl.png b/core/res/res/drawable-hdpi/ic_media_route_connected_dark_12_mtrl.png
new file mode 100644
index 0000000..f38ba50
--- /dev/null
+++ b/core/res/res/drawable-hdpi/ic_media_route_connected_dark_12_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_media_route_connected_dark_13_mtrl.png b/core/res/res/drawable-hdpi/ic_media_route_connected_dark_13_mtrl.png
new file mode 100644
index 0000000..92d5887
--- /dev/null
+++ b/core/res/res/drawable-hdpi/ic_media_route_connected_dark_13_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_media_route_connected_dark_14_mtrl.png b/core/res/res/drawable-hdpi/ic_media_route_connected_dark_14_mtrl.png
new file mode 100644
index 0000000..e5fb306
--- /dev/null
+++ b/core/res/res/drawable-hdpi/ic_media_route_connected_dark_14_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_media_route_connected_dark_15_mtrl.png b/core/res/res/drawable-hdpi/ic_media_route_connected_dark_15_mtrl.png
new file mode 100644
index 0000000..ae62588
--- /dev/null
+++ b/core/res/res/drawable-hdpi/ic_media_route_connected_dark_15_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_media_route_connected_dark_16_mtrl.png b/core/res/res/drawable-hdpi/ic_media_route_connected_dark_16_mtrl.png
new file mode 100644
index 0000000..952eda0
--- /dev/null
+++ b/core/res/res/drawable-hdpi/ic_media_route_connected_dark_16_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_media_route_connected_dark_17_mtrl.png b/core/res/res/drawable-hdpi/ic_media_route_connected_dark_17_mtrl.png
new file mode 100644
index 0000000..d51aa42
--- /dev/null
+++ b/core/res/res/drawable-hdpi/ic_media_route_connected_dark_17_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_media_route_connected_dark_18_mtrl.png b/core/res/res/drawable-hdpi/ic_media_route_connected_dark_18_mtrl.png
new file mode 100644
index 0000000..95a696f
--- /dev/null
+++ b/core/res/res/drawable-hdpi/ic_media_route_connected_dark_18_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_media_route_connected_dark_19_mtrl.png b/core/res/res/drawable-hdpi/ic_media_route_connected_dark_19_mtrl.png
new file mode 100644
index 0000000..a9435e3
--- /dev/null
+++ b/core/res/res/drawable-hdpi/ic_media_route_connected_dark_19_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_media_route_connected_dark_20_mtrl.png b/core/res/res/drawable-hdpi/ic_media_route_connected_dark_20_mtrl.png
new file mode 100644
index 0000000..851168a
--- /dev/null
+++ b/core/res/res/drawable-hdpi/ic_media_route_connected_dark_20_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_media_route_connected_dark_21_mtrl.png b/core/res/res/drawable-hdpi/ic_media_route_connected_dark_21_mtrl.png
new file mode 100644
index 0000000..c0e15e9
--- /dev/null
+++ b/core/res/res/drawable-hdpi/ic_media_route_connected_dark_21_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_media_route_connected_dark_22_mtrl.png b/core/res/res/drawable-hdpi/ic_media_route_connected_dark_22_mtrl.png
new file mode 100644
index 0000000..41a7b9c
--- /dev/null
+++ b/core/res/res/drawable-hdpi/ic_media_route_connected_dark_22_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_media_route_connected_dark_23_mtrl.png b/core/res/res/drawable-hdpi/ic_media_route_connected_dark_23_mtrl.png
new file mode 100644
index 0000000..8618f96
--- /dev/null
+++ b/core/res/res/drawable-hdpi/ic_media_route_connected_dark_23_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_media_route_connected_dark_24_mtrl.png b/core/res/res/drawable-hdpi/ic_media_route_connected_dark_24_mtrl.png
new file mode 100644
index 0000000..81488d1
--- /dev/null
+++ b/core/res/res/drawable-hdpi/ic_media_route_connected_dark_24_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_media_route_connected_dark_25_mtrl.png b/core/res/res/drawable-hdpi/ic_media_route_connected_dark_25_mtrl.png
new file mode 100644
index 0000000..6b635c9
--- /dev/null
+++ b/core/res/res/drawable-hdpi/ic_media_route_connected_dark_25_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_media_route_connected_dark_26_mtrl.png b/core/res/res/drawable-hdpi/ic_media_route_connected_dark_26_mtrl.png
new file mode 100644
index 0000000..8a7e4fc
--- /dev/null
+++ b/core/res/res/drawable-hdpi/ic_media_route_connected_dark_26_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_media_route_connected_dark_27_mtrl.png b/core/res/res/drawable-hdpi/ic_media_route_connected_dark_27_mtrl.png
new file mode 100644
index 0000000..6f7d3a2
--- /dev/null
+++ b/core/res/res/drawable-hdpi/ic_media_route_connected_dark_27_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_media_route_connected_dark_28_mtrl.png b/core/res/res/drawable-hdpi/ic_media_route_connected_dark_28_mtrl.png
new file mode 100644
index 0000000..fc6caf5
--- /dev/null
+++ b/core/res/res/drawable-hdpi/ic_media_route_connected_dark_28_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_media_route_connected_dark_29_mtrl.png b/core/res/res/drawable-hdpi/ic_media_route_connected_dark_29_mtrl.png
new file mode 100644
index 0000000..3554271
--- /dev/null
+++ b/core/res/res/drawable-hdpi/ic_media_route_connected_dark_29_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_media_route_connected_dark_30_mtrl.png b/core/res/res/drawable-hdpi/ic_media_route_connected_dark_30_mtrl.png
new file mode 100644
index 0000000..6c82a89
--- /dev/null
+++ b/core/res/res/drawable-hdpi/ic_media_route_connected_dark_30_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_media_route_connected_light_00_mtrl.png b/core/res/res/drawable-hdpi/ic_media_route_connected_light_00_mtrl.png
new file mode 100644
index 0000000..d6daf80
--- /dev/null
+++ b/core/res/res/drawable-hdpi/ic_media_route_connected_light_00_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_media_route_connected_light_01_mtrl.png b/core/res/res/drawable-hdpi/ic_media_route_connected_light_01_mtrl.png
new file mode 100644
index 0000000..41275f1
--- /dev/null
+++ b/core/res/res/drawable-hdpi/ic_media_route_connected_light_01_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_media_route_connected_light_02_mtrl.png b/core/res/res/drawable-hdpi/ic_media_route_connected_light_02_mtrl.png
new file mode 100644
index 0000000..b63b7c3
--- /dev/null
+++ b/core/res/res/drawable-hdpi/ic_media_route_connected_light_02_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_media_route_connected_light_03_mtrl.png b/core/res/res/drawable-hdpi/ic_media_route_connected_light_03_mtrl.png
new file mode 100644
index 0000000..403d1b7
--- /dev/null
+++ b/core/res/res/drawable-hdpi/ic_media_route_connected_light_03_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_media_route_connected_light_04_mtrl.png b/core/res/res/drawable-hdpi/ic_media_route_connected_light_04_mtrl.png
new file mode 100644
index 0000000..a19aa06
--- /dev/null
+++ b/core/res/res/drawable-hdpi/ic_media_route_connected_light_04_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_media_route_connected_light_05_mtrl.png b/core/res/res/drawable-hdpi/ic_media_route_connected_light_05_mtrl.png
new file mode 100644
index 0000000..33d4dbf
--- /dev/null
+++ b/core/res/res/drawable-hdpi/ic_media_route_connected_light_05_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_media_route_connected_light_06_mtrl.png b/core/res/res/drawable-hdpi/ic_media_route_connected_light_06_mtrl.png
new file mode 100644
index 0000000..ad89fa1
--- /dev/null
+++ b/core/res/res/drawable-hdpi/ic_media_route_connected_light_06_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_media_route_connected_light_07_mtrl.png b/core/res/res/drawable-hdpi/ic_media_route_connected_light_07_mtrl.png
new file mode 100644
index 0000000..9a970c8
--- /dev/null
+++ b/core/res/res/drawable-hdpi/ic_media_route_connected_light_07_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_media_route_connected_light_08_mtrl.png b/core/res/res/drawable-hdpi/ic_media_route_connected_light_08_mtrl.png
new file mode 100644
index 0000000..5c71217
--- /dev/null
+++ b/core/res/res/drawable-hdpi/ic_media_route_connected_light_08_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_media_route_connected_light_09_mtrl.png b/core/res/res/drawable-hdpi/ic_media_route_connected_light_09_mtrl.png
new file mode 100644
index 0000000..181c812
--- /dev/null
+++ b/core/res/res/drawable-hdpi/ic_media_route_connected_light_09_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_media_route_connected_light_10_mtrl.png b/core/res/res/drawable-hdpi/ic_media_route_connected_light_10_mtrl.png
new file mode 100644
index 0000000..1b285a7
--- /dev/null
+++ b/core/res/res/drawable-hdpi/ic_media_route_connected_light_10_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_media_route_connected_light_11_mtrl.png b/core/res/res/drawable-hdpi/ic_media_route_connected_light_11_mtrl.png
new file mode 100644
index 0000000..00f1c62
--- /dev/null
+++ b/core/res/res/drawable-hdpi/ic_media_route_connected_light_11_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_media_route_connected_light_12_mtrl.png b/core/res/res/drawable-hdpi/ic_media_route_connected_light_12_mtrl.png
new file mode 100644
index 0000000..f86cca7
--- /dev/null
+++ b/core/res/res/drawable-hdpi/ic_media_route_connected_light_12_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_media_route_connected_light_13_mtrl.png b/core/res/res/drawable-hdpi/ic_media_route_connected_light_13_mtrl.png
new file mode 100644
index 0000000..4e47fc8
--- /dev/null
+++ b/core/res/res/drawable-hdpi/ic_media_route_connected_light_13_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_media_route_connected_light_14_mtrl.png b/core/res/res/drawable-hdpi/ic_media_route_connected_light_14_mtrl.png
new file mode 100644
index 0000000..8b33f2c
--- /dev/null
+++ b/core/res/res/drawable-hdpi/ic_media_route_connected_light_14_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_media_route_connected_light_15_mtrl.png b/core/res/res/drawable-hdpi/ic_media_route_connected_light_15_mtrl.png
new file mode 100644
index 0000000..451bda7
--- /dev/null
+++ b/core/res/res/drawable-hdpi/ic_media_route_connected_light_15_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_media_route_connected_light_16_mtrl.png b/core/res/res/drawable-hdpi/ic_media_route_connected_light_16_mtrl.png
new file mode 100644
index 0000000..dedbff1
--- /dev/null
+++ b/core/res/res/drawable-hdpi/ic_media_route_connected_light_16_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_media_route_connected_light_17_mtrl.png b/core/res/res/drawable-hdpi/ic_media_route_connected_light_17_mtrl.png
new file mode 100644
index 0000000..d6be31c
--- /dev/null
+++ b/core/res/res/drawable-hdpi/ic_media_route_connected_light_17_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_media_route_connected_light_18_mtrl.png b/core/res/res/drawable-hdpi/ic_media_route_connected_light_18_mtrl.png
new file mode 100644
index 0000000..bfd1900
--- /dev/null
+++ b/core/res/res/drawable-hdpi/ic_media_route_connected_light_18_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_media_route_connected_light_19_mtrl.png b/core/res/res/drawable-hdpi/ic_media_route_connected_light_19_mtrl.png
new file mode 100644
index 0000000..003f13d
--- /dev/null
+++ b/core/res/res/drawable-hdpi/ic_media_route_connected_light_19_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_media_route_connected_light_20_mtrl.png b/core/res/res/drawable-hdpi/ic_media_route_connected_light_20_mtrl.png
new file mode 100644
index 0000000..5564760
--- /dev/null
+++ b/core/res/res/drawable-hdpi/ic_media_route_connected_light_20_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_media_route_connected_light_21_mtrl.png b/core/res/res/drawable-hdpi/ic_media_route_connected_light_21_mtrl.png
new file mode 100644
index 0000000..ebc1ae1
--- /dev/null
+++ b/core/res/res/drawable-hdpi/ic_media_route_connected_light_21_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_media_route_connected_light_22_mtrl.png b/core/res/res/drawable-hdpi/ic_media_route_connected_light_22_mtrl.png
new file mode 100644
index 0000000..92a9230
--- /dev/null
+++ b/core/res/res/drawable-hdpi/ic_media_route_connected_light_22_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_media_route_connected_light_23_mtrl.png b/core/res/res/drawable-hdpi/ic_media_route_connected_light_23_mtrl.png
new file mode 100644
index 0000000..6f5c84a
--- /dev/null
+++ b/core/res/res/drawable-hdpi/ic_media_route_connected_light_23_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_media_route_connected_light_24_mtrl.png b/core/res/res/drawable-hdpi/ic_media_route_connected_light_24_mtrl.png
new file mode 100644
index 0000000..ba4ab1d
--- /dev/null
+++ b/core/res/res/drawable-hdpi/ic_media_route_connected_light_24_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_media_route_connected_light_25_mtrl.png b/core/res/res/drawable-hdpi/ic_media_route_connected_light_25_mtrl.png
new file mode 100644
index 0000000..f246835
--- /dev/null
+++ b/core/res/res/drawable-hdpi/ic_media_route_connected_light_25_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_media_route_connected_light_26_mtrl.png b/core/res/res/drawable-hdpi/ic_media_route_connected_light_26_mtrl.png
new file mode 100644
index 0000000..04b790b
--- /dev/null
+++ b/core/res/res/drawable-hdpi/ic_media_route_connected_light_26_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_media_route_connected_light_27_mtrl.png b/core/res/res/drawable-hdpi/ic_media_route_connected_light_27_mtrl.png
new file mode 100644
index 0000000..63e0d79
--- /dev/null
+++ b/core/res/res/drawable-hdpi/ic_media_route_connected_light_27_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_media_route_connected_light_28_mtrl.png b/core/res/res/drawable-hdpi/ic_media_route_connected_light_28_mtrl.png
new file mode 100644
index 0000000..89027f4
--- /dev/null
+++ b/core/res/res/drawable-hdpi/ic_media_route_connected_light_28_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_media_route_connected_light_29_mtrl.png b/core/res/res/drawable-hdpi/ic_media_route_connected_light_29_mtrl.png
new file mode 100644
index 0000000..4b4870b
--- /dev/null
+++ b/core/res/res/drawable-hdpi/ic_media_route_connected_light_29_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_media_route_connected_light_30_mtrl.png b/core/res/res/drawable-hdpi/ic_media_route_connected_light_30_mtrl.png
new file mode 100644
index 0000000..1dae891
--- /dev/null
+++ b/core/res/res/drawable-hdpi/ic_media_route_connected_light_30_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_media_route_connecting_dark_00_mtrl.png b/core/res/res/drawable-hdpi/ic_media_route_connecting_dark_00_mtrl.png
new file mode 100644
index 0000000..5aa4e98
--- /dev/null
+++ b/core/res/res/drawable-hdpi/ic_media_route_connecting_dark_00_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_media_route_connecting_dark_01_mtrl.png b/core/res/res/drawable-hdpi/ic_media_route_connecting_dark_01_mtrl.png
new file mode 100644
index 0000000..2afba7f
--- /dev/null
+++ b/core/res/res/drawable-hdpi/ic_media_route_connecting_dark_01_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_media_route_connecting_dark_02_mtrl.png b/core/res/res/drawable-hdpi/ic_media_route_connecting_dark_02_mtrl.png
new file mode 100644
index 0000000..94c696c
--- /dev/null
+++ b/core/res/res/drawable-hdpi/ic_media_route_connecting_dark_02_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_media_route_connecting_dark_03_mtrl.png b/core/res/res/drawable-hdpi/ic_media_route_connecting_dark_03_mtrl.png
new file mode 100644
index 0000000..2bbc0e0
--- /dev/null
+++ b/core/res/res/drawable-hdpi/ic_media_route_connecting_dark_03_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_media_route_connecting_dark_04_mtrl.png b/core/res/res/drawable-hdpi/ic_media_route_connecting_dark_04_mtrl.png
new file mode 100644
index 0000000..1554f45
--- /dev/null
+++ b/core/res/res/drawable-hdpi/ic_media_route_connecting_dark_04_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_media_route_connecting_dark_05_mtrl.png b/core/res/res/drawable-hdpi/ic_media_route_connecting_dark_05_mtrl.png
new file mode 100644
index 0000000..f7f9bec
--- /dev/null
+++ b/core/res/res/drawable-hdpi/ic_media_route_connecting_dark_05_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_media_route_connecting_dark_06_mtrl.png b/core/res/res/drawable-hdpi/ic_media_route_connecting_dark_06_mtrl.png
new file mode 100644
index 0000000..caf35d0
--- /dev/null
+++ b/core/res/res/drawable-hdpi/ic_media_route_connecting_dark_06_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_media_route_connecting_dark_07_mtrl.png b/core/res/res/drawable-hdpi/ic_media_route_connecting_dark_07_mtrl.png
new file mode 100644
index 0000000..4c36c70
--- /dev/null
+++ b/core/res/res/drawable-hdpi/ic_media_route_connecting_dark_07_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_media_route_connecting_dark_08_mtrl.png b/core/res/res/drawable-hdpi/ic_media_route_connecting_dark_08_mtrl.png
new file mode 100644
index 0000000..5bba9bb
--- /dev/null
+++ b/core/res/res/drawable-hdpi/ic_media_route_connecting_dark_08_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_media_route_connecting_dark_09_mtrl.png b/core/res/res/drawable-hdpi/ic_media_route_connecting_dark_09_mtrl.png
new file mode 100644
index 0000000..d562375
--- /dev/null
+++ b/core/res/res/drawable-hdpi/ic_media_route_connecting_dark_09_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_media_route_connecting_dark_10_mtrl.png b/core/res/res/drawable-hdpi/ic_media_route_connecting_dark_10_mtrl.png
new file mode 100644
index 0000000..19b7700
--- /dev/null
+++ b/core/res/res/drawable-hdpi/ic_media_route_connecting_dark_10_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_media_route_connecting_dark_11_mtrl.png b/core/res/res/drawable-hdpi/ic_media_route_connecting_dark_11_mtrl.png
new file mode 100644
index 0000000..d86fe79
--- /dev/null
+++ b/core/res/res/drawable-hdpi/ic_media_route_connecting_dark_11_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_media_route_connecting_dark_12_mtrl.png b/core/res/res/drawable-hdpi/ic_media_route_connecting_dark_12_mtrl.png
new file mode 100644
index 0000000..c38e4be
--- /dev/null
+++ b/core/res/res/drawable-hdpi/ic_media_route_connecting_dark_12_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_media_route_connecting_dark_13_mtrl.png b/core/res/res/drawable-hdpi/ic_media_route_connecting_dark_13_mtrl.png
new file mode 100644
index 0000000..4ea1ce3
--- /dev/null
+++ b/core/res/res/drawable-hdpi/ic_media_route_connecting_dark_13_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_media_route_connecting_dark_14_mtrl.png b/core/res/res/drawable-hdpi/ic_media_route_connecting_dark_14_mtrl.png
new file mode 100644
index 0000000..b49e12c
--- /dev/null
+++ b/core/res/res/drawable-hdpi/ic_media_route_connecting_dark_14_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_media_route_connecting_dark_15_mtrl.png b/core/res/res/drawable-hdpi/ic_media_route_connecting_dark_15_mtrl.png
new file mode 100644
index 0000000..5ce7064
--- /dev/null
+++ b/core/res/res/drawable-hdpi/ic_media_route_connecting_dark_15_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_media_route_connecting_dark_16_mtrl.png b/core/res/res/drawable-hdpi/ic_media_route_connecting_dark_16_mtrl.png
new file mode 100644
index 0000000..ebc0b50
--- /dev/null
+++ b/core/res/res/drawable-hdpi/ic_media_route_connecting_dark_16_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_media_route_connecting_dark_17_mtrl.png b/core/res/res/drawable-hdpi/ic_media_route_connecting_dark_17_mtrl.png
new file mode 100644
index 0000000..04af459
--- /dev/null
+++ b/core/res/res/drawable-hdpi/ic_media_route_connecting_dark_17_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_media_route_connecting_dark_18_mtrl.png b/core/res/res/drawable-hdpi/ic_media_route_connecting_dark_18_mtrl.png
new file mode 100644
index 0000000..862f22d
--- /dev/null
+++ b/core/res/res/drawable-hdpi/ic_media_route_connecting_dark_18_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_media_route_connecting_dark_19_mtrl.png b/core/res/res/drawable-hdpi/ic_media_route_connecting_dark_19_mtrl.png
new file mode 100644
index 0000000..9d8f434
--- /dev/null
+++ b/core/res/res/drawable-hdpi/ic_media_route_connecting_dark_19_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_media_route_connecting_dark_20_mtrl.png b/core/res/res/drawable-hdpi/ic_media_route_connecting_dark_20_mtrl.png
new file mode 100644
index 0000000..099703e
--- /dev/null
+++ b/core/res/res/drawable-hdpi/ic_media_route_connecting_dark_20_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_media_route_connecting_dark_21_mtrl.png b/core/res/res/drawable-hdpi/ic_media_route_connecting_dark_21_mtrl.png
new file mode 100644
index 0000000..6463743
--- /dev/null
+++ b/core/res/res/drawable-hdpi/ic_media_route_connecting_dark_21_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_media_route_connecting_dark_22_mtrl.png b/core/res/res/drawable-hdpi/ic_media_route_connecting_dark_22_mtrl.png
new file mode 100644
index 0000000..c3808eb
--- /dev/null
+++ b/core/res/res/drawable-hdpi/ic_media_route_connecting_dark_22_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_media_route_connecting_dark_23_mtrl.png b/core/res/res/drawable-hdpi/ic_media_route_connecting_dark_23_mtrl.png
new file mode 100644
index 0000000..14215bb
--- /dev/null
+++ b/core/res/res/drawable-hdpi/ic_media_route_connecting_dark_23_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_media_route_connecting_dark_24_mtrl.png b/core/res/res/drawable-hdpi/ic_media_route_connecting_dark_24_mtrl.png
new file mode 100644
index 0000000..0720a34
--- /dev/null
+++ b/core/res/res/drawable-hdpi/ic_media_route_connecting_dark_24_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_media_route_connecting_dark_25_mtrl.png b/core/res/res/drawable-hdpi/ic_media_route_connecting_dark_25_mtrl.png
new file mode 100644
index 0000000..08d94b0
--- /dev/null
+++ b/core/res/res/drawable-hdpi/ic_media_route_connecting_dark_25_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_media_route_connecting_dark_26_mtrl.png b/core/res/res/drawable-hdpi/ic_media_route_connecting_dark_26_mtrl.png
new file mode 100644
index 0000000..58bd80b
--- /dev/null
+++ b/core/res/res/drawable-hdpi/ic_media_route_connecting_dark_26_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_media_route_connecting_dark_27_mtrl.png b/core/res/res/drawable-hdpi/ic_media_route_connecting_dark_27_mtrl.png
new file mode 100644
index 0000000..0c7a1f3
--- /dev/null
+++ b/core/res/res/drawable-hdpi/ic_media_route_connecting_dark_27_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_media_route_connecting_dark_28_mtrl.png b/core/res/res/drawable-hdpi/ic_media_route_connecting_dark_28_mtrl.png
new file mode 100644
index 0000000..1a8c1e6
--- /dev/null
+++ b/core/res/res/drawable-hdpi/ic_media_route_connecting_dark_28_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_media_route_connecting_dark_29_mtrl.png b/core/res/res/drawable-hdpi/ic_media_route_connecting_dark_29_mtrl.png
new file mode 100644
index 0000000..10bc6c4
--- /dev/null
+++ b/core/res/res/drawable-hdpi/ic_media_route_connecting_dark_29_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_media_route_connecting_dark_30_mtrl.png b/core/res/res/drawable-hdpi/ic_media_route_connecting_dark_30_mtrl.png
new file mode 100644
index 0000000..5aa4e98
--- /dev/null
+++ b/core/res/res/drawable-hdpi/ic_media_route_connecting_dark_30_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_media_route_connecting_light_00_mtrl.png b/core/res/res/drawable-hdpi/ic_media_route_connecting_light_00_mtrl.png
new file mode 100644
index 0000000..d6daf80
--- /dev/null
+++ b/core/res/res/drawable-hdpi/ic_media_route_connecting_light_00_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_media_route_connecting_light_01_mtrl.png b/core/res/res/drawable-hdpi/ic_media_route_connecting_light_01_mtrl.png
new file mode 100644
index 0000000..41275f1
--- /dev/null
+++ b/core/res/res/drawable-hdpi/ic_media_route_connecting_light_01_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_media_route_connecting_light_02_mtrl.png b/core/res/res/drawable-hdpi/ic_media_route_connecting_light_02_mtrl.png
new file mode 100644
index 0000000..b63b7c3
--- /dev/null
+++ b/core/res/res/drawable-hdpi/ic_media_route_connecting_light_02_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_media_route_connecting_light_03_mtrl.png b/core/res/res/drawable-hdpi/ic_media_route_connecting_light_03_mtrl.png
new file mode 100644
index 0000000..403d1b7
--- /dev/null
+++ b/core/res/res/drawable-hdpi/ic_media_route_connecting_light_03_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_media_route_connecting_light_04_mtrl.png b/core/res/res/drawable-hdpi/ic_media_route_connecting_light_04_mtrl.png
new file mode 100644
index 0000000..a19aa06
--- /dev/null
+++ b/core/res/res/drawable-hdpi/ic_media_route_connecting_light_04_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_media_route_connecting_light_05_mtrl.png b/core/res/res/drawable-hdpi/ic_media_route_connecting_light_05_mtrl.png
new file mode 100644
index 0000000..33d4dbf
--- /dev/null
+++ b/core/res/res/drawable-hdpi/ic_media_route_connecting_light_05_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_media_route_connecting_light_06_mtrl.png b/core/res/res/drawable-hdpi/ic_media_route_connecting_light_06_mtrl.png
new file mode 100644
index 0000000..ad89fa1
--- /dev/null
+++ b/core/res/res/drawable-hdpi/ic_media_route_connecting_light_06_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_media_route_connecting_light_07_mtrl.png b/core/res/res/drawable-hdpi/ic_media_route_connecting_light_07_mtrl.png
new file mode 100644
index 0000000..9a970c8
--- /dev/null
+++ b/core/res/res/drawable-hdpi/ic_media_route_connecting_light_07_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_media_route_connecting_light_08_mtrl.png b/core/res/res/drawable-hdpi/ic_media_route_connecting_light_08_mtrl.png
new file mode 100644
index 0000000..5c71217
--- /dev/null
+++ b/core/res/res/drawable-hdpi/ic_media_route_connecting_light_08_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_media_route_connecting_light_09_mtrl.png b/core/res/res/drawable-hdpi/ic_media_route_connecting_light_09_mtrl.png
new file mode 100644
index 0000000..181c812
--- /dev/null
+++ b/core/res/res/drawable-hdpi/ic_media_route_connecting_light_09_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_media_route_connecting_light_10_mtrl.png b/core/res/res/drawable-hdpi/ic_media_route_connecting_light_10_mtrl.png
new file mode 100644
index 0000000..1b285a7
--- /dev/null
+++ b/core/res/res/drawable-hdpi/ic_media_route_connecting_light_10_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_media_route_connecting_light_11_mtrl.png b/core/res/res/drawable-hdpi/ic_media_route_connecting_light_11_mtrl.png
new file mode 100644
index 0000000..4a91688
--- /dev/null
+++ b/core/res/res/drawable-hdpi/ic_media_route_connecting_light_11_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_media_route_connecting_light_12_mtrl.png b/core/res/res/drawable-hdpi/ic_media_route_connecting_light_12_mtrl.png
new file mode 100644
index 0000000..4a3bc21
--- /dev/null
+++ b/core/res/res/drawable-hdpi/ic_media_route_connecting_light_12_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_media_route_connecting_light_13_mtrl.png b/core/res/res/drawable-hdpi/ic_media_route_connecting_light_13_mtrl.png
new file mode 100644
index 0000000..84d944d
--- /dev/null
+++ b/core/res/res/drawable-hdpi/ic_media_route_connecting_light_13_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_media_route_connecting_light_14_mtrl.png b/core/res/res/drawable-hdpi/ic_media_route_connecting_light_14_mtrl.png
new file mode 100644
index 0000000..8fe61bc
--- /dev/null
+++ b/core/res/res/drawable-hdpi/ic_media_route_connecting_light_14_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_media_route_connecting_light_15_mtrl.png b/core/res/res/drawable-hdpi/ic_media_route_connecting_light_15_mtrl.png
new file mode 100644
index 0000000..d1a9906
--- /dev/null
+++ b/core/res/res/drawable-hdpi/ic_media_route_connecting_light_15_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_media_route_connecting_light_16_mtrl.png b/core/res/res/drawable-hdpi/ic_media_route_connecting_light_16_mtrl.png
new file mode 100644
index 0000000..3de70e3
--- /dev/null
+++ b/core/res/res/drawable-hdpi/ic_media_route_connecting_light_16_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_media_route_connecting_light_17_mtrl.png b/core/res/res/drawable-hdpi/ic_media_route_connecting_light_17_mtrl.png
new file mode 100644
index 0000000..a2caa31
--- /dev/null
+++ b/core/res/res/drawable-hdpi/ic_media_route_connecting_light_17_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_media_route_connecting_light_18_mtrl.png b/core/res/res/drawable-hdpi/ic_media_route_connecting_light_18_mtrl.png
new file mode 100644
index 0000000..984e0b1
--- /dev/null
+++ b/core/res/res/drawable-hdpi/ic_media_route_connecting_light_18_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_media_route_connecting_light_19_mtrl.png b/core/res/res/drawable-hdpi/ic_media_route_connecting_light_19_mtrl.png
new file mode 100644
index 0000000..1a20df8
--- /dev/null
+++ b/core/res/res/drawable-hdpi/ic_media_route_connecting_light_19_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_media_route_connecting_light_20_mtrl.png b/core/res/res/drawable-hdpi/ic_media_route_connecting_light_20_mtrl.png
new file mode 100644
index 0000000..2ffe3f7
--- /dev/null
+++ b/core/res/res/drawable-hdpi/ic_media_route_connecting_light_20_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_media_route_connecting_light_21_mtrl.png b/core/res/res/drawable-hdpi/ic_media_route_connecting_light_21_mtrl.png
new file mode 100644
index 0000000..87f7880
--- /dev/null
+++ b/core/res/res/drawable-hdpi/ic_media_route_connecting_light_21_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_media_route_connecting_light_22_mtrl.png b/core/res/res/drawable-hdpi/ic_media_route_connecting_light_22_mtrl.png
new file mode 100644
index 0000000..39f1db7
--- /dev/null
+++ b/core/res/res/drawable-hdpi/ic_media_route_connecting_light_22_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_media_route_connecting_light_23_mtrl.png b/core/res/res/drawable-hdpi/ic_media_route_connecting_light_23_mtrl.png
new file mode 100644
index 0000000..e1501fe6
--- /dev/null
+++ b/core/res/res/drawable-hdpi/ic_media_route_connecting_light_23_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_media_route_connecting_light_24_mtrl.png b/core/res/res/drawable-hdpi/ic_media_route_connecting_light_24_mtrl.png
new file mode 100644
index 0000000..1ac9df6
--- /dev/null
+++ b/core/res/res/drawable-hdpi/ic_media_route_connecting_light_24_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_media_route_connecting_light_25_mtrl.png b/core/res/res/drawable-hdpi/ic_media_route_connecting_light_25_mtrl.png
new file mode 100644
index 0000000..486225b
--- /dev/null
+++ b/core/res/res/drawable-hdpi/ic_media_route_connecting_light_25_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_media_route_connecting_light_26_mtrl.png b/core/res/res/drawable-hdpi/ic_media_route_connecting_light_26_mtrl.png
new file mode 100644
index 0000000..e2aec2b
--- /dev/null
+++ b/core/res/res/drawable-hdpi/ic_media_route_connecting_light_26_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_media_route_connecting_light_27_mtrl.png b/core/res/res/drawable-hdpi/ic_media_route_connecting_light_27_mtrl.png
new file mode 100644
index 0000000..c63ee04
--- /dev/null
+++ b/core/res/res/drawable-hdpi/ic_media_route_connecting_light_27_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_media_route_connecting_light_28_mtrl.png b/core/res/res/drawable-hdpi/ic_media_route_connecting_light_28_mtrl.png
new file mode 100644
index 0000000..2f92d61
--- /dev/null
+++ b/core/res/res/drawable-hdpi/ic_media_route_connecting_light_28_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_media_route_connecting_light_29_mtrl.png b/core/res/res/drawable-hdpi/ic_media_route_connecting_light_29_mtrl.png
new file mode 100644
index 0000000..9b7c065
--- /dev/null
+++ b/core/res/res/drawable-hdpi/ic_media_route_connecting_light_29_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_media_route_connecting_light_30_mtrl.png b/core/res/res/drawable-hdpi/ic_media_route_connecting_light_30_mtrl.png
new file mode 100644
index 0000000..d6daf80
--- /dev/null
+++ b/core/res/res/drawable-hdpi/ic_media_route_connecting_light_30_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_media_route_off_dark_mtrl.png b/core/res/res/drawable-hdpi/ic_media_route_off_dark_mtrl.png
new file mode 100644
index 0000000..1f180ad
--- /dev/null
+++ b/core/res/res/drawable-hdpi/ic_media_route_off_dark_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_media_route_off_light_mtrl.png b/core/res/res/drawable-hdpi/ic_media_route_off_light_mtrl.png
new file mode 100644
index 0000000..18d83e9
--- /dev/null
+++ b/core/res/res/drawable-hdpi/ic_media_route_off_light_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_media_route_off_mtrl_alpha.png b/core/res/res/drawable-hdpi/ic_media_route_off_mtrl_alpha.png
deleted file mode 100644
index d37e8ee..0000000
--- a/core/res/res/drawable-hdpi/ic_media_route_off_mtrl_alpha.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_media_route_on_0_mtrl_alpha.png b/core/res/res/drawable-hdpi/ic_media_route_on_0_mtrl_alpha.png
deleted file mode 100644
index 0c604d9..0000000
--- a/core/res/res/drawable-hdpi/ic_media_route_on_0_mtrl_alpha.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_media_route_on_1_mtrl_alpha.png b/core/res/res/drawable-hdpi/ic_media_route_on_1_mtrl_alpha.png
deleted file mode 100644
index 2c3f4ff..0000000
--- a/core/res/res/drawable-hdpi/ic_media_route_on_1_mtrl_alpha.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_media_route_on_2_mtrl_alpha.png b/core/res/res/drawable-hdpi/ic_media_route_on_2_mtrl_alpha.png
deleted file mode 100644
index 991c50e..0000000
--- a/core/res/res/drawable-hdpi/ic_media_route_on_2_mtrl_alpha.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_media_route_on_mtrl_alpha.png b/core/res/res/drawable-hdpi/ic_media_route_on_mtrl_alpha.png
deleted file mode 100644
index 05fb919..0000000
--- a/core/res/res/drawable-hdpi/ic_media_route_on_mtrl_alpha.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_media_route_connected_dark_00_mtrl.png b/core/res/res/drawable-mdpi/ic_media_route_connected_dark_00_mtrl.png
new file mode 100644
index 0000000..49d353d
--- /dev/null
+++ b/core/res/res/drawable-mdpi/ic_media_route_connected_dark_00_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_media_route_connected_dark_01_mtrl.png b/core/res/res/drawable-mdpi/ic_media_route_connected_dark_01_mtrl.png
new file mode 100644
index 0000000..ca1bf45
--- /dev/null
+++ b/core/res/res/drawable-mdpi/ic_media_route_connected_dark_01_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_media_route_connected_dark_02_mtrl.png b/core/res/res/drawable-mdpi/ic_media_route_connected_dark_02_mtrl.png
new file mode 100644
index 0000000..69611bc
--- /dev/null
+++ b/core/res/res/drawable-mdpi/ic_media_route_connected_dark_02_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_media_route_connected_dark_03_mtrl.png b/core/res/res/drawable-mdpi/ic_media_route_connected_dark_03_mtrl.png
new file mode 100644
index 0000000..53e0f64
--- /dev/null
+++ b/core/res/res/drawable-mdpi/ic_media_route_connected_dark_03_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_media_route_connected_dark_04_mtrl.png b/core/res/res/drawable-mdpi/ic_media_route_connected_dark_04_mtrl.png
new file mode 100644
index 0000000..bcf2a18
--- /dev/null
+++ b/core/res/res/drawable-mdpi/ic_media_route_connected_dark_04_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_media_route_connected_dark_05_mtrl.png b/core/res/res/drawable-mdpi/ic_media_route_connected_dark_05_mtrl.png
new file mode 100644
index 0000000..937f5a0
--- /dev/null
+++ b/core/res/res/drawable-mdpi/ic_media_route_connected_dark_05_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_media_route_connected_dark_06_mtrl.png b/core/res/res/drawable-mdpi/ic_media_route_connected_dark_06_mtrl.png
new file mode 100644
index 0000000..5a5e2d5
--- /dev/null
+++ b/core/res/res/drawable-mdpi/ic_media_route_connected_dark_06_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_media_route_connected_dark_07_mtrl.png b/core/res/res/drawable-mdpi/ic_media_route_connected_dark_07_mtrl.png
new file mode 100644
index 0000000..82cf33c
--- /dev/null
+++ b/core/res/res/drawable-mdpi/ic_media_route_connected_dark_07_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_media_route_connected_dark_08_mtrl.png b/core/res/res/drawable-mdpi/ic_media_route_connected_dark_08_mtrl.png
new file mode 100644
index 0000000..522b331
--- /dev/null
+++ b/core/res/res/drawable-mdpi/ic_media_route_connected_dark_08_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_media_route_connected_dark_09_mtrl.png b/core/res/res/drawable-mdpi/ic_media_route_connected_dark_09_mtrl.png
new file mode 100644
index 0000000..23723a3
--- /dev/null
+++ b/core/res/res/drawable-mdpi/ic_media_route_connected_dark_09_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_media_route_connected_dark_10_mtrl.png b/core/res/res/drawable-mdpi/ic_media_route_connected_dark_10_mtrl.png
new file mode 100644
index 0000000..313b6d2
--- /dev/null
+++ b/core/res/res/drawable-mdpi/ic_media_route_connected_dark_10_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_media_route_connected_dark_11_mtrl.png b/core/res/res/drawable-mdpi/ic_media_route_connected_dark_11_mtrl.png
new file mode 100644
index 0000000..cfbc110
--- /dev/null
+++ b/core/res/res/drawable-mdpi/ic_media_route_connected_dark_11_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_media_route_connected_dark_12_mtrl.png b/core/res/res/drawable-mdpi/ic_media_route_connected_dark_12_mtrl.png
new file mode 100644
index 0000000..2b2c628
--- /dev/null
+++ b/core/res/res/drawable-mdpi/ic_media_route_connected_dark_12_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_media_route_connected_dark_13_mtrl.png b/core/res/res/drawable-mdpi/ic_media_route_connected_dark_13_mtrl.png
new file mode 100644
index 0000000..260adca
--- /dev/null
+++ b/core/res/res/drawable-mdpi/ic_media_route_connected_dark_13_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_media_route_connected_dark_14_mtrl.png b/core/res/res/drawable-mdpi/ic_media_route_connected_dark_14_mtrl.png
new file mode 100644
index 0000000..cadb1c5
--- /dev/null
+++ b/core/res/res/drawable-mdpi/ic_media_route_connected_dark_14_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_media_route_connected_dark_15_mtrl.png b/core/res/res/drawable-mdpi/ic_media_route_connected_dark_15_mtrl.png
new file mode 100644
index 0000000..b91e799
--- /dev/null
+++ b/core/res/res/drawable-mdpi/ic_media_route_connected_dark_15_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_media_route_connected_dark_16_mtrl.png b/core/res/res/drawable-mdpi/ic_media_route_connected_dark_16_mtrl.png
new file mode 100644
index 0000000..19edb96
--- /dev/null
+++ b/core/res/res/drawable-mdpi/ic_media_route_connected_dark_16_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_media_route_connected_dark_17_mtrl.png b/core/res/res/drawable-mdpi/ic_media_route_connected_dark_17_mtrl.png
new file mode 100644
index 0000000..e7b20cc
--- /dev/null
+++ b/core/res/res/drawable-mdpi/ic_media_route_connected_dark_17_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_media_route_connected_dark_18_mtrl.png b/core/res/res/drawable-mdpi/ic_media_route_connected_dark_18_mtrl.png
new file mode 100644
index 0000000..7ea341e
--- /dev/null
+++ b/core/res/res/drawable-mdpi/ic_media_route_connected_dark_18_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_media_route_connected_dark_19_mtrl.png b/core/res/res/drawable-mdpi/ic_media_route_connected_dark_19_mtrl.png
new file mode 100644
index 0000000..fb8b556
--- /dev/null
+++ b/core/res/res/drawable-mdpi/ic_media_route_connected_dark_19_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_media_route_connected_dark_20_mtrl.png b/core/res/res/drawable-mdpi/ic_media_route_connected_dark_20_mtrl.png
new file mode 100644
index 0000000..91016aa
--- /dev/null
+++ b/core/res/res/drawable-mdpi/ic_media_route_connected_dark_20_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_media_route_connected_dark_21_mtrl.png b/core/res/res/drawable-mdpi/ic_media_route_connected_dark_21_mtrl.png
new file mode 100644
index 0000000..c51481a
--- /dev/null
+++ b/core/res/res/drawable-mdpi/ic_media_route_connected_dark_21_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_media_route_connected_dark_22_mtrl.png b/core/res/res/drawable-mdpi/ic_media_route_connected_dark_22_mtrl.png
new file mode 100644
index 0000000..80d09e2
--- /dev/null
+++ b/core/res/res/drawable-mdpi/ic_media_route_connected_dark_22_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_media_route_connected_dark_23_mtrl.png b/core/res/res/drawable-mdpi/ic_media_route_connected_dark_23_mtrl.png
new file mode 100644
index 0000000..c4ad65f
--- /dev/null
+++ b/core/res/res/drawable-mdpi/ic_media_route_connected_dark_23_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_media_route_connected_dark_24_mtrl.png b/core/res/res/drawable-mdpi/ic_media_route_connected_dark_24_mtrl.png
new file mode 100644
index 0000000..060be80
--- /dev/null
+++ b/core/res/res/drawable-mdpi/ic_media_route_connected_dark_24_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_media_route_connected_dark_25_mtrl.png b/core/res/res/drawable-mdpi/ic_media_route_connected_dark_25_mtrl.png
new file mode 100644
index 0000000..e45e858
--- /dev/null
+++ b/core/res/res/drawable-mdpi/ic_media_route_connected_dark_25_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_media_route_connected_dark_26_mtrl.png b/core/res/res/drawable-mdpi/ic_media_route_connected_dark_26_mtrl.png
new file mode 100644
index 0000000..55c3959
--- /dev/null
+++ b/core/res/res/drawable-mdpi/ic_media_route_connected_dark_26_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_media_route_connected_dark_27_mtrl.png b/core/res/res/drawable-mdpi/ic_media_route_connected_dark_27_mtrl.png
new file mode 100644
index 0000000..9c11d99
--- /dev/null
+++ b/core/res/res/drawable-mdpi/ic_media_route_connected_dark_27_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_media_route_connected_dark_28_mtrl.png b/core/res/res/drawable-mdpi/ic_media_route_connected_dark_28_mtrl.png
new file mode 100644
index 0000000..d3c334b
--- /dev/null
+++ b/core/res/res/drawable-mdpi/ic_media_route_connected_dark_28_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_media_route_connected_dark_29_mtrl.png b/core/res/res/drawable-mdpi/ic_media_route_connected_dark_29_mtrl.png
new file mode 100644
index 0000000..a3164c9
--- /dev/null
+++ b/core/res/res/drawable-mdpi/ic_media_route_connected_dark_29_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_media_route_connected_dark_30_mtrl.png b/core/res/res/drawable-mdpi/ic_media_route_connected_dark_30_mtrl.png
new file mode 100644
index 0000000..b550760
--- /dev/null
+++ b/core/res/res/drawable-mdpi/ic_media_route_connected_dark_30_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_media_route_connected_light_00_mtrl.png b/core/res/res/drawable-mdpi/ic_media_route_connected_light_00_mtrl.png
new file mode 100644
index 0000000..d5efab4
--- /dev/null
+++ b/core/res/res/drawable-mdpi/ic_media_route_connected_light_00_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_media_route_connected_light_01_mtrl.png b/core/res/res/drawable-mdpi/ic_media_route_connected_light_01_mtrl.png
new file mode 100644
index 0000000..74d39ac
--- /dev/null
+++ b/core/res/res/drawable-mdpi/ic_media_route_connected_light_01_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_media_route_connected_light_02_mtrl.png b/core/res/res/drawable-mdpi/ic_media_route_connected_light_02_mtrl.png
new file mode 100644
index 0000000..3775cef
--- /dev/null
+++ b/core/res/res/drawable-mdpi/ic_media_route_connected_light_02_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_media_route_connected_light_03_mtrl.png b/core/res/res/drawable-mdpi/ic_media_route_connected_light_03_mtrl.png
new file mode 100644
index 0000000..d960a39
--- /dev/null
+++ b/core/res/res/drawable-mdpi/ic_media_route_connected_light_03_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_media_route_connected_light_04_mtrl.png b/core/res/res/drawable-mdpi/ic_media_route_connected_light_04_mtrl.png
new file mode 100644
index 0000000..6101cdf
--- /dev/null
+++ b/core/res/res/drawable-mdpi/ic_media_route_connected_light_04_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_media_route_connected_light_05_mtrl.png b/core/res/res/drawable-mdpi/ic_media_route_connected_light_05_mtrl.png
new file mode 100644
index 0000000..fca6c96
--- /dev/null
+++ b/core/res/res/drawable-mdpi/ic_media_route_connected_light_05_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_media_route_connected_light_06_mtrl.png b/core/res/res/drawable-mdpi/ic_media_route_connected_light_06_mtrl.png
new file mode 100644
index 0000000..b2bdc46
--- /dev/null
+++ b/core/res/res/drawable-mdpi/ic_media_route_connected_light_06_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_media_route_connected_light_07_mtrl.png b/core/res/res/drawable-mdpi/ic_media_route_connected_light_07_mtrl.png
new file mode 100644
index 0000000..9d8335e
--- /dev/null
+++ b/core/res/res/drawable-mdpi/ic_media_route_connected_light_07_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_media_route_connected_light_08_mtrl.png b/core/res/res/drawable-mdpi/ic_media_route_connected_light_08_mtrl.png
new file mode 100644
index 0000000..4593765
--- /dev/null
+++ b/core/res/res/drawable-mdpi/ic_media_route_connected_light_08_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_media_route_connected_light_09_mtrl.png b/core/res/res/drawable-mdpi/ic_media_route_connected_light_09_mtrl.png
new file mode 100644
index 0000000..d740810
--- /dev/null
+++ b/core/res/res/drawable-mdpi/ic_media_route_connected_light_09_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_media_route_connected_light_10_mtrl.png b/core/res/res/drawable-mdpi/ic_media_route_connected_light_10_mtrl.png
new file mode 100644
index 0000000..7b8a7fc
--- /dev/null
+++ b/core/res/res/drawable-mdpi/ic_media_route_connected_light_10_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_media_route_connected_light_11_mtrl.png b/core/res/res/drawable-mdpi/ic_media_route_connected_light_11_mtrl.png
new file mode 100644
index 0000000..e5d3c682
--- /dev/null
+++ b/core/res/res/drawable-mdpi/ic_media_route_connected_light_11_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_media_route_connected_light_12_mtrl.png b/core/res/res/drawable-mdpi/ic_media_route_connected_light_12_mtrl.png
new file mode 100644
index 0000000..b264a99
--- /dev/null
+++ b/core/res/res/drawable-mdpi/ic_media_route_connected_light_12_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_media_route_connected_light_13_mtrl.png b/core/res/res/drawable-mdpi/ic_media_route_connected_light_13_mtrl.png
new file mode 100644
index 0000000..0232d72
--- /dev/null
+++ b/core/res/res/drawable-mdpi/ic_media_route_connected_light_13_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_media_route_connected_light_14_mtrl.png b/core/res/res/drawable-mdpi/ic_media_route_connected_light_14_mtrl.png
new file mode 100644
index 0000000..2aa94bb
--- /dev/null
+++ b/core/res/res/drawable-mdpi/ic_media_route_connected_light_14_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_media_route_connected_light_15_mtrl.png b/core/res/res/drawable-mdpi/ic_media_route_connected_light_15_mtrl.png
new file mode 100644
index 0000000..693f6c6
--- /dev/null
+++ b/core/res/res/drawable-mdpi/ic_media_route_connected_light_15_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_media_route_connected_light_16_mtrl.png b/core/res/res/drawable-mdpi/ic_media_route_connected_light_16_mtrl.png
new file mode 100644
index 0000000..b7aea5c
--- /dev/null
+++ b/core/res/res/drawable-mdpi/ic_media_route_connected_light_16_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_media_route_connected_light_17_mtrl.png b/core/res/res/drawable-mdpi/ic_media_route_connected_light_17_mtrl.png
new file mode 100644
index 0000000..217cb3d
--- /dev/null
+++ b/core/res/res/drawable-mdpi/ic_media_route_connected_light_17_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_media_route_connected_light_18_mtrl.png b/core/res/res/drawable-mdpi/ic_media_route_connected_light_18_mtrl.png
new file mode 100644
index 0000000..933f338
--- /dev/null
+++ b/core/res/res/drawable-mdpi/ic_media_route_connected_light_18_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_media_route_connected_light_19_mtrl.png b/core/res/res/drawable-mdpi/ic_media_route_connected_light_19_mtrl.png
new file mode 100644
index 0000000..a2ced71
--- /dev/null
+++ b/core/res/res/drawable-mdpi/ic_media_route_connected_light_19_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_media_route_connected_light_20_mtrl.png b/core/res/res/drawable-mdpi/ic_media_route_connected_light_20_mtrl.png
new file mode 100644
index 0000000..4303ca4
--- /dev/null
+++ b/core/res/res/drawable-mdpi/ic_media_route_connected_light_20_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_media_route_connected_light_21_mtrl.png b/core/res/res/drawable-mdpi/ic_media_route_connected_light_21_mtrl.png
new file mode 100644
index 0000000..c4d9559
--- /dev/null
+++ b/core/res/res/drawable-mdpi/ic_media_route_connected_light_21_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_media_route_connected_light_22_mtrl.png b/core/res/res/drawable-mdpi/ic_media_route_connected_light_22_mtrl.png
new file mode 100644
index 0000000..a6e278b
--- /dev/null
+++ b/core/res/res/drawable-mdpi/ic_media_route_connected_light_22_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_media_route_connected_light_23_mtrl.png b/core/res/res/drawable-mdpi/ic_media_route_connected_light_23_mtrl.png
new file mode 100644
index 0000000..19bf6c2
--- /dev/null
+++ b/core/res/res/drawable-mdpi/ic_media_route_connected_light_23_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_media_route_connected_light_24_mtrl.png b/core/res/res/drawable-mdpi/ic_media_route_connected_light_24_mtrl.png
new file mode 100644
index 0000000..c6c2163
--- /dev/null
+++ b/core/res/res/drawable-mdpi/ic_media_route_connected_light_24_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_media_route_connected_light_25_mtrl.png b/core/res/res/drawable-mdpi/ic_media_route_connected_light_25_mtrl.png
new file mode 100644
index 0000000..fe87238
--- /dev/null
+++ b/core/res/res/drawable-mdpi/ic_media_route_connected_light_25_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_media_route_connected_light_26_mtrl.png b/core/res/res/drawable-mdpi/ic_media_route_connected_light_26_mtrl.png
new file mode 100644
index 0000000..229c489
--- /dev/null
+++ b/core/res/res/drawable-mdpi/ic_media_route_connected_light_26_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_media_route_connected_light_27_mtrl.png b/core/res/res/drawable-mdpi/ic_media_route_connected_light_27_mtrl.png
new file mode 100644
index 0000000..64155d9
--- /dev/null
+++ b/core/res/res/drawable-mdpi/ic_media_route_connected_light_27_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_media_route_connected_light_28_mtrl.png b/core/res/res/drawable-mdpi/ic_media_route_connected_light_28_mtrl.png
new file mode 100644
index 0000000..cb4c0ed
--- /dev/null
+++ b/core/res/res/drawable-mdpi/ic_media_route_connected_light_28_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_media_route_connected_light_29_mtrl.png b/core/res/res/drawable-mdpi/ic_media_route_connected_light_29_mtrl.png
new file mode 100644
index 0000000..a85c70c
--- /dev/null
+++ b/core/res/res/drawable-mdpi/ic_media_route_connected_light_29_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_media_route_connected_light_30_mtrl.png b/core/res/res/drawable-mdpi/ic_media_route_connected_light_30_mtrl.png
new file mode 100644
index 0000000..d99afbf
--- /dev/null
+++ b/core/res/res/drawable-mdpi/ic_media_route_connected_light_30_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_media_route_connecting_dark_00_mtrl.png b/core/res/res/drawable-mdpi/ic_media_route_connecting_dark_00_mtrl.png
new file mode 100644
index 0000000..49d353d
--- /dev/null
+++ b/core/res/res/drawable-mdpi/ic_media_route_connecting_dark_00_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_media_route_connecting_dark_01_mtrl.png b/core/res/res/drawable-mdpi/ic_media_route_connecting_dark_01_mtrl.png
new file mode 100644
index 0000000..ca1bf45
--- /dev/null
+++ b/core/res/res/drawable-mdpi/ic_media_route_connecting_dark_01_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_media_route_connecting_dark_02_mtrl.png b/core/res/res/drawable-mdpi/ic_media_route_connecting_dark_02_mtrl.png
new file mode 100644
index 0000000..69611bc
--- /dev/null
+++ b/core/res/res/drawable-mdpi/ic_media_route_connecting_dark_02_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_media_route_connecting_dark_03_mtrl.png b/core/res/res/drawable-mdpi/ic_media_route_connecting_dark_03_mtrl.png
new file mode 100644
index 0000000..53e0f64
--- /dev/null
+++ b/core/res/res/drawable-mdpi/ic_media_route_connecting_dark_03_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_media_route_connecting_dark_04_mtrl.png b/core/res/res/drawable-mdpi/ic_media_route_connecting_dark_04_mtrl.png
new file mode 100644
index 0000000..bcf2a18
--- /dev/null
+++ b/core/res/res/drawable-mdpi/ic_media_route_connecting_dark_04_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_media_route_connecting_dark_05_mtrl.png b/core/res/res/drawable-mdpi/ic_media_route_connecting_dark_05_mtrl.png
new file mode 100644
index 0000000..937f5a0
--- /dev/null
+++ b/core/res/res/drawable-mdpi/ic_media_route_connecting_dark_05_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_media_route_connecting_dark_06_mtrl.png b/core/res/res/drawable-mdpi/ic_media_route_connecting_dark_06_mtrl.png
new file mode 100644
index 0000000..5a5e2d5
--- /dev/null
+++ b/core/res/res/drawable-mdpi/ic_media_route_connecting_dark_06_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_media_route_connecting_dark_07_mtrl.png b/core/res/res/drawable-mdpi/ic_media_route_connecting_dark_07_mtrl.png
new file mode 100644
index 0000000..82cf33c
--- /dev/null
+++ b/core/res/res/drawable-mdpi/ic_media_route_connecting_dark_07_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_media_route_connecting_dark_08_mtrl.png b/core/res/res/drawable-mdpi/ic_media_route_connecting_dark_08_mtrl.png
new file mode 100644
index 0000000..522b331
--- /dev/null
+++ b/core/res/res/drawable-mdpi/ic_media_route_connecting_dark_08_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_media_route_connecting_dark_09_mtrl.png b/core/res/res/drawable-mdpi/ic_media_route_connecting_dark_09_mtrl.png
new file mode 100644
index 0000000..23723a3
--- /dev/null
+++ b/core/res/res/drawable-mdpi/ic_media_route_connecting_dark_09_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_media_route_connecting_dark_10_mtrl.png b/core/res/res/drawable-mdpi/ic_media_route_connecting_dark_10_mtrl.png
new file mode 100644
index 0000000..313b6d2
--- /dev/null
+++ b/core/res/res/drawable-mdpi/ic_media_route_connecting_dark_10_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_media_route_connecting_dark_11_mtrl.png b/core/res/res/drawable-mdpi/ic_media_route_connecting_dark_11_mtrl.png
new file mode 100644
index 0000000..db37fc5
--- /dev/null
+++ b/core/res/res/drawable-mdpi/ic_media_route_connecting_dark_11_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_media_route_connecting_dark_12_mtrl.png b/core/res/res/drawable-mdpi/ic_media_route_connecting_dark_12_mtrl.png
new file mode 100644
index 0000000..79941dc
--- /dev/null
+++ b/core/res/res/drawable-mdpi/ic_media_route_connecting_dark_12_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_media_route_connecting_dark_13_mtrl.png b/core/res/res/drawable-mdpi/ic_media_route_connecting_dark_13_mtrl.png
new file mode 100644
index 0000000..3361fe2
--- /dev/null
+++ b/core/res/res/drawable-mdpi/ic_media_route_connecting_dark_13_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_media_route_connecting_dark_14_mtrl.png b/core/res/res/drawable-mdpi/ic_media_route_connecting_dark_14_mtrl.png
new file mode 100644
index 0000000..5649d0f
--- /dev/null
+++ b/core/res/res/drawable-mdpi/ic_media_route_connecting_dark_14_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_media_route_connecting_dark_15_mtrl.png b/core/res/res/drawable-mdpi/ic_media_route_connecting_dark_15_mtrl.png
new file mode 100644
index 0000000..801b562
--- /dev/null
+++ b/core/res/res/drawable-mdpi/ic_media_route_connecting_dark_15_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_media_route_connecting_dark_16_mtrl.png b/core/res/res/drawable-mdpi/ic_media_route_connecting_dark_16_mtrl.png
new file mode 100644
index 0000000..38e1408
--- /dev/null
+++ b/core/res/res/drawable-mdpi/ic_media_route_connecting_dark_16_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_media_route_connecting_dark_17_mtrl.png b/core/res/res/drawable-mdpi/ic_media_route_connecting_dark_17_mtrl.png
new file mode 100644
index 0000000..f99797d
--- /dev/null
+++ b/core/res/res/drawable-mdpi/ic_media_route_connecting_dark_17_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_media_route_connecting_dark_18_mtrl.png b/core/res/res/drawable-mdpi/ic_media_route_connecting_dark_18_mtrl.png
new file mode 100644
index 0000000..7048711
--- /dev/null
+++ b/core/res/res/drawable-mdpi/ic_media_route_connecting_dark_18_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_media_route_connecting_dark_19_mtrl.png b/core/res/res/drawable-mdpi/ic_media_route_connecting_dark_19_mtrl.png
new file mode 100644
index 0000000..6f60804
--- /dev/null
+++ b/core/res/res/drawable-mdpi/ic_media_route_connecting_dark_19_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_media_route_connecting_dark_20_mtrl.png b/core/res/res/drawable-mdpi/ic_media_route_connecting_dark_20_mtrl.png
new file mode 100644
index 0000000..da3a23b
--- /dev/null
+++ b/core/res/res/drawable-mdpi/ic_media_route_connecting_dark_20_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_media_route_connecting_dark_21_mtrl.png b/core/res/res/drawable-mdpi/ic_media_route_connecting_dark_21_mtrl.png
new file mode 100644
index 0000000..4007ed1
--- /dev/null
+++ b/core/res/res/drawable-mdpi/ic_media_route_connecting_dark_21_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_media_route_connecting_dark_22_mtrl.png b/core/res/res/drawable-mdpi/ic_media_route_connecting_dark_22_mtrl.png
new file mode 100644
index 0000000..518d2b9
--- /dev/null
+++ b/core/res/res/drawable-mdpi/ic_media_route_connecting_dark_22_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_media_route_connecting_dark_23_mtrl.png b/core/res/res/drawable-mdpi/ic_media_route_connecting_dark_23_mtrl.png
new file mode 100644
index 0000000..d821697
--- /dev/null
+++ b/core/res/res/drawable-mdpi/ic_media_route_connecting_dark_23_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_media_route_connecting_dark_24_mtrl.png b/core/res/res/drawable-mdpi/ic_media_route_connecting_dark_24_mtrl.png
new file mode 100644
index 0000000..aa19608
--- /dev/null
+++ b/core/res/res/drawable-mdpi/ic_media_route_connecting_dark_24_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_media_route_connecting_dark_25_mtrl.png b/core/res/res/drawable-mdpi/ic_media_route_connecting_dark_25_mtrl.png
new file mode 100644
index 0000000..81bf08c
--- /dev/null
+++ b/core/res/res/drawable-mdpi/ic_media_route_connecting_dark_25_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_media_route_connecting_dark_26_mtrl.png b/core/res/res/drawable-mdpi/ic_media_route_connecting_dark_26_mtrl.png
new file mode 100644
index 0000000..1bb7aec
--- /dev/null
+++ b/core/res/res/drawable-mdpi/ic_media_route_connecting_dark_26_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_media_route_connecting_dark_27_mtrl.png b/core/res/res/drawable-mdpi/ic_media_route_connecting_dark_27_mtrl.png
new file mode 100644
index 0000000..864795c
--- /dev/null
+++ b/core/res/res/drawable-mdpi/ic_media_route_connecting_dark_27_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_media_route_connecting_dark_28_mtrl.png b/core/res/res/drawable-mdpi/ic_media_route_connecting_dark_28_mtrl.png
new file mode 100644
index 0000000..ed07e9f
--- /dev/null
+++ b/core/res/res/drawable-mdpi/ic_media_route_connecting_dark_28_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_media_route_connecting_dark_29_mtrl.png b/core/res/res/drawable-mdpi/ic_media_route_connecting_dark_29_mtrl.png
new file mode 100644
index 0000000..a188260
--- /dev/null
+++ b/core/res/res/drawable-mdpi/ic_media_route_connecting_dark_29_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_media_route_connecting_dark_30_mtrl.png b/core/res/res/drawable-mdpi/ic_media_route_connecting_dark_30_mtrl.png
new file mode 100644
index 0000000..49d353d
--- /dev/null
+++ b/core/res/res/drawable-mdpi/ic_media_route_connecting_dark_30_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_media_route_connecting_light_00_mtrl.png b/core/res/res/drawable-mdpi/ic_media_route_connecting_light_00_mtrl.png
new file mode 100644
index 0000000..d5efab4
--- /dev/null
+++ b/core/res/res/drawable-mdpi/ic_media_route_connecting_light_00_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_media_route_connecting_light_01_mtrl.png b/core/res/res/drawable-mdpi/ic_media_route_connecting_light_01_mtrl.png
new file mode 100644
index 0000000..74d39ac
--- /dev/null
+++ b/core/res/res/drawable-mdpi/ic_media_route_connecting_light_01_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_media_route_connecting_light_02_mtrl.png b/core/res/res/drawable-mdpi/ic_media_route_connecting_light_02_mtrl.png
new file mode 100644
index 0000000..3775cef
--- /dev/null
+++ b/core/res/res/drawable-mdpi/ic_media_route_connecting_light_02_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_media_route_connecting_light_03_mtrl.png b/core/res/res/drawable-mdpi/ic_media_route_connecting_light_03_mtrl.png
new file mode 100644
index 0000000..d960a39
--- /dev/null
+++ b/core/res/res/drawable-mdpi/ic_media_route_connecting_light_03_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_media_route_connecting_light_04_mtrl.png b/core/res/res/drawable-mdpi/ic_media_route_connecting_light_04_mtrl.png
new file mode 100644
index 0000000..6101cdf
--- /dev/null
+++ b/core/res/res/drawable-mdpi/ic_media_route_connecting_light_04_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_media_route_connecting_light_05_mtrl.png b/core/res/res/drawable-mdpi/ic_media_route_connecting_light_05_mtrl.png
new file mode 100644
index 0000000..fca6c96
--- /dev/null
+++ b/core/res/res/drawable-mdpi/ic_media_route_connecting_light_05_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_media_route_connecting_light_06_mtrl.png b/core/res/res/drawable-mdpi/ic_media_route_connecting_light_06_mtrl.png
new file mode 100644
index 0000000..b2bdc46
--- /dev/null
+++ b/core/res/res/drawable-mdpi/ic_media_route_connecting_light_06_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_media_route_connecting_light_07_mtrl.png b/core/res/res/drawable-mdpi/ic_media_route_connecting_light_07_mtrl.png
new file mode 100644
index 0000000..9d8335e
--- /dev/null
+++ b/core/res/res/drawable-mdpi/ic_media_route_connecting_light_07_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_media_route_connecting_light_08_mtrl.png b/core/res/res/drawable-mdpi/ic_media_route_connecting_light_08_mtrl.png
new file mode 100644
index 0000000..4593765
--- /dev/null
+++ b/core/res/res/drawable-mdpi/ic_media_route_connecting_light_08_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_media_route_connecting_light_09_mtrl.png b/core/res/res/drawable-mdpi/ic_media_route_connecting_light_09_mtrl.png
new file mode 100644
index 0000000..d740810
--- /dev/null
+++ b/core/res/res/drawable-mdpi/ic_media_route_connecting_light_09_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_media_route_connecting_light_10_mtrl.png b/core/res/res/drawable-mdpi/ic_media_route_connecting_light_10_mtrl.png
new file mode 100644
index 0000000..7b8a7fc
--- /dev/null
+++ b/core/res/res/drawable-mdpi/ic_media_route_connecting_light_10_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_media_route_connecting_light_11_mtrl.png b/core/res/res/drawable-mdpi/ic_media_route_connecting_light_11_mtrl.png
new file mode 100644
index 0000000..aadb0cd
--- /dev/null
+++ b/core/res/res/drawable-mdpi/ic_media_route_connecting_light_11_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_media_route_connecting_light_12_mtrl.png b/core/res/res/drawable-mdpi/ic_media_route_connecting_light_12_mtrl.png
new file mode 100644
index 0000000..628e63d
--- /dev/null
+++ b/core/res/res/drawable-mdpi/ic_media_route_connecting_light_12_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_media_route_connecting_light_13_mtrl.png b/core/res/res/drawable-mdpi/ic_media_route_connecting_light_13_mtrl.png
new file mode 100644
index 0000000..dfc63ae
--- /dev/null
+++ b/core/res/res/drawable-mdpi/ic_media_route_connecting_light_13_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_media_route_connecting_light_14_mtrl.png b/core/res/res/drawable-mdpi/ic_media_route_connecting_light_14_mtrl.png
new file mode 100644
index 0000000..450ead1
--- /dev/null
+++ b/core/res/res/drawable-mdpi/ic_media_route_connecting_light_14_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_media_route_connecting_light_15_mtrl.png b/core/res/res/drawable-mdpi/ic_media_route_connecting_light_15_mtrl.png
new file mode 100644
index 0000000..6424958
--- /dev/null
+++ b/core/res/res/drawable-mdpi/ic_media_route_connecting_light_15_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_media_route_connecting_light_16_mtrl.png b/core/res/res/drawable-mdpi/ic_media_route_connecting_light_16_mtrl.png
new file mode 100644
index 0000000..c5b7fa4
--- /dev/null
+++ b/core/res/res/drawable-mdpi/ic_media_route_connecting_light_16_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_media_route_connecting_light_17_mtrl.png b/core/res/res/drawable-mdpi/ic_media_route_connecting_light_17_mtrl.png
new file mode 100644
index 0000000..13fcf6f
--- /dev/null
+++ b/core/res/res/drawable-mdpi/ic_media_route_connecting_light_17_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_media_route_connecting_light_18_mtrl.png b/core/res/res/drawable-mdpi/ic_media_route_connecting_light_18_mtrl.png
new file mode 100644
index 0000000..5be9c69
--- /dev/null
+++ b/core/res/res/drawable-mdpi/ic_media_route_connecting_light_18_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_media_route_connecting_light_19_mtrl.png b/core/res/res/drawable-mdpi/ic_media_route_connecting_light_19_mtrl.png
new file mode 100644
index 0000000..3de2194
--- /dev/null
+++ b/core/res/res/drawable-mdpi/ic_media_route_connecting_light_19_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_media_route_connecting_light_20_mtrl.png b/core/res/res/drawable-mdpi/ic_media_route_connecting_light_20_mtrl.png
new file mode 100644
index 0000000..c40a2cf
--- /dev/null
+++ b/core/res/res/drawable-mdpi/ic_media_route_connecting_light_20_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_media_route_connecting_light_21_mtrl.png b/core/res/res/drawable-mdpi/ic_media_route_connecting_light_21_mtrl.png
new file mode 100644
index 0000000..9923ccd
--- /dev/null
+++ b/core/res/res/drawable-mdpi/ic_media_route_connecting_light_21_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_media_route_connecting_light_22_mtrl.png b/core/res/res/drawable-mdpi/ic_media_route_connecting_light_22_mtrl.png
new file mode 100644
index 0000000..8a000c1
--- /dev/null
+++ b/core/res/res/drawable-mdpi/ic_media_route_connecting_light_22_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_media_route_connecting_light_23_mtrl.png b/core/res/res/drawable-mdpi/ic_media_route_connecting_light_23_mtrl.png
new file mode 100644
index 0000000..3680ced
--- /dev/null
+++ b/core/res/res/drawable-mdpi/ic_media_route_connecting_light_23_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_media_route_connecting_light_24_mtrl.png b/core/res/res/drawable-mdpi/ic_media_route_connecting_light_24_mtrl.png
new file mode 100644
index 0000000..d014f5e
--- /dev/null
+++ b/core/res/res/drawable-mdpi/ic_media_route_connecting_light_24_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_media_route_connecting_light_25_mtrl.png b/core/res/res/drawable-mdpi/ic_media_route_connecting_light_25_mtrl.png
new file mode 100644
index 0000000..a8aefdb
--- /dev/null
+++ b/core/res/res/drawable-mdpi/ic_media_route_connecting_light_25_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_media_route_connecting_light_26_mtrl.png b/core/res/res/drawable-mdpi/ic_media_route_connecting_light_26_mtrl.png
new file mode 100644
index 0000000..4716d66
--- /dev/null
+++ b/core/res/res/drawable-mdpi/ic_media_route_connecting_light_26_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_media_route_connecting_light_27_mtrl.png b/core/res/res/drawable-mdpi/ic_media_route_connecting_light_27_mtrl.png
new file mode 100644
index 0000000..fdeaf4f
--- /dev/null
+++ b/core/res/res/drawable-mdpi/ic_media_route_connecting_light_27_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_media_route_connecting_light_28_mtrl.png b/core/res/res/drawable-mdpi/ic_media_route_connecting_light_28_mtrl.png
new file mode 100644
index 0000000..9accc7a
--- /dev/null
+++ b/core/res/res/drawable-mdpi/ic_media_route_connecting_light_28_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_media_route_connecting_light_29_mtrl.png b/core/res/res/drawable-mdpi/ic_media_route_connecting_light_29_mtrl.png
new file mode 100644
index 0000000..1f0a327
--- /dev/null
+++ b/core/res/res/drawable-mdpi/ic_media_route_connecting_light_29_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_media_route_connecting_light_30_mtrl.png b/core/res/res/drawable-mdpi/ic_media_route_connecting_light_30_mtrl.png
new file mode 100644
index 0000000..d5efab4
--- /dev/null
+++ b/core/res/res/drawable-mdpi/ic_media_route_connecting_light_30_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_media_route_off_dark_mtrl.png b/core/res/res/drawable-mdpi/ic_media_route_off_dark_mtrl.png
new file mode 100644
index 0000000..9ef3ea6
--- /dev/null
+++ b/core/res/res/drawable-mdpi/ic_media_route_off_dark_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_media_route_off_light_mtrl.png b/core/res/res/drawable-mdpi/ic_media_route_off_light_mtrl.png
new file mode 100644
index 0000000..cbcc75a
--- /dev/null
+++ b/core/res/res/drawable-mdpi/ic_media_route_off_light_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_media_route_off_mtrl_alpha.png b/core/res/res/drawable-mdpi/ic_media_route_off_mtrl_alpha.png
deleted file mode 100644
index a652117..0000000
--- a/core/res/res/drawable-mdpi/ic_media_route_off_mtrl_alpha.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_media_route_on_0_mtrl_alpha.png b/core/res/res/drawable-mdpi/ic_media_route_on_0_mtrl_alpha.png
deleted file mode 100644
index 35d1c56..0000000
--- a/core/res/res/drawable-mdpi/ic_media_route_on_0_mtrl_alpha.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_media_route_on_1_mtrl_alpha.png b/core/res/res/drawable-mdpi/ic_media_route_on_1_mtrl_alpha.png
deleted file mode 100644
index 264188b..0000000
--- a/core/res/res/drawable-mdpi/ic_media_route_on_1_mtrl_alpha.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_media_route_on_2_mtrl_alpha.png b/core/res/res/drawable-mdpi/ic_media_route_on_2_mtrl_alpha.png
deleted file mode 100644
index 06ae665..0000000
--- a/core/res/res/drawable-mdpi/ic_media_route_on_2_mtrl_alpha.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_media_route_on_mtrl_alpha.png b/core/res/res/drawable-mdpi/ic_media_route_on_mtrl_alpha.png
deleted file mode 100644
index a69e301..0000000
--- a/core/res/res/drawable-mdpi/ic_media_route_on_mtrl_alpha.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/ic_media_route_connected_dark_00_mtrl.png b/core/res/res/drawable-xhdpi/ic_media_route_connected_dark_00_mtrl.png
new file mode 100644
index 0000000..0907a1e
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/ic_media_route_connected_dark_00_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/ic_media_route_connected_dark_01_mtrl.png b/core/res/res/drawable-xhdpi/ic_media_route_connected_dark_01_mtrl.png
new file mode 100644
index 0000000..365d96e
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/ic_media_route_connected_dark_01_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/ic_media_route_connected_dark_02_mtrl.png b/core/res/res/drawable-xhdpi/ic_media_route_connected_dark_02_mtrl.png
new file mode 100644
index 0000000..7191add
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/ic_media_route_connected_dark_02_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/ic_media_route_connected_dark_03_mtrl.png b/core/res/res/drawable-xhdpi/ic_media_route_connected_dark_03_mtrl.png
new file mode 100644
index 0000000..fd8a5df
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/ic_media_route_connected_dark_03_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/ic_media_route_connected_dark_04_mtrl.png b/core/res/res/drawable-xhdpi/ic_media_route_connected_dark_04_mtrl.png
new file mode 100644
index 0000000..9ae573b
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/ic_media_route_connected_dark_04_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/ic_media_route_connected_dark_05_mtrl.png b/core/res/res/drawable-xhdpi/ic_media_route_connected_dark_05_mtrl.png
new file mode 100644
index 0000000..a41d549
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/ic_media_route_connected_dark_05_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/ic_media_route_connected_dark_06_mtrl.png b/core/res/res/drawable-xhdpi/ic_media_route_connected_dark_06_mtrl.png
new file mode 100644
index 0000000..4faef98
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/ic_media_route_connected_dark_06_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/ic_media_route_connected_dark_07_mtrl.png b/core/res/res/drawable-xhdpi/ic_media_route_connected_dark_07_mtrl.png
new file mode 100644
index 0000000..337372c
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/ic_media_route_connected_dark_07_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/ic_media_route_connected_dark_08_mtrl.png b/core/res/res/drawable-xhdpi/ic_media_route_connected_dark_08_mtrl.png
new file mode 100644
index 0000000..a78de95
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/ic_media_route_connected_dark_08_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/ic_media_route_connected_dark_09_mtrl.png b/core/res/res/drawable-xhdpi/ic_media_route_connected_dark_09_mtrl.png
new file mode 100644
index 0000000..74f8912
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/ic_media_route_connected_dark_09_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/ic_media_route_connected_dark_10_mtrl.png b/core/res/res/drawable-xhdpi/ic_media_route_connected_dark_10_mtrl.png
new file mode 100644
index 0000000..0b868fb
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/ic_media_route_connected_dark_10_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/ic_media_route_connected_dark_11_mtrl.png b/core/res/res/drawable-xhdpi/ic_media_route_connected_dark_11_mtrl.png
new file mode 100644
index 0000000..8c381cc
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/ic_media_route_connected_dark_11_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/ic_media_route_connected_dark_12_mtrl.png b/core/res/res/drawable-xhdpi/ic_media_route_connected_dark_12_mtrl.png
new file mode 100644
index 0000000..17e40a0
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/ic_media_route_connected_dark_12_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/ic_media_route_connected_dark_13_mtrl.png b/core/res/res/drawable-xhdpi/ic_media_route_connected_dark_13_mtrl.png
new file mode 100644
index 0000000..5280b2f
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/ic_media_route_connected_dark_13_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/ic_media_route_connected_dark_14_mtrl.png b/core/res/res/drawable-xhdpi/ic_media_route_connected_dark_14_mtrl.png
new file mode 100644
index 0000000..dfe29ce
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/ic_media_route_connected_dark_14_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/ic_media_route_connected_dark_15_mtrl.png b/core/res/res/drawable-xhdpi/ic_media_route_connected_dark_15_mtrl.png
new file mode 100644
index 0000000..663d361
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/ic_media_route_connected_dark_15_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/ic_media_route_connected_dark_16_mtrl.png b/core/res/res/drawable-xhdpi/ic_media_route_connected_dark_16_mtrl.png
new file mode 100644
index 0000000..5e2ecdf
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/ic_media_route_connected_dark_16_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/ic_media_route_connected_dark_17_mtrl.png b/core/res/res/drawable-xhdpi/ic_media_route_connected_dark_17_mtrl.png
new file mode 100644
index 0000000..8d6c1f24
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/ic_media_route_connected_dark_17_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/ic_media_route_connected_dark_18_mtrl.png b/core/res/res/drawable-xhdpi/ic_media_route_connected_dark_18_mtrl.png
new file mode 100644
index 0000000..4edd1ac
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/ic_media_route_connected_dark_18_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/ic_media_route_connected_dark_19_mtrl.png b/core/res/res/drawable-xhdpi/ic_media_route_connected_dark_19_mtrl.png
new file mode 100644
index 0000000..af45ea7
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/ic_media_route_connected_dark_19_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/ic_media_route_connected_dark_20_mtrl.png b/core/res/res/drawable-xhdpi/ic_media_route_connected_dark_20_mtrl.png
new file mode 100644
index 0000000..f075756
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/ic_media_route_connected_dark_20_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/ic_media_route_connected_dark_21_mtrl.png b/core/res/res/drawable-xhdpi/ic_media_route_connected_dark_21_mtrl.png
new file mode 100644
index 0000000..69bd877
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/ic_media_route_connected_dark_21_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/ic_media_route_connected_dark_22_mtrl.png b/core/res/res/drawable-xhdpi/ic_media_route_connected_dark_22_mtrl.png
new file mode 100644
index 0000000..8c956bb
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/ic_media_route_connected_dark_22_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/ic_media_route_connected_dark_23_mtrl.png b/core/res/res/drawable-xhdpi/ic_media_route_connected_dark_23_mtrl.png
new file mode 100644
index 0000000..76bf9ab
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/ic_media_route_connected_dark_23_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/ic_media_route_connected_dark_24_mtrl.png b/core/res/res/drawable-xhdpi/ic_media_route_connected_dark_24_mtrl.png
new file mode 100644
index 0000000..1a7f9a1
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/ic_media_route_connected_dark_24_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/ic_media_route_connected_dark_25_mtrl.png b/core/res/res/drawable-xhdpi/ic_media_route_connected_dark_25_mtrl.png
new file mode 100644
index 0000000..1275eaa
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/ic_media_route_connected_dark_25_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/ic_media_route_connected_dark_26_mtrl.png b/core/res/res/drawable-xhdpi/ic_media_route_connected_dark_26_mtrl.png
new file mode 100644
index 0000000..c4ed71d
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/ic_media_route_connected_dark_26_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/ic_media_route_connected_dark_27_mtrl.png b/core/res/res/drawable-xhdpi/ic_media_route_connected_dark_27_mtrl.png
new file mode 100644
index 0000000..03252d7
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/ic_media_route_connected_dark_27_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/ic_media_route_connected_dark_28_mtrl.png b/core/res/res/drawable-xhdpi/ic_media_route_connected_dark_28_mtrl.png
new file mode 100644
index 0000000..ea8ee85
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/ic_media_route_connected_dark_28_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/ic_media_route_connected_dark_29_mtrl.png b/core/res/res/drawable-xhdpi/ic_media_route_connected_dark_29_mtrl.png
new file mode 100644
index 0000000..b024c4e
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/ic_media_route_connected_dark_29_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/ic_media_route_connected_dark_30_mtrl.png b/core/res/res/drawable-xhdpi/ic_media_route_connected_dark_30_mtrl.png
new file mode 100644
index 0000000..35516f6
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/ic_media_route_connected_dark_30_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/ic_media_route_connected_light_00_mtrl.png b/core/res/res/drawable-xhdpi/ic_media_route_connected_light_00_mtrl.png
new file mode 100644
index 0000000..dbd9854
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/ic_media_route_connected_light_00_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/ic_media_route_connected_light_01_mtrl.png b/core/res/res/drawable-xhdpi/ic_media_route_connected_light_01_mtrl.png
new file mode 100644
index 0000000..f0c30b1
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/ic_media_route_connected_light_01_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/ic_media_route_connected_light_02_mtrl.png b/core/res/res/drawable-xhdpi/ic_media_route_connected_light_02_mtrl.png
new file mode 100644
index 0000000..f21743b
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/ic_media_route_connected_light_02_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/ic_media_route_connected_light_03_mtrl.png b/core/res/res/drawable-xhdpi/ic_media_route_connected_light_03_mtrl.png
new file mode 100644
index 0000000..0b96ae0
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/ic_media_route_connected_light_03_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/ic_media_route_connected_light_04_mtrl.png b/core/res/res/drawable-xhdpi/ic_media_route_connected_light_04_mtrl.png
new file mode 100644
index 0000000..2e5407f
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/ic_media_route_connected_light_04_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/ic_media_route_connected_light_05_mtrl.png b/core/res/res/drawable-xhdpi/ic_media_route_connected_light_05_mtrl.png
new file mode 100644
index 0000000..0beb389
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/ic_media_route_connected_light_05_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/ic_media_route_connected_light_06_mtrl.png b/core/res/res/drawable-xhdpi/ic_media_route_connected_light_06_mtrl.png
new file mode 100644
index 0000000..a41586e
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/ic_media_route_connected_light_06_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/ic_media_route_connected_light_07_mtrl.png b/core/res/res/drawable-xhdpi/ic_media_route_connected_light_07_mtrl.png
new file mode 100644
index 0000000..709f650
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/ic_media_route_connected_light_07_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/ic_media_route_connected_light_08_mtrl.png b/core/res/res/drawable-xhdpi/ic_media_route_connected_light_08_mtrl.png
new file mode 100644
index 0000000..494a716
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/ic_media_route_connected_light_08_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/ic_media_route_connected_light_09_mtrl.png b/core/res/res/drawable-xhdpi/ic_media_route_connected_light_09_mtrl.png
new file mode 100644
index 0000000..9ec48f4
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/ic_media_route_connected_light_09_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/ic_media_route_connected_light_10_mtrl.png b/core/res/res/drawable-xhdpi/ic_media_route_connected_light_10_mtrl.png
new file mode 100644
index 0000000..f4c22b1
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/ic_media_route_connected_light_10_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/ic_media_route_connected_light_11_mtrl.png b/core/res/res/drawable-xhdpi/ic_media_route_connected_light_11_mtrl.png
new file mode 100644
index 0000000..6fd600f
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/ic_media_route_connected_light_11_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/ic_media_route_connected_light_12_mtrl.png b/core/res/res/drawable-xhdpi/ic_media_route_connected_light_12_mtrl.png
new file mode 100644
index 0000000..cfee3e1
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/ic_media_route_connected_light_12_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/ic_media_route_connected_light_13_mtrl.png b/core/res/res/drawable-xhdpi/ic_media_route_connected_light_13_mtrl.png
new file mode 100644
index 0000000..cc79904
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/ic_media_route_connected_light_13_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/ic_media_route_connected_light_14_mtrl.png b/core/res/res/drawable-xhdpi/ic_media_route_connected_light_14_mtrl.png
new file mode 100644
index 0000000..28c7441
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/ic_media_route_connected_light_14_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/ic_media_route_connected_light_15_mtrl.png b/core/res/res/drawable-xhdpi/ic_media_route_connected_light_15_mtrl.png
new file mode 100644
index 0000000..6c7e534
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/ic_media_route_connected_light_15_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/ic_media_route_connected_light_16_mtrl.png b/core/res/res/drawable-xhdpi/ic_media_route_connected_light_16_mtrl.png
new file mode 100644
index 0000000..e7d20d7
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/ic_media_route_connected_light_16_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/ic_media_route_connected_light_17_mtrl.png b/core/res/res/drawable-xhdpi/ic_media_route_connected_light_17_mtrl.png
new file mode 100644
index 0000000..ea492d2
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/ic_media_route_connected_light_17_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/ic_media_route_connected_light_18_mtrl.png b/core/res/res/drawable-xhdpi/ic_media_route_connected_light_18_mtrl.png
new file mode 100644
index 0000000..92c6ad8
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/ic_media_route_connected_light_18_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/ic_media_route_connected_light_19_mtrl.png b/core/res/res/drawable-xhdpi/ic_media_route_connected_light_19_mtrl.png
new file mode 100644
index 0000000..f666d89
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/ic_media_route_connected_light_19_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/ic_media_route_connected_light_20_mtrl.png b/core/res/res/drawable-xhdpi/ic_media_route_connected_light_20_mtrl.png
new file mode 100644
index 0000000..c2b600d
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/ic_media_route_connected_light_20_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/ic_media_route_connected_light_21_mtrl.png b/core/res/res/drawable-xhdpi/ic_media_route_connected_light_21_mtrl.png
new file mode 100644
index 0000000..31736aa
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/ic_media_route_connected_light_21_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/ic_media_route_connected_light_22_mtrl.png b/core/res/res/drawable-xhdpi/ic_media_route_connected_light_22_mtrl.png
new file mode 100644
index 0000000..3032ef0
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/ic_media_route_connected_light_22_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/ic_media_route_connected_light_23_mtrl.png b/core/res/res/drawable-xhdpi/ic_media_route_connected_light_23_mtrl.png
new file mode 100644
index 0000000..1d5cbc0
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/ic_media_route_connected_light_23_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/ic_media_route_connected_light_24_mtrl.png b/core/res/res/drawable-xhdpi/ic_media_route_connected_light_24_mtrl.png
new file mode 100644
index 0000000..84ebcc3
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/ic_media_route_connected_light_24_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/ic_media_route_connected_light_25_mtrl.png b/core/res/res/drawable-xhdpi/ic_media_route_connected_light_25_mtrl.png
new file mode 100644
index 0000000..505aadf
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/ic_media_route_connected_light_25_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/ic_media_route_connected_light_26_mtrl.png b/core/res/res/drawable-xhdpi/ic_media_route_connected_light_26_mtrl.png
new file mode 100644
index 0000000..3b8c662
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/ic_media_route_connected_light_26_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/ic_media_route_connected_light_27_mtrl.png b/core/res/res/drawable-xhdpi/ic_media_route_connected_light_27_mtrl.png
new file mode 100644
index 0000000..d650c07
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/ic_media_route_connected_light_27_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/ic_media_route_connected_light_28_mtrl.png b/core/res/res/drawable-xhdpi/ic_media_route_connected_light_28_mtrl.png
new file mode 100644
index 0000000..3caa40d
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/ic_media_route_connected_light_28_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/ic_media_route_connected_light_29_mtrl.png b/core/res/res/drawable-xhdpi/ic_media_route_connected_light_29_mtrl.png
new file mode 100644
index 0000000..d3f8ea6
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/ic_media_route_connected_light_29_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/ic_media_route_connected_light_30_mtrl.png b/core/res/res/drawable-xhdpi/ic_media_route_connected_light_30_mtrl.png
new file mode 100644
index 0000000..b54396c
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/ic_media_route_connected_light_30_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/ic_media_route_connecting_dark_00_mtrl.png b/core/res/res/drawable-xhdpi/ic_media_route_connecting_dark_00_mtrl.png
new file mode 100644
index 0000000..0907a1e
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/ic_media_route_connecting_dark_00_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/ic_media_route_connecting_dark_01_mtrl.png b/core/res/res/drawable-xhdpi/ic_media_route_connecting_dark_01_mtrl.png
new file mode 100644
index 0000000..365d96e
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/ic_media_route_connecting_dark_01_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/ic_media_route_connecting_dark_02_mtrl.png b/core/res/res/drawable-xhdpi/ic_media_route_connecting_dark_02_mtrl.png
new file mode 100644
index 0000000..7191add
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/ic_media_route_connecting_dark_02_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/ic_media_route_connecting_dark_03_mtrl.png b/core/res/res/drawable-xhdpi/ic_media_route_connecting_dark_03_mtrl.png
new file mode 100644
index 0000000..fd8a5df
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/ic_media_route_connecting_dark_03_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/ic_media_route_connecting_dark_04_mtrl.png b/core/res/res/drawable-xhdpi/ic_media_route_connecting_dark_04_mtrl.png
new file mode 100644
index 0000000..9ae573b
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/ic_media_route_connecting_dark_04_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/ic_media_route_connecting_dark_05_mtrl.png b/core/res/res/drawable-xhdpi/ic_media_route_connecting_dark_05_mtrl.png
new file mode 100644
index 0000000..a41d549
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/ic_media_route_connecting_dark_05_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/ic_media_route_connecting_dark_06_mtrl.png b/core/res/res/drawable-xhdpi/ic_media_route_connecting_dark_06_mtrl.png
new file mode 100644
index 0000000..4faef98
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/ic_media_route_connecting_dark_06_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/ic_media_route_connecting_dark_07_mtrl.png b/core/res/res/drawable-xhdpi/ic_media_route_connecting_dark_07_mtrl.png
new file mode 100644
index 0000000..337372c
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/ic_media_route_connecting_dark_07_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/ic_media_route_connecting_dark_08_mtrl.png b/core/res/res/drawable-xhdpi/ic_media_route_connecting_dark_08_mtrl.png
new file mode 100644
index 0000000..a78de95
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/ic_media_route_connecting_dark_08_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/ic_media_route_connecting_dark_09_mtrl.png b/core/res/res/drawable-xhdpi/ic_media_route_connecting_dark_09_mtrl.png
new file mode 100644
index 0000000..74f8912
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/ic_media_route_connecting_dark_09_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/ic_media_route_connecting_dark_10_mtrl.png b/core/res/res/drawable-xhdpi/ic_media_route_connecting_dark_10_mtrl.png
new file mode 100644
index 0000000..0b868fb
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/ic_media_route_connecting_dark_10_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/ic_media_route_connecting_dark_11_mtrl.png b/core/res/res/drawable-xhdpi/ic_media_route_connecting_dark_11_mtrl.png
new file mode 100644
index 0000000..90ca46c
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/ic_media_route_connecting_dark_11_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/ic_media_route_connecting_dark_12_mtrl.png b/core/res/res/drawable-xhdpi/ic_media_route_connecting_dark_12_mtrl.png
new file mode 100644
index 0000000..db02019
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/ic_media_route_connecting_dark_12_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/ic_media_route_connecting_dark_13_mtrl.png b/core/res/res/drawable-xhdpi/ic_media_route_connecting_dark_13_mtrl.png
new file mode 100644
index 0000000..ce5ee81
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/ic_media_route_connecting_dark_13_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/ic_media_route_connecting_dark_14_mtrl.png b/core/res/res/drawable-xhdpi/ic_media_route_connecting_dark_14_mtrl.png
new file mode 100644
index 0000000..3dcbef6
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/ic_media_route_connecting_dark_14_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/ic_media_route_connecting_dark_15_mtrl.png b/core/res/res/drawable-xhdpi/ic_media_route_connecting_dark_15_mtrl.png
new file mode 100644
index 0000000..7646f18
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/ic_media_route_connecting_dark_15_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/ic_media_route_connecting_dark_16_mtrl.png b/core/res/res/drawable-xhdpi/ic_media_route_connecting_dark_16_mtrl.png
new file mode 100644
index 0000000..c16cac9
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/ic_media_route_connecting_dark_16_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/ic_media_route_connecting_dark_17_mtrl.png b/core/res/res/drawable-xhdpi/ic_media_route_connecting_dark_17_mtrl.png
new file mode 100644
index 0000000..7dbc57c
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/ic_media_route_connecting_dark_17_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/ic_media_route_connecting_dark_18_mtrl.png b/core/res/res/drawable-xhdpi/ic_media_route_connecting_dark_18_mtrl.png
new file mode 100644
index 0000000..39d17e0
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/ic_media_route_connecting_dark_18_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/ic_media_route_connecting_dark_19_mtrl.png b/core/res/res/drawable-xhdpi/ic_media_route_connecting_dark_19_mtrl.png
new file mode 100644
index 0000000..ad0e121
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/ic_media_route_connecting_dark_19_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/ic_media_route_connecting_dark_20_mtrl.png b/core/res/res/drawable-xhdpi/ic_media_route_connecting_dark_20_mtrl.png
new file mode 100644
index 0000000..30ea5df
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/ic_media_route_connecting_dark_20_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/ic_media_route_connecting_dark_21_mtrl.png b/core/res/res/drawable-xhdpi/ic_media_route_connecting_dark_21_mtrl.png
new file mode 100644
index 0000000..151a222
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/ic_media_route_connecting_dark_21_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/ic_media_route_connecting_dark_22_mtrl.png b/core/res/res/drawable-xhdpi/ic_media_route_connecting_dark_22_mtrl.png
new file mode 100644
index 0000000..7d4e660
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/ic_media_route_connecting_dark_22_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/ic_media_route_connecting_dark_23_mtrl.png b/core/res/res/drawable-xhdpi/ic_media_route_connecting_dark_23_mtrl.png
new file mode 100644
index 0000000..8e26d54
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/ic_media_route_connecting_dark_23_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/ic_media_route_connecting_dark_24_mtrl.png b/core/res/res/drawable-xhdpi/ic_media_route_connecting_dark_24_mtrl.png
new file mode 100644
index 0000000..e85f239
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/ic_media_route_connecting_dark_24_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/ic_media_route_connecting_dark_25_mtrl.png b/core/res/res/drawable-xhdpi/ic_media_route_connecting_dark_25_mtrl.png
new file mode 100644
index 0000000..81f38ac
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/ic_media_route_connecting_dark_25_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/ic_media_route_connecting_dark_26_mtrl.png b/core/res/res/drawable-xhdpi/ic_media_route_connecting_dark_26_mtrl.png
new file mode 100644
index 0000000..5a50e6a
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/ic_media_route_connecting_dark_26_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/ic_media_route_connecting_dark_27_mtrl.png b/core/res/res/drawable-xhdpi/ic_media_route_connecting_dark_27_mtrl.png
new file mode 100644
index 0000000..6069dad
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/ic_media_route_connecting_dark_27_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/ic_media_route_connecting_dark_28_mtrl.png b/core/res/res/drawable-xhdpi/ic_media_route_connecting_dark_28_mtrl.png
new file mode 100644
index 0000000..37cc4c6
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/ic_media_route_connecting_dark_28_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/ic_media_route_connecting_dark_29_mtrl.png b/core/res/res/drawable-xhdpi/ic_media_route_connecting_dark_29_mtrl.png
new file mode 100644
index 0000000..aff0d4c
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/ic_media_route_connecting_dark_29_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/ic_media_route_connecting_dark_30_mtrl.png b/core/res/res/drawable-xhdpi/ic_media_route_connecting_dark_30_mtrl.png
new file mode 100644
index 0000000..0907a1e
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/ic_media_route_connecting_dark_30_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/ic_media_route_connecting_light_00_mtrl.png b/core/res/res/drawable-xhdpi/ic_media_route_connecting_light_00_mtrl.png
new file mode 100644
index 0000000..dbd9854
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/ic_media_route_connecting_light_00_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/ic_media_route_connecting_light_01_mtrl.png b/core/res/res/drawable-xhdpi/ic_media_route_connecting_light_01_mtrl.png
new file mode 100644
index 0000000..f0c30b1
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/ic_media_route_connecting_light_01_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/ic_media_route_connecting_light_02_mtrl.png b/core/res/res/drawable-xhdpi/ic_media_route_connecting_light_02_mtrl.png
new file mode 100644
index 0000000..f21743b
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/ic_media_route_connecting_light_02_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/ic_media_route_connecting_light_03_mtrl.png b/core/res/res/drawable-xhdpi/ic_media_route_connecting_light_03_mtrl.png
new file mode 100644
index 0000000..0b96ae0
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/ic_media_route_connecting_light_03_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/ic_media_route_connecting_light_04_mtrl.png b/core/res/res/drawable-xhdpi/ic_media_route_connecting_light_04_mtrl.png
new file mode 100644
index 0000000..2e5407f
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/ic_media_route_connecting_light_04_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/ic_media_route_connecting_light_05_mtrl.png b/core/res/res/drawable-xhdpi/ic_media_route_connecting_light_05_mtrl.png
new file mode 100644
index 0000000..302fafa
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/ic_media_route_connecting_light_05_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/ic_media_route_connecting_light_06_mtrl.png b/core/res/res/drawable-xhdpi/ic_media_route_connecting_light_06_mtrl.png
new file mode 100644
index 0000000..a41586e
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/ic_media_route_connecting_light_06_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/ic_media_route_connecting_light_07_mtrl.png b/core/res/res/drawable-xhdpi/ic_media_route_connecting_light_07_mtrl.png
new file mode 100644
index 0000000..709f650
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/ic_media_route_connecting_light_07_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/ic_media_route_connecting_light_08_mtrl.png b/core/res/res/drawable-xhdpi/ic_media_route_connecting_light_08_mtrl.png
new file mode 100644
index 0000000..494a716
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/ic_media_route_connecting_light_08_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/ic_media_route_connecting_light_09_mtrl.png b/core/res/res/drawable-xhdpi/ic_media_route_connecting_light_09_mtrl.png
new file mode 100644
index 0000000..9ec48f4
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/ic_media_route_connecting_light_09_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/ic_media_route_connecting_light_10_mtrl.png b/core/res/res/drawable-xhdpi/ic_media_route_connecting_light_10_mtrl.png
new file mode 100644
index 0000000..f4c22b1
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/ic_media_route_connecting_light_10_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/ic_media_route_connecting_light_11_mtrl.png b/core/res/res/drawable-xhdpi/ic_media_route_connecting_light_11_mtrl.png
new file mode 100644
index 0000000..7d72685
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/ic_media_route_connecting_light_11_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/ic_media_route_connecting_light_12_mtrl.png b/core/res/res/drawable-xhdpi/ic_media_route_connecting_light_12_mtrl.png
new file mode 100644
index 0000000..4c545ef
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/ic_media_route_connecting_light_12_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/ic_media_route_connecting_light_13_mtrl.png b/core/res/res/drawable-xhdpi/ic_media_route_connecting_light_13_mtrl.png
new file mode 100644
index 0000000..a8e9b00
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/ic_media_route_connecting_light_13_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/ic_media_route_connecting_light_14_mtrl.png b/core/res/res/drawable-xhdpi/ic_media_route_connecting_light_14_mtrl.png
new file mode 100644
index 0000000..b67140d
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/ic_media_route_connecting_light_14_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/ic_media_route_connecting_light_15_mtrl.png b/core/res/res/drawable-xhdpi/ic_media_route_connecting_light_15_mtrl.png
new file mode 100644
index 0000000..166bdae
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/ic_media_route_connecting_light_15_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/ic_media_route_connecting_light_16_mtrl.png b/core/res/res/drawable-xhdpi/ic_media_route_connecting_light_16_mtrl.png
new file mode 100644
index 0000000..1bda465
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/ic_media_route_connecting_light_16_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/ic_media_route_connecting_light_17_mtrl.png b/core/res/res/drawable-xhdpi/ic_media_route_connecting_light_17_mtrl.png
new file mode 100644
index 0000000..cc8ec51
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/ic_media_route_connecting_light_17_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/ic_media_route_connecting_light_18_mtrl.png b/core/res/res/drawable-xhdpi/ic_media_route_connecting_light_18_mtrl.png
new file mode 100644
index 0000000..cf2013c
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/ic_media_route_connecting_light_18_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/ic_media_route_connecting_light_19_mtrl.png b/core/res/res/drawable-xhdpi/ic_media_route_connecting_light_19_mtrl.png
new file mode 100644
index 0000000..daa1ca1
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/ic_media_route_connecting_light_19_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/ic_media_route_connecting_light_20_mtrl.png b/core/res/res/drawable-xhdpi/ic_media_route_connecting_light_20_mtrl.png
new file mode 100644
index 0000000..56355d9
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/ic_media_route_connecting_light_20_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/ic_media_route_connecting_light_21_mtrl.png b/core/res/res/drawable-xhdpi/ic_media_route_connecting_light_21_mtrl.png
new file mode 100644
index 0000000..42657ec
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/ic_media_route_connecting_light_21_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/ic_media_route_connecting_light_22_mtrl.png b/core/res/res/drawable-xhdpi/ic_media_route_connecting_light_22_mtrl.png
new file mode 100644
index 0000000..53e4121
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/ic_media_route_connecting_light_22_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/ic_media_route_connecting_light_23_mtrl.png b/core/res/res/drawable-xhdpi/ic_media_route_connecting_light_23_mtrl.png
new file mode 100644
index 0000000..f6e9ffa
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/ic_media_route_connecting_light_23_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/ic_media_route_connecting_light_24_mtrl.png b/core/res/res/drawable-xhdpi/ic_media_route_connecting_light_24_mtrl.png
new file mode 100644
index 0000000..a313e16
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/ic_media_route_connecting_light_24_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/ic_media_route_connecting_light_25_mtrl.png b/core/res/res/drawable-xhdpi/ic_media_route_connecting_light_25_mtrl.png
new file mode 100644
index 0000000..40034b6
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/ic_media_route_connecting_light_25_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/ic_media_route_connecting_light_26_mtrl.png b/core/res/res/drawable-xhdpi/ic_media_route_connecting_light_26_mtrl.png
new file mode 100644
index 0000000..678f972
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/ic_media_route_connecting_light_26_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/ic_media_route_connecting_light_27_mtrl.png b/core/res/res/drawable-xhdpi/ic_media_route_connecting_light_27_mtrl.png
new file mode 100644
index 0000000..d2a3707
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/ic_media_route_connecting_light_27_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/ic_media_route_connecting_light_28_mtrl.png b/core/res/res/drawable-xhdpi/ic_media_route_connecting_light_28_mtrl.png
new file mode 100644
index 0000000..427c52e
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/ic_media_route_connecting_light_28_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/ic_media_route_connecting_light_29_mtrl.png b/core/res/res/drawable-xhdpi/ic_media_route_connecting_light_29_mtrl.png
new file mode 100644
index 0000000..8a4d807
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/ic_media_route_connecting_light_29_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/ic_media_route_connecting_light_30_mtrl.png b/core/res/res/drawable-xhdpi/ic_media_route_connecting_light_30_mtrl.png
new file mode 100644
index 0000000..dbd9854
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/ic_media_route_connecting_light_30_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/ic_media_route_off_dark_mtrl.png b/core/res/res/drawable-xhdpi/ic_media_route_off_dark_mtrl.png
new file mode 100644
index 0000000..1d85b66
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/ic_media_route_off_dark_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/ic_media_route_off_light_mtrl.png b/core/res/res/drawable-xhdpi/ic_media_route_off_light_mtrl.png
new file mode 100644
index 0000000..231aec4
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/ic_media_route_off_light_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/ic_media_route_off_mtrl_alpha.png b/core/res/res/drawable-xhdpi/ic_media_route_off_mtrl_alpha.png
deleted file mode 100644
index e75ffb8..0000000
--- a/core/res/res/drawable-xhdpi/ic_media_route_off_mtrl_alpha.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/ic_media_route_on_0_mtrl_alpha.png b/core/res/res/drawable-xhdpi/ic_media_route_on_0_mtrl_alpha.png
deleted file mode 100644
index 1b9fc12..0000000
--- a/core/res/res/drawable-xhdpi/ic_media_route_on_0_mtrl_alpha.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/ic_media_route_on_1_mtrl_alpha.png b/core/res/res/drawable-xhdpi/ic_media_route_on_1_mtrl_alpha.png
deleted file mode 100644
index f63d47e..0000000
--- a/core/res/res/drawable-xhdpi/ic_media_route_on_1_mtrl_alpha.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/ic_media_route_on_2_mtrl_alpha.png b/core/res/res/drawable-xhdpi/ic_media_route_on_2_mtrl_alpha.png
deleted file mode 100644
index fe005d9..0000000
--- a/core/res/res/drawable-xhdpi/ic_media_route_on_2_mtrl_alpha.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/ic_media_route_on_mtrl_alpha.png b/core/res/res/drawable-xhdpi/ic_media_route_on_mtrl_alpha.png
deleted file mode 100644
index ea86408..0000000
--- a/core/res/res/drawable-xhdpi/ic_media_route_on_mtrl_alpha.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/ic_media_route_connected_dark_00_mtrl.png b/core/res/res/drawable-xxhdpi/ic_media_route_connected_dark_00_mtrl.png
new file mode 100644
index 0000000..97949c6
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/ic_media_route_connected_dark_00_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/ic_media_route_connected_dark_01_mtrl.png b/core/res/res/drawable-xxhdpi/ic_media_route_connected_dark_01_mtrl.png
new file mode 100644
index 0000000..3f35bcd
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/ic_media_route_connected_dark_01_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/ic_media_route_connected_dark_02_mtrl.png b/core/res/res/drawable-xxhdpi/ic_media_route_connected_dark_02_mtrl.png
new file mode 100644
index 0000000..0ed2094
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/ic_media_route_connected_dark_02_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/ic_media_route_connected_dark_03_mtrl.png b/core/res/res/drawable-xxhdpi/ic_media_route_connected_dark_03_mtrl.png
new file mode 100644
index 0000000..7fa7f01
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/ic_media_route_connected_dark_03_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/ic_media_route_connected_dark_04_mtrl.png b/core/res/res/drawable-xxhdpi/ic_media_route_connected_dark_04_mtrl.png
new file mode 100644
index 0000000..b7e1ea1
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/ic_media_route_connected_dark_04_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/ic_media_route_connected_dark_05_mtrl.png b/core/res/res/drawable-xxhdpi/ic_media_route_connected_dark_05_mtrl.png
new file mode 100644
index 0000000..ff07932
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/ic_media_route_connected_dark_05_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/ic_media_route_connected_dark_06_mtrl.png b/core/res/res/drawable-xxhdpi/ic_media_route_connected_dark_06_mtrl.png
new file mode 100644
index 0000000..4a7b283
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/ic_media_route_connected_dark_06_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/ic_media_route_connected_dark_07_mtrl.png b/core/res/res/drawable-xxhdpi/ic_media_route_connected_dark_07_mtrl.png
new file mode 100644
index 0000000..41c4443
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/ic_media_route_connected_dark_07_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/ic_media_route_connected_dark_08_mtrl.png b/core/res/res/drawable-xxhdpi/ic_media_route_connected_dark_08_mtrl.png
new file mode 100644
index 0000000..461fa46
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/ic_media_route_connected_dark_08_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/ic_media_route_connected_dark_09_mtrl.png b/core/res/res/drawable-xxhdpi/ic_media_route_connected_dark_09_mtrl.png
new file mode 100644
index 0000000..f96d1f0
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/ic_media_route_connected_dark_09_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/ic_media_route_connected_dark_10_mtrl.png b/core/res/res/drawable-xxhdpi/ic_media_route_connected_dark_10_mtrl.png
new file mode 100644
index 0000000..87a9b49
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/ic_media_route_connected_dark_10_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/ic_media_route_connected_dark_11_mtrl.png b/core/res/res/drawable-xxhdpi/ic_media_route_connected_dark_11_mtrl.png
new file mode 100644
index 0000000..96b5366
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/ic_media_route_connected_dark_11_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/ic_media_route_connected_dark_12_mtrl.png b/core/res/res/drawable-xxhdpi/ic_media_route_connected_dark_12_mtrl.png
new file mode 100644
index 0000000..532a86d
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/ic_media_route_connected_dark_12_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/ic_media_route_connected_dark_13_mtrl.png b/core/res/res/drawable-xxhdpi/ic_media_route_connected_dark_13_mtrl.png
new file mode 100644
index 0000000..9f37eb2
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/ic_media_route_connected_dark_13_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/ic_media_route_connected_dark_14_mtrl.png b/core/res/res/drawable-xxhdpi/ic_media_route_connected_dark_14_mtrl.png
new file mode 100644
index 0000000..2437e0e
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/ic_media_route_connected_dark_14_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/ic_media_route_connected_dark_15_mtrl.png b/core/res/res/drawable-xxhdpi/ic_media_route_connected_dark_15_mtrl.png
new file mode 100644
index 0000000..79bc6ce
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/ic_media_route_connected_dark_15_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/ic_media_route_connected_dark_16_mtrl.png b/core/res/res/drawable-xxhdpi/ic_media_route_connected_dark_16_mtrl.png
new file mode 100644
index 0000000..9dbbe2f
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/ic_media_route_connected_dark_16_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/ic_media_route_connected_dark_17_mtrl.png b/core/res/res/drawable-xxhdpi/ic_media_route_connected_dark_17_mtrl.png
new file mode 100644
index 0000000..1da48b8
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/ic_media_route_connected_dark_17_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/ic_media_route_connected_dark_18_mtrl.png b/core/res/res/drawable-xxhdpi/ic_media_route_connected_dark_18_mtrl.png
new file mode 100644
index 0000000..8963582
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/ic_media_route_connected_dark_18_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/ic_media_route_connected_dark_19_mtrl.png b/core/res/res/drawable-xxhdpi/ic_media_route_connected_dark_19_mtrl.png
new file mode 100644
index 0000000..ec4be83
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/ic_media_route_connected_dark_19_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/ic_media_route_connected_dark_20_mtrl.png b/core/res/res/drawable-xxhdpi/ic_media_route_connected_dark_20_mtrl.png
new file mode 100644
index 0000000..4658fc3
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/ic_media_route_connected_dark_20_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/ic_media_route_connected_dark_21_mtrl.png b/core/res/res/drawable-xxhdpi/ic_media_route_connected_dark_21_mtrl.png
new file mode 100644
index 0000000..3ecd6bd
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/ic_media_route_connected_dark_21_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/ic_media_route_connected_dark_22_mtrl.png b/core/res/res/drawable-xxhdpi/ic_media_route_connected_dark_22_mtrl.png
new file mode 100644
index 0000000..565e8de
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/ic_media_route_connected_dark_22_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/ic_media_route_connected_dark_23_mtrl.png b/core/res/res/drawable-xxhdpi/ic_media_route_connected_dark_23_mtrl.png
new file mode 100644
index 0000000..9c5c607
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/ic_media_route_connected_dark_23_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/ic_media_route_connected_dark_24_mtrl.png b/core/res/res/drawable-xxhdpi/ic_media_route_connected_dark_24_mtrl.png
new file mode 100644
index 0000000..87b84a7
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/ic_media_route_connected_dark_24_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/ic_media_route_connected_dark_25_mtrl.png b/core/res/res/drawable-xxhdpi/ic_media_route_connected_dark_25_mtrl.png
new file mode 100644
index 0000000..9e12e5b
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/ic_media_route_connected_dark_25_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/ic_media_route_connected_dark_26_mtrl.png b/core/res/res/drawable-xxhdpi/ic_media_route_connected_dark_26_mtrl.png
new file mode 100644
index 0000000..85e8791
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/ic_media_route_connected_dark_26_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/ic_media_route_connected_dark_27_mtrl.png b/core/res/res/drawable-xxhdpi/ic_media_route_connected_dark_27_mtrl.png
new file mode 100644
index 0000000..a7bd75d
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/ic_media_route_connected_dark_27_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/ic_media_route_connected_dark_28_mtrl.png b/core/res/res/drawable-xxhdpi/ic_media_route_connected_dark_28_mtrl.png
new file mode 100644
index 0000000..204f393
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/ic_media_route_connected_dark_28_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/ic_media_route_connected_dark_29_mtrl.png b/core/res/res/drawable-xxhdpi/ic_media_route_connected_dark_29_mtrl.png
new file mode 100644
index 0000000..78f787a
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/ic_media_route_connected_dark_29_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/ic_media_route_connected_dark_30_mtrl.png b/core/res/res/drawable-xxhdpi/ic_media_route_connected_dark_30_mtrl.png
new file mode 100644
index 0000000..a2e61c2
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/ic_media_route_connected_dark_30_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/ic_media_route_connected_light_00_mtrl.png b/core/res/res/drawable-xxhdpi/ic_media_route_connected_light_00_mtrl.png
new file mode 100644
index 0000000..80d5155
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/ic_media_route_connected_light_00_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/ic_media_route_connected_light_01_mtrl.png b/core/res/res/drawable-xxhdpi/ic_media_route_connected_light_01_mtrl.png
new file mode 100644
index 0000000..8ec05ae
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/ic_media_route_connected_light_01_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/ic_media_route_connected_light_02_mtrl.png b/core/res/res/drawable-xxhdpi/ic_media_route_connected_light_02_mtrl.png
new file mode 100644
index 0000000..42cd13c
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/ic_media_route_connected_light_02_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/ic_media_route_connected_light_03_mtrl.png b/core/res/res/drawable-xxhdpi/ic_media_route_connected_light_03_mtrl.png
new file mode 100644
index 0000000..f43727f
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/ic_media_route_connected_light_03_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/ic_media_route_connected_light_04_mtrl.png b/core/res/res/drawable-xxhdpi/ic_media_route_connected_light_04_mtrl.png
new file mode 100644
index 0000000..e61a05c
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/ic_media_route_connected_light_04_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/ic_media_route_connected_light_05_mtrl.png b/core/res/res/drawable-xxhdpi/ic_media_route_connected_light_05_mtrl.png
new file mode 100644
index 0000000..8dc5976
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/ic_media_route_connected_light_05_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/ic_media_route_connected_light_06_mtrl.png b/core/res/res/drawable-xxhdpi/ic_media_route_connected_light_06_mtrl.png
new file mode 100644
index 0000000..ead968e
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/ic_media_route_connected_light_06_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/ic_media_route_connected_light_07_mtrl.png b/core/res/res/drawable-xxhdpi/ic_media_route_connected_light_07_mtrl.png
new file mode 100644
index 0000000..52aa7d5
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/ic_media_route_connected_light_07_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/ic_media_route_connected_light_08_mtrl.png b/core/res/res/drawable-xxhdpi/ic_media_route_connected_light_08_mtrl.png
new file mode 100644
index 0000000..58986d4
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/ic_media_route_connected_light_08_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/ic_media_route_connected_light_09_mtrl.png b/core/res/res/drawable-xxhdpi/ic_media_route_connected_light_09_mtrl.png
new file mode 100644
index 0000000..42d6c7c
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/ic_media_route_connected_light_09_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/ic_media_route_connected_light_10_mtrl.png b/core/res/res/drawable-xxhdpi/ic_media_route_connected_light_10_mtrl.png
new file mode 100644
index 0000000..e7e0e60
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/ic_media_route_connected_light_10_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/ic_media_route_connected_light_11_mtrl.png b/core/res/res/drawable-xxhdpi/ic_media_route_connected_light_11_mtrl.png
new file mode 100644
index 0000000..75bd261
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/ic_media_route_connected_light_11_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/ic_media_route_connected_light_12_mtrl.png b/core/res/res/drawable-xxhdpi/ic_media_route_connected_light_12_mtrl.png
new file mode 100644
index 0000000..5207dac
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/ic_media_route_connected_light_12_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/ic_media_route_connected_light_13_mtrl.png b/core/res/res/drawable-xxhdpi/ic_media_route_connected_light_13_mtrl.png
new file mode 100644
index 0000000..c2f69e7
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/ic_media_route_connected_light_13_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/ic_media_route_connected_light_14_mtrl.png b/core/res/res/drawable-xxhdpi/ic_media_route_connected_light_14_mtrl.png
new file mode 100644
index 0000000..167f882
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/ic_media_route_connected_light_14_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/ic_media_route_connected_light_15_mtrl.png b/core/res/res/drawable-xxhdpi/ic_media_route_connected_light_15_mtrl.png
new file mode 100644
index 0000000..d6a85f2
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/ic_media_route_connected_light_15_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/ic_media_route_connected_light_16_mtrl.png b/core/res/res/drawable-xxhdpi/ic_media_route_connected_light_16_mtrl.png
new file mode 100644
index 0000000..cb0390d
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/ic_media_route_connected_light_16_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/ic_media_route_connected_light_17_mtrl.png b/core/res/res/drawable-xxhdpi/ic_media_route_connected_light_17_mtrl.png
new file mode 100644
index 0000000..6a65d15
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/ic_media_route_connected_light_17_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/ic_media_route_connected_light_18_mtrl.png b/core/res/res/drawable-xxhdpi/ic_media_route_connected_light_18_mtrl.png
new file mode 100644
index 0000000..8086a55
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/ic_media_route_connected_light_18_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/ic_media_route_connected_light_19_mtrl.png b/core/res/res/drawable-xxhdpi/ic_media_route_connected_light_19_mtrl.png
new file mode 100644
index 0000000..eee4115
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/ic_media_route_connected_light_19_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/ic_media_route_connected_light_20_mtrl.png b/core/res/res/drawable-xxhdpi/ic_media_route_connected_light_20_mtrl.png
new file mode 100644
index 0000000..d214ad1
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/ic_media_route_connected_light_20_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/ic_media_route_connected_light_21_mtrl.png b/core/res/res/drawable-xxhdpi/ic_media_route_connected_light_21_mtrl.png
new file mode 100644
index 0000000..ac56ac4
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/ic_media_route_connected_light_21_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/ic_media_route_connected_light_22_mtrl.png b/core/res/res/drawable-xxhdpi/ic_media_route_connected_light_22_mtrl.png
new file mode 100644
index 0000000..49af14e
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/ic_media_route_connected_light_22_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/ic_media_route_connected_light_23_mtrl.png b/core/res/res/drawable-xxhdpi/ic_media_route_connected_light_23_mtrl.png
new file mode 100644
index 0000000..5003095b
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/ic_media_route_connected_light_23_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/ic_media_route_connected_light_24_mtrl.png b/core/res/res/drawable-xxhdpi/ic_media_route_connected_light_24_mtrl.png
new file mode 100644
index 0000000..62054a0
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/ic_media_route_connected_light_24_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/ic_media_route_connected_light_25_mtrl.png b/core/res/res/drawable-xxhdpi/ic_media_route_connected_light_25_mtrl.png
new file mode 100644
index 0000000..4c83dd4
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/ic_media_route_connected_light_25_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/ic_media_route_connected_light_26_mtrl.png b/core/res/res/drawable-xxhdpi/ic_media_route_connected_light_26_mtrl.png
new file mode 100644
index 0000000..a55f8e4
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/ic_media_route_connected_light_26_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/ic_media_route_connected_light_27_mtrl.png b/core/res/res/drawable-xxhdpi/ic_media_route_connected_light_27_mtrl.png
new file mode 100644
index 0000000..81567b7
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/ic_media_route_connected_light_27_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/ic_media_route_connected_light_28_mtrl.png b/core/res/res/drawable-xxhdpi/ic_media_route_connected_light_28_mtrl.png
new file mode 100644
index 0000000..4047475
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/ic_media_route_connected_light_28_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/ic_media_route_connected_light_29_mtrl.png b/core/res/res/drawable-xxhdpi/ic_media_route_connected_light_29_mtrl.png
new file mode 100644
index 0000000..23db45a
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/ic_media_route_connected_light_29_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/ic_media_route_connected_light_30_mtrl.png b/core/res/res/drawable-xxhdpi/ic_media_route_connected_light_30_mtrl.png
new file mode 100644
index 0000000..e7d9961
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/ic_media_route_connected_light_30_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/ic_media_route_connecting_dark_00_mtrl.png b/core/res/res/drawable-xxhdpi/ic_media_route_connecting_dark_00_mtrl.png
new file mode 100644
index 0000000..97949c6
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/ic_media_route_connecting_dark_00_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/ic_media_route_connecting_dark_01_mtrl.png b/core/res/res/drawable-xxhdpi/ic_media_route_connecting_dark_01_mtrl.png
new file mode 100644
index 0000000..3f35bcd
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/ic_media_route_connecting_dark_01_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/ic_media_route_connecting_dark_02_mtrl.png b/core/res/res/drawable-xxhdpi/ic_media_route_connecting_dark_02_mtrl.png
new file mode 100644
index 0000000..0ed2094
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/ic_media_route_connecting_dark_02_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/ic_media_route_connecting_dark_03_mtrl.png b/core/res/res/drawable-xxhdpi/ic_media_route_connecting_dark_03_mtrl.png
new file mode 100644
index 0000000..7fa7f01
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/ic_media_route_connecting_dark_03_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/ic_media_route_connecting_dark_04_mtrl.png b/core/res/res/drawable-xxhdpi/ic_media_route_connecting_dark_04_mtrl.png
new file mode 100644
index 0000000..b7e1ea1
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/ic_media_route_connecting_dark_04_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/ic_media_route_connecting_dark_05_mtrl.png b/core/res/res/drawable-xxhdpi/ic_media_route_connecting_dark_05_mtrl.png
new file mode 100644
index 0000000..ff07932
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/ic_media_route_connecting_dark_05_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/ic_media_route_connecting_dark_06_mtrl.png b/core/res/res/drawable-xxhdpi/ic_media_route_connecting_dark_06_mtrl.png
new file mode 100644
index 0000000..4a7b283
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/ic_media_route_connecting_dark_06_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/ic_media_route_connecting_dark_07_mtrl.png b/core/res/res/drawable-xxhdpi/ic_media_route_connecting_dark_07_mtrl.png
new file mode 100644
index 0000000..41c4443
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/ic_media_route_connecting_dark_07_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/ic_media_route_connecting_dark_08_mtrl.png b/core/res/res/drawable-xxhdpi/ic_media_route_connecting_dark_08_mtrl.png
new file mode 100644
index 0000000..461fa46
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/ic_media_route_connecting_dark_08_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/ic_media_route_connecting_dark_09_mtrl.png b/core/res/res/drawable-xxhdpi/ic_media_route_connecting_dark_09_mtrl.png
new file mode 100644
index 0000000..f96d1f0
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/ic_media_route_connecting_dark_09_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/ic_media_route_connecting_dark_10_mtrl.png b/core/res/res/drawable-xxhdpi/ic_media_route_connecting_dark_10_mtrl.png
new file mode 100644
index 0000000..87a9b49
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/ic_media_route_connecting_dark_10_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/ic_media_route_connecting_dark_11_mtrl.png b/core/res/res/drawable-xxhdpi/ic_media_route_connecting_dark_11_mtrl.png
new file mode 100644
index 0000000..685a340
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/ic_media_route_connecting_dark_11_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/ic_media_route_connecting_dark_12_mtrl.png b/core/res/res/drawable-xxhdpi/ic_media_route_connecting_dark_12_mtrl.png
new file mode 100644
index 0000000..b49c30e
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/ic_media_route_connecting_dark_12_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/ic_media_route_connecting_dark_13_mtrl.png b/core/res/res/drawable-xxhdpi/ic_media_route_connecting_dark_13_mtrl.png
new file mode 100644
index 0000000..aa92d9e
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/ic_media_route_connecting_dark_13_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/ic_media_route_connecting_dark_14_mtrl.png b/core/res/res/drawable-xxhdpi/ic_media_route_connecting_dark_14_mtrl.png
new file mode 100644
index 0000000..73d487c
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/ic_media_route_connecting_dark_14_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/ic_media_route_connecting_dark_15_mtrl.png b/core/res/res/drawable-xxhdpi/ic_media_route_connecting_dark_15_mtrl.png
new file mode 100644
index 0000000..f427d0c
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/ic_media_route_connecting_dark_15_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/ic_media_route_connecting_dark_16_mtrl.png b/core/res/res/drawable-xxhdpi/ic_media_route_connecting_dark_16_mtrl.png
new file mode 100644
index 0000000..8fbaa08
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/ic_media_route_connecting_dark_16_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/ic_media_route_connecting_dark_17_mtrl.png b/core/res/res/drawable-xxhdpi/ic_media_route_connecting_dark_17_mtrl.png
new file mode 100644
index 0000000..1f0fcd6
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/ic_media_route_connecting_dark_17_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/ic_media_route_connecting_dark_18_mtrl.png b/core/res/res/drawable-xxhdpi/ic_media_route_connecting_dark_18_mtrl.png
new file mode 100644
index 0000000..80c905a
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/ic_media_route_connecting_dark_18_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/ic_media_route_connecting_dark_19_mtrl.png b/core/res/res/drawable-xxhdpi/ic_media_route_connecting_dark_19_mtrl.png
new file mode 100644
index 0000000..9b27fa3
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/ic_media_route_connecting_dark_19_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/ic_media_route_connecting_dark_20_mtrl.png b/core/res/res/drawable-xxhdpi/ic_media_route_connecting_dark_20_mtrl.png
new file mode 100644
index 0000000..24f2b75
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/ic_media_route_connecting_dark_20_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/ic_media_route_connecting_dark_21_mtrl.png b/core/res/res/drawable-xxhdpi/ic_media_route_connecting_dark_21_mtrl.png
new file mode 100644
index 0000000..f499d0a
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/ic_media_route_connecting_dark_21_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/ic_media_route_connecting_dark_22_mtrl.png b/core/res/res/drawable-xxhdpi/ic_media_route_connecting_dark_22_mtrl.png
new file mode 100644
index 0000000..e22ca9b
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/ic_media_route_connecting_dark_22_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/ic_media_route_connecting_dark_23_mtrl.png b/core/res/res/drawable-xxhdpi/ic_media_route_connecting_dark_23_mtrl.png
new file mode 100644
index 0000000..596ca8b
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/ic_media_route_connecting_dark_23_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/ic_media_route_connecting_dark_24_mtrl.png b/core/res/res/drawable-xxhdpi/ic_media_route_connecting_dark_24_mtrl.png
new file mode 100644
index 0000000..363a4d6
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/ic_media_route_connecting_dark_24_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/ic_media_route_connecting_dark_25_mtrl.png b/core/res/res/drawable-xxhdpi/ic_media_route_connecting_dark_25_mtrl.png
new file mode 100644
index 0000000..10e275b
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/ic_media_route_connecting_dark_25_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/ic_media_route_connecting_dark_26_mtrl.png b/core/res/res/drawable-xxhdpi/ic_media_route_connecting_dark_26_mtrl.png
new file mode 100644
index 0000000..aa87c9e
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/ic_media_route_connecting_dark_26_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/ic_media_route_connecting_dark_27_mtrl.png b/core/res/res/drawable-xxhdpi/ic_media_route_connecting_dark_27_mtrl.png
new file mode 100644
index 0000000..77a2b8f
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/ic_media_route_connecting_dark_27_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/ic_media_route_connecting_dark_28_mtrl.png b/core/res/res/drawable-xxhdpi/ic_media_route_connecting_dark_28_mtrl.png
new file mode 100644
index 0000000..f6108d9
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/ic_media_route_connecting_dark_28_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/ic_media_route_connecting_dark_29_mtrl.png b/core/res/res/drawable-xxhdpi/ic_media_route_connecting_dark_29_mtrl.png
new file mode 100644
index 0000000..e3a6a20
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/ic_media_route_connecting_dark_29_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/ic_media_route_connecting_dark_30_mtrl.png b/core/res/res/drawable-xxhdpi/ic_media_route_connecting_dark_30_mtrl.png
new file mode 100644
index 0000000..fa6dc9a
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/ic_media_route_connecting_dark_30_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/ic_media_route_connecting_light_00_mtrl.png b/core/res/res/drawable-xxhdpi/ic_media_route_connecting_light_00_mtrl.png
new file mode 100644
index 0000000..80d5155
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/ic_media_route_connecting_light_00_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/ic_media_route_connecting_light_01_mtrl.png b/core/res/res/drawable-xxhdpi/ic_media_route_connecting_light_01_mtrl.png
new file mode 100644
index 0000000..8ec05ae
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/ic_media_route_connecting_light_01_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/ic_media_route_connecting_light_02_mtrl.png b/core/res/res/drawable-xxhdpi/ic_media_route_connecting_light_02_mtrl.png
new file mode 100644
index 0000000..42cd13c
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/ic_media_route_connecting_light_02_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/ic_media_route_connecting_light_03_mtrl.png b/core/res/res/drawable-xxhdpi/ic_media_route_connecting_light_03_mtrl.png
new file mode 100644
index 0000000..f43727f
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/ic_media_route_connecting_light_03_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/ic_media_route_connecting_light_04_mtrl.png b/core/res/res/drawable-xxhdpi/ic_media_route_connecting_light_04_mtrl.png
new file mode 100644
index 0000000..849638b
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/ic_media_route_connecting_light_04_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/ic_media_route_connecting_light_05_mtrl.png b/core/res/res/drawable-xxhdpi/ic_media_route_connecting_light_05_mtrl.png
new file mode 100644
index 0000000..06e21b0
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/ic_media_route_connecting_light_05_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/ic_media_route_connecting_light_06_mtrl.png b/core/res/res/drawable-xxhdpi/ic_media_route_connecting_light_06_mtrl.png
new file mode 100644
index 0000000..ead968e
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/ic_media_route_connecting_light_06_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/ic_media_route_connecting_light_07_mtrl.png b/core/res/res/drawable-xxhdpi/ic_media_route_connecting_light_07_mtrl.png
new file mode 100644
index 0000000..52aa7d5
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/ic_media_route_connecting_light_07_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/ic_media_route_connecting_light_08_mtrl.png b/core/res/res/drawable-xxhdpi/ic_media_route_connecting_light_08_mtrl.png
new file mode 100644
index 0000000..58986d4
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/ic_media_route_connecting_light_08_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/ic_media_route_connecting_light_09_mtrl.png b/core/res/res/drawable-xxhdpi/ic_media_route_connecting_light_09_mtrl.png
new file mode 100644
index 0000000..42d6c7c
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/ic_media_route_connecting_light_09_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/ic_media_route_connecting_light_10_mtrl.png b/core/res/res/drawable-xxhdpi/ic_media_route_connecting_light_10_mtrl.png
new file mode 100644
index 0000000..e7e0e60
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/ic_media_route_connecting_light_10_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/ic_media_route_connecting_light_11_mtrl.png b/core/res/res/drawable-xxhdpi/ic_media_route_connecting_light_11_mtrl.png
new file mode 100644
index 0000000..933be30
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/ic_media_route_connecting_light_11_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/ic_media_route_connecting_light_12_mtrl.png b/core/res/res/drawable-xxhdpi/ic_media_route_connecting_light_12_mtrl.png
new file mode 100644
index 0000000..b39230f
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/ic_media_route_connecting_light_12_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/ic_media_route_connecting_light_13_mtrl.png b/core/res/res/drawable-xxhdpi/ic_media_route_connecting_light_13_mtrl.png
new file mode 100644
index 0000000..01492c4
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/ic_media_route_connecting_light_13_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/ic_media_route_connecting_light_14_mtrl.png b/core/res/res/drawable-xxhdpi/ic_media_route_connecting_light_14_mtrl.png
new file mode 100644
index 0000000..b56aa16
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/ic_media_route_connecting_light_14_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/ic_media_route_connecting_light_15_mtrl.png b/core/res/res/drawable-xxhdpi/ic_media_route_connecting_light_15_mtrl.png
new file mode 100644
index 0000000..bdd3d2c
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/ic_media_route_connecting_light_15_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/ic_media_route_connecting_light_16_mtrl.png b/core/res/res/drawable-xxhdpi/ic_media_route_connecting_light_16_mtrl.png
new file mode 100644
index 0000000..b76d5d7
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/ic_media_route_connecting_light_16_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/ic_media_route_connecting_light_17_mtrl.png b/core/res/res/drawable-xxhdpi/ic_media_route_connecting_light_17_mtrl.png
new file mode 100644
index 0000000..5fb12e7
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/ic_media_route_connecting_light_17_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/ic_media_route_connecting_light_18_mtrl.png b/core/res/res/drawable-xxhdpi/ic_media_route_connecting_light_18_mtrl.png
new file mode 100644
index 0000000..64fe3c5
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/ic_media_route_connecting_light_18_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/ic_media_route_connecting_light_19_mtrl.png b/core/res/res/drawable-xxhdpi/ic_media_route_connecting_light_19_mtrl.png
new file mode 100644
index 0000000..68fd051
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/ic_media_route_connecting_light_19_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/ic_media_route_connecting_light_20_mtrl.png b/core/res/res/drawable-xxhdpi/ic_media_route_connecting_light_20_mtrl.png
new file mode 100644
index 0000000..a4fd4cc
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/ic_media_route_connecting_light_20_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/ic_media_route_connecting_light_21_mtrl.png b/core/res/res/drawable-xxhdpi/ic_media_route_connecting_light_21_mtrl.png
new file mode 100644
index 0000000..40b02f0
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/ic_media_route_connecting_light_21_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/ic_media_route_connecting_light_22_mtrl.png b/core/res/res/drawable-xxhdpi/ic_media_route_connecting_light_22_mtrl.png
new file mode 100644
index 0000000..fcd6ddb
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/ic_media_route_connecting_light_22_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/ic_media_route_connecting_light_23_mtrl.png b/core/res/res/drawable-xxhdpi/ic_media_route_connecting_light_23_mtrl.png
new file mode 100644
index 0000000..cb250dd
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/ic_media_route_connecting_light_23_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/ic_media_route_connecting_light_24_mtrl.png b/core/res/res/drawable-xxhdpi/ic_media_route_connecting_light_24_mtrl.png
new file mode 100644
index 0000000..43d2119
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/ic_media_route_connecting_light_24_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/ic_media_route_connecting_light_25_mtrl.png b/core/res/res/drawable-xxhdpi/ic_media_route_connecting_light_25_mtrl.png
new file mode 100644
index 0000000..bdcaf69
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/ic_media_route_connecting_light_25_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/ic_media_route_connecting_light_26_mtrl.png b/core/res/res/drawable-xxhdpi/ic_media_route_connecting_light_26_mtrl.png
new file mode 100644
index 0000000..3d2bff4
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/ic_media_route_connecting_light_26_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/ic_media_route_connecting_light_27_mtrl.png b/core/res/res/drawable-xxhdpi/ic_media_route_connecting_light_27_mtrl.png
new file mode 100644
index 0000000..290e7d9
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/ic_media_route_connecting_light_27_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/ic_media_route_connecting_light_28_mtrl.png b/core/res/res/drawable-xxhdpi/ic_media_route_connecting_light_28_mtrl.png
new file mode 100644
index 0000000..6c670fb
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/ic_media_route_connecting_light_28_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/ic_media_route_connecting_light_29_mtrl.png b/core/res/res/drawable-xxhdpi/ic_media_route_connecting_light_29_mtrl.png
new file mode 100644
index 0000000..e93c824
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/ic_media_route_connecting_light_29_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/ic_media_route_connecting_light_30_mtrl.png b/core/res/res/drawable-xxhdpi/ic_media_route_connecting_light_30_mtrl.png
new file mode 100644
index 0000000..80d5155
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/ic_media_route_connecting_light_30_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/ic_media_route_off_dark_mtrl.png b/core/res/res/drawable-xxhdpi/ic_media_route_off_dark_mtrl.png
new file mode 100644
index 0000000..9acbd29
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/ic_media_route_off_dark_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/ic_media_route_off_light_mtrl.png b/core/res/res/drawable-xxhdpi/ic_media_route_off_light_mtrl.png
new file mode 100644
index 0000000..5d4273d
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/ic_media_route_off_light_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/ic_media_route_off_mtrl_alpha.png b/core/res/res/drawable-xxhdpi/ic_media_route_off_mtrl_alpha.png
deleted file mode 100644
index 44d98d5..0000000
--- a/core/res/res/drawable-xxhdpi/ic_media_route_off_mtrl_alpha.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/ic_media_route_on_0_mtrl_alpha.png b/core/res/res/drawable-xxhdpi/ic_media_route_on_0_mtrl_alpha.png
deleted file mode 100644
index c807b50..0000000
--- a/core/res/res/drawable-xxhdpi/ic_media_route_on_0_mtrl_alpha.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/ic_media_route_on_1_mtrl_alpha.png b/core/res/res/drawable-xxhdpi/ic_media_route_on_1_mtrl_alpha.png
deleted file mode 100644
index d54f44a..0000000
--- a/core/res/res/drawable-xxhdpi/ic_media_route_on_1_mtrl_alpha.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/ic_media_route_on_2_mtrl_alpha.png b/core/res/res/drawable-xxhdpi/ic_media_route_on_2_mtrl_alpha.png
deleted file mode 100644
index 17c1d99..0000000
--- a/core/res/res/drawable-xxhdpi/ic_media_route_on_2_mtrl_alpha.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/ic_media_route_on_mtrl_alpha.png b/core/res/res/drawable-xxhdpi/ic_media_route_on_mtrl_alpha.png
deleted file mode 100644
index 906401e..0000000
--- a/core/res/res/drawable-xxhdpi/ic_media_route_on_mtrl_alpha.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/ic_media_route_connected_dark_00_mtrl.png b/core/res/res/drawable-xxxhdpi/ic_media_route_connected_dark_00_mtrl.png
new file mode 100644
index 0000000..5561c62
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/ic_media_route_connected_dark_00_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/ic_media_route_connected_dark_01_mtrl.png b/core/res/res/drawable-xxxhdpi/ic_media_route_connected_dark_01_mtrl.png
new file mode 100644
index 0000000..9eff17e
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/ic_media_route_connected_dark_01_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/ic_media_route_connected_dark_02_mtrl.png b/core/res/res/drawable-xxxhdpi/ic_media_route_connected_dark_02_mtrl.png
new file mode 100644
index 0000000..67923e2
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/ic_media_route_connected_dark_02_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/ic_media_route_connected_dark_03_mtrl.png b/core/res/res/drawable-xxxhdpi/ic_media_route_connected_dark_03_mtrl.png
new file mode 100644
index 0000000..1aa0e98
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/ic_media_route_connected_dark_03_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/ic_media_route_connected_dark_04_mtrl.png b/core/res/res/drawable-xxxhdpi/ic_media_route_connected_dark_04_mtrl.png
new file mode 100644
index 0000000..7cd549d
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/ic_media_route_connected_dark_04_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/ic_media_route_connected_dark_05_mtrl.png b/core/res/res/drawable-xxxhdpi/ic_media_route_connected_dark_05_mtrl.png
new file mode 100644
index 0000000..2c14d79
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/ic_media_route_connected_dark_05_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/ic_media_route_connected_dark_06_mtrl.png b/core/res/res/drawable-xxxhdpi/ic_media_route_connected_dark_06_mtrl.png
new file mode 100644
index 0000000..9f061a5
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/ic_media_route_connected_dark_06_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/ic_media_route_connected_dark_07_mtrl.png b/core/res/res/drawable-xxxhdpi/ic_media_route_connected_dark_07_mtrl.png
new file mode 100644
index 0000000..fe15230
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/ic_media_route_connected_dark_07_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/ic_media_route_connected_dark_08_mtrl.png b/core/res/res/drawable-xxxhdpi/ic_media_route_connected_dark_08_mtrl.png
new file mode 100644
index 0000000..220a4fe
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/ic_media_route_connected_dark_08_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/ic_media_route_connected_dark_09_mtrl.png b/core/res/res/drawable-xxxhdpi/ic_media_route_connected_dark_09_mtrl.png
new file mode 100644
index 0000000..77aac4c
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/ic_media_route_connected_dark_09_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/ic_media_route_connected_dark_10_mtrl.png b/core/res/res/drawable-xxxhdpi/ic_media_route_connected_dark_10_mtrl.png
new file mode 100644
index 0000000..bf69188
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/ic_media_route_connected_dark_10_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/ic_media_route_connected_dark_11_mtrl.png b/core/res/res/drawable-xxxhdpi/ic_media_route_connected_dark_11_mtrl.png
new file mode 100644
index 0000000..22197da
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/ic_media_route_connected_dark_11_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/ic_media_route_connected_dark_12_mtrl.png b/core/res/res/drawable-xxxhdpi/ic_media_route_connected_dark_12_mtrl.png
new file mode 100644
index 0000000..380e929
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/ic_media_route_connected_dark_12_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/ic_media_route_connected_dark_13_mtrl.png b/core/res/res/drawable-xxxhdpi/ic_media_route_connected_dark_13_mtrl.png
new file mode 100644
index 0000000..d93a600
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/ic_media_route_connected_dark_13_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/ic_media_route_connected_dark_14_mtrl.png b/core/res/res/drawable-xxxhdpi/ic_media_route_connected_dark_14_mtrl.png
new file mode 100644
index 0000000..3709d5f
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/ic_media_route_connected_dark_14_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/ic_media_route_connected_dark_15_mtrl.png b/core/res/res/drawable-xxxhdpi/ic_media_route_connected_dark_15_mtrl.png
new file mode 100644
index 0000000..2a5019b
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/ic_media_route_connected_dark_15_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/ic_media_route_connected_dark_16_mtrl.png b/core/res/res/drawable-xxxhdpi/ic_media_route_connected_dark_16_mtrl.png
new file mode 100644
index 0000000..f75a072
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/ic_media_route_connected_dark_16_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/ic_media_route_connected_dark_17_mtrl.png b/core/res/res/drawable-xxxhdpi/ic_media_route_connected_dark_17_mtrl.png
new file mode 100644
index 0000000..c8dcd0d
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/ic_media_route_connected_dark_17_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/ic_media_route_connected_dark_18_mtrl.png b/core/res/res/drawable-xxxhdpi/ic_media_route_connected_dark_18_mtrl.png
new file mode 100644
index 0000000..172775e
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/ic_media_route_connected_dark_18_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/ic_media_route_connected_dark_19_mtrl.png b/core/res/res/drawable-xxxhdpi/ic_media_route_connected_dark_19_mtrl.png
new file mode 100644
index 0000000..2f081fd
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/ic_media_route_connected_dark_19_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/ic_media_route_connected_dark_20_mtrl.png b/core/res/res/drawable-xxxhdpi/ic_media_route_connected_dark_20_mtrl.png
new file mode 100644
index 0000000..cb4c55b
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/ic_media_route_connected_dark_20_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/ic_media_route_connected_dark_21_mtrl.png b/core/res/res/drawable-xxxhdpi/ic_media_route_connected_dark_21_mtrl.png
new file mode 100644
index 0000000..de155f6
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/ic_media_route_connected_dark_21_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/ic_media_route_connected_dark_22_mtrl.png b/core/res/res/drawable-xxxhdpi/ic_media_route_connected_dark_22_mtrl.png
new file mode 100644
index 0000000..2cc028d
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/ic_media_route_connected_dark_22_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/ic_media_route_connected_dark_23_mtrl.png b/core/res/res/drawable-xxxhdpi/ic_media_route_connected_dark_23_mtrl.png
new file mode 100644
index 0000000..e848d08
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/ic_media_route_connected_dark_23_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/ic_media_route_connected_dark_24_mtrl.png b/core/res/res/drawable-xxxhdpi/ic_media_route_connected_dark_24_mtrl.png
new file mode 100644
index 0000000..584c2f8
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/ic_media_route_connected_dark_24_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/ic_media_route_connected_dark_25_mtrl.png b/core/res/res/drawable-xxxhdpi/ic_media_route_connected_dark_25_mtrl.png
new file mode 100644
index 0000000..c9299be
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/ic_media_route_connected_dark_25_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/ic_media_route_connected_dark_26_mtrl.png b/core/res/res/drawable-xxxhdpi/ic_media_route_connected_dark_26_mtrl.png
new file mode 100644
index 0000000..8abcbd6
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/ic_media_route_connected_dark_26_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/ic_media_route_connected_dark_27_mtrl.png b/core/res/res/drawable-xxxhdpi/ic_media_route_connected_dark_27_mtrl.png
new file mode 100644
index 0000000..ee53e47
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/ic_media_route_connected_dark_27_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/ic_media_route_connected_dark_28_mtrl.png b/core/res/res/drawable-xxxhdpi/ic_media_route_connected_dark_28_mtrl.png
new file mode 100644
index 0000000..b10d23a
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/ic_media_route_connected_dark_28_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/ic_media_route_connected_dark_29_mtrl.png b/core/res/res/drawable-xxxhdpi/ic_media_route_connected_dark_29_mtrl.png
new file mode 100644
index 0000000..65a2363
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/ic_media_route_connected_dark_29_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/ic_media_route_connected_dark_30_mtrl.png b/core/res/res/drawable-xxxhdpi/ic_media_route_connected_dark_30_mtrl.png
new file mode 100644
index 0000000..2b73472
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/ic_media_route_connected_dark_30_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/ic_media_route_connected_light_00_mtrl.png b/core/res/res/drawable-xxxhdpi/ic_media_route_connected_light_00_mtrl.png
new file mode 100644
index 0000000..6a656b8
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/ic_media_route_connected_light_00_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/ic_media_route_connected_light_01_mtrl.png b/core/res/res/drawable-xxxhdpi/ic_media_route_connected_light_01_mtrl.png
new file mode 100644
index 0000000..7d3a3b6
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/ic_media_route_connected_light_01_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/ic_media_route_connected_light_02_mtrl.png b/core/res/res/drawable-xxxhdpi/ic_media_route_connected_light_02_mtrl.png
new file mode 100644
index 0000000..6b22554
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/ic_media_route_connected_light_02_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/ic_media_route_connected_light_03_mtrl.png b/core/res/res/drawable-xxxhdpi/ic_media_route_connected_light_03_mtrl.png
new file mode 100644
index 0000000..0a9245c
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/ic_media_route_connected_light_03_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/ic_media_route_connected_light_04_mtrl.png b/core/res/res/drawable-xxxhdpi/ic_media_route_connected_light_04_mtrl.png
new file mode 100644
index 0000000..f9a7f6c
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/ic_media_route_connected_light_04_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/ic_media_route_connected_light_05_mtrl.png b/core/res/res/drawable-xxxhdpi/ic_media_route_connected_light_05_mtrl.png
new file mode 100644
index 0000000..ac396ed
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/ic_media_route_connected_light_05_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/ic_media_route_connected_light_06_mtrl.png b/core/res/res/drawable-xxxhdpi/ic_media_route_connected_light_06_mtrl.png
new file mode 100644
index 0000000..8c15241d
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/ic_media_route_connected_light_06_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/ic_media_route_connected_light_07_mtrl.png b/core/res/res/drawable-xxxhdpi/ic_media_route_connected_light_07_mtrl.png
new file mode 100644
index 0000000..e6a75e2
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/ic_media_route_connected_light_07_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/ic_media_route_connected_light_08_mtrl.png b/core/res/res/drawable-xxxhdpi/ic_media_route_connected_light_08_mtrl.png
new file mode 100644
index 0000000..90280a9
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/ic_media_route_connected_light_08_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/ic_media_route_connected_light_09_mtrl.png b/core/res/res/drawable-xxxhdpi/ic_media_route_connected_light_09_mtrl.png
new file mode 100644
index 0000000..d9a4632
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/ic_media_route_connected_light_09_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/ic_media_route_connected_light_10_mtrl.png b/core/res/res/drawable-xxxhdpi/ic_media_route_connected_light_10_mtrl.png
new file mode 100644
index 0000000..b1ae68b
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/ic_media_route_connected_light_10_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/ic_media_route_connected_light_11_mtrl.png b/core/res/res/drawable-xxxhdpi/ic_media_route_connected_light_11_mtrl.png
new file mode 100644
index 0000000..e5cba8f
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/ic_media_route_connected_light_11_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/ic_media_route_connected_light_12_mtrl.png b/core/res/res/drawable-xxxhdpi/ic_media_route_connected_light_12_mtrl.png
new file mode 100644
index 0000000..611faeb
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/ic_media_route_connected_light_12_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/ic_media_route_connected_light_13_mtrl.png b/core/res/res/drawable-xxxhdpi/ic_media_route_connected_light_13_mtrl.png
new file mode 100644
index 0000000..dfada4d
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/ic_media_route_connected_light_13_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/ic_media_route_connected_light_14_mtrl.png b/core/res/res/drawable-xxxhdpi/ic_media_route_connected_light_14_mtrl.png
new file mode 100644
index 0000000..6118202
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/ic_media_route_connected_light_14_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/ic_media_route_connected_light_15_mtrl.png b/core/res/res/drawable-xxxhdpi/ic_media_route_connected_light_15_mtrl.png
new file mode 100644
index 0000000..60d5199
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/ic_media_route_connected_light_15_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/ic_media_route_connected_light_16_mtrl.png b/core/res/res/drawable-xxxhdpi/ic_media_route_connected_light_16_mtrl.png
new file mode 100644
index 0000000..ee0b672
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/ic_media_route_connected_light_16_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/ic_media_route_connected_light_17_mtrl.png b/core/res/res/drawable-xxxhdpi/ic_media_route_connected_light_17_mtrl.png
new file mode 100644
index 0000000..5a97f67
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/ic_media_route_connected_light_17_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/ic_media_route_connected_light_18_mtrl.png b/core/res/res/drawable-xxxhdpi/ic_media_route_connected_light_18_mtrl.png
new file mode 100644
index 0000000..9237a7d
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/ic_media_route_connected_light_18_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/ic_media_route_connected_light_19_mtrl.png b/core/res/res/drawable-xxxhdpi/ic_media_route_connected_light_19_mtrl.png
new file mode 100644
index 0000000..4d4c663
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/ic_media_route_connected_light_19_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/ic_media_route_connected_light_20_mtrl.png b/core/res/res/drawable-xxxhdpi/ic_media_route_connected_light_20_mtrl.png
new file mode 100644
index 0000000..141f9f6
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/ic_media_route_connected_light_20_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/ic_media_route_connected_light_21_mtrl.png b/core/res/res/drawable-xxxhdpi/ic_media_route_connected_light_21_mtrl.png
new file mode 100644
index 0000000..2baa531
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/ic_media_route_connected_light_21_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/ic_media_route_connected_light_22_mtrl.png b/core/res/res/drawable-xxxhdpi/ic_media_route_connected_light_22_mtrl.png
new file mode 100644
index 0000000..7a911d5
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/ic_media_route_connected_light_22_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/ic_media_route_connected_light_23_mtrl.png b/core/res/res/drawable-xxxhdpi/ic_media_route_connected_light_23_mtrl.png
new file mode 100644
index 0000000..a760b85
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/ic_media_route_connected_light_23_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/ic_media_route_connected_light_24_mtrl.png b/core/res/res/drawable-xxxhdpi/ic_media_route_connected_light_24_mtrl.png
new file mode 100644
index 0000000..5c15a87
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/ic_media_route_connected_light_24_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/ic_media_route_connected_light_25_mtrl.png b/core/res/res/drawable-xxxhdpi/ic_media_route_connected_light_25_mtrl.png
new file mode 100644
index 0000000..36a9f1f
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/ic_media_route_connected_light_25_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/ic_media_route_connected_light_26_mtrl.png b/core/res/res/drawable-xxxhdpi/ic_media_route_connected_light_26_mtrl.png
new file mode 100644
index 0000000..03e8c9f
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/ic_media_route_connected_light_26_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/ic_media_route_connected_light_27_mtrl.png b/core/res/res/drawable-xxxhdpi/ic_media_route_connected_light_27_mtrl.png
new file mode 100644
index 0000000..533c694
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/ic_media_route_connected_light_27_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/ic_media_route_connected_light_28_mtrl.png b/core/res/res/drawable-xxxhdpi/ic_media_route_connected_light_28_mtrl.png
new file mode 100644
index 0000000..ead60f5
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/ic_media_route_connected_light_28_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/ic_media_route_connected_light_29_mtrl.png b/core/res/res/drawable-xxxhdpi/ic_media_route_connected_light_29_mtrl.png
new file mode 100644
index 0000000..0a8534f
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/ic_media_route_connected_light_29_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/ic_media_route_connected_light_30_mtrl.png b/core/res/res/drawable-xxxhdpi/ic_media_route_connected_light_30_mtrl.png
new file mode 100644
index 0000000..fa69ad0
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/ic_media_route_connected_light_30_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/ic_media_route_connecting_dark_00_mtrl.png b/core/res/res/drawable-xxxhdpi/ic_media_route_connecting_dark_00_mtrl.png
new file mode 100644
index 0000000..5561c62
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/ic_media_route_connecting_dark_00_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/ic_media_route_connecting_dark_01_mtrl.png b/core/res/res/drawable-xxxhdpi/ic_media_route_connecting_dark_01_mtrl.png
new file mode 100644
index 0000000..9eff17e
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/ic_media_route_connecting_dark_01_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/ic_media_route_connecting_dark_02_mtrl.png b/core/res/res/drawable-xxxhdpi/ic_media_route_connecting_dark_02_mtrl.png
new file mode 100644
index 0000000..67923e2
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/ic_media_route_connecting_dark_02_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/ic_media_route_connecting_dark_03_mtrl.png b/core/res/res/drawable-xxxhdpi/ic_media_route_connecting_dark_03_mtrl.png
new file mode 100644
index 0000000..1aa0e98
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/ic_media_route_connecting_dark_03_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/ic_media_route_connecting_dark_04_mtrl.png b/core/res/res/drawable-xxxhdpi/ic_media_route_connecting_dark_04_mtrl.png
new file mode 100644
index 0000000..7cd549d
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/ic_media_route_connecting_dark_04_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/ic_media_route_connecting_dark_05_mtrl.png b/core/res/res/drawable-xxxhdpi/ic_media_route_connecting_dark_05_mtrl.png
new file mode 100644
index 0000000..2c14d79
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/ic_media_route_connecting_dark_05_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/ic_media_route_connecting_dark_06_mtrl.png b/core/res/res/drawable-xxxhdpi/ic_media_route_connecting_dark_06_mtrl.png
new file mode 100644
index 0000000..9f061a5
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/ic_media_route_connecting_dark_06_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/ic_media_route_connecting_dark_07_mtrl.png b/core/res/res/drawable-xxxhdpi/ic_media_route_connecting_dark_07_mtrl.png
new file mode 100644
index 0000000..fe15230
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/ic_media_route_connecting_dark_07_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/ic_media_route_connecting_dark_08_mtrl.png b/core/res/res/drawable-xxxhdpi/ic_media_route_connecting_dark_08_mtrl.png
new file mode 100644
index 0000000..220a4fe
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/ic_media_route_connecting_dark_08_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/ic_media_route_connecting_dark_09_mtrl.png b/core/res/res/drawable-xxxhdpi/ic_media_route_connecting_dark_09_mtrl.png
new file mode 100644
index 0000000..77aac4c
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/ic_media_route_connecting_dark_09_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/ic_media_route_connecting_dark_10_mtrl.png b/core/res/res/drawable-xxxhdpi/ic_media_route_connecting_dark_10_mtrl.png
new file mode 100644
index 0000000..bf69188
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/ic_media_route_connecting_dark_10_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/ic_media_route_connecting_dark_11_mtrl.png b/core/res/res/drawable-xxxhdpi/ic_media_route_connecting_dark_11_mtrl.png
new file mode 100644
index 0000000..98b0448
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/ic_media_route_connecting_dark_11_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/ic_media_route_connecting_dark_12_mtrl.png b/core/res/res/drawable-xxxhdpi/ic_media_route_connecting_dark_12_mtrl.png
new file mode 100644
index 0000000..a926419
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/ic_media_route_connecting_dark_12_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/ic_media_route_connecting_dark_13_mtrl.png b/core/res/res/drawable-xxxhdpi/ic_media_route_connecting_dark_13_mtrl.png
new file mode 100644
index 0000000..53f6e3e
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/ic_media_route_connecting_dark_13_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/ic_media_route_connecting_dark_14_mtrl.png b/core/res/res/drawable-xxxhdpi/ic_media_route_connecting_dark_14_mtrl.png
new file mode 100644
index 0000000..4a2d1b1
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/ic_media_route_connecting_dark_14_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/ic_media_route_connecting_dark_15_mtrl.png b/core/res/res/drawable-xxxhdpi/ic_media_route_connecting_dark_15_mtrl.png
new file mode 100644
index 0000000..22cce05
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/ic_media_route_connecting_dark_15_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/ic_media_route_connecting_dark_16_mtrl.png b/core/res/res/drawable-xxxhdpi/ic_media_route_connecting_dark_16_mtrl.png
new file mode 100644
index 0000000..d40db21
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/ic_media_route_connecting_dark_16_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/ic_media_route_connecting_dark_17_mtrl.png b/core/res/res/drawable-xxxhdpi/ic_media_route_connecting_dark_17_mtrl.png
new file mode 100644
index 0000000..0a95b7e
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/ic_media_route_connecting_dark_17_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/ic_media_route_connecting_dark_18_mtrl.png b/core/res/res/drawable-xxxhdpi/ic_media_route_connecting_dark_18_mtrl.png
new file mode 100644
index 0000000..08698bd
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/ic_media_route_connecting_dark_18_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/ic_media_route_connecting_dark_19_mtrl.png b/core/res/res/drawable-xxxhdpi/ic_media_route_connecting_dark_19_mtrl.png
new file mode 100644
index 0000000..698d23a
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/ic_media_route_connecting_dark_19_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/ic_media_route_connecting_dark_20_mtrl.png b/core/res/res/drawable-xxxhdpi/ic_media_route_connecting_dark_20_mtrl.png
new file mode 100644
index 0000000..36ed854
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/ic_media_route_connecting_dark_20_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/ic_media_route_connecting_dark_21_mtrl.png b/core/res/res/drawable-xxxhdpi/ic_media_route_connecting_dark_21_mtrl.png
new file mode 100644
index 0000000..2c0d925
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/ic_media_route_connecting_dark_21_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/ic_media_route_connecting_dark_22_mtrl.png b/core/res/res/drawable-xxxhdpi/ic_media_route_connecting_dark_22_mtrl.png
new file mode 100644
index 0000000..747b901
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/ic_media_route_connecting_dark_22_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/ic_media_route_connecting_dark_23_mtrl.png b/core/res/res/drawable-xxxhdpi/ic_media_route_connecting_dark_23_mtrl.png
new file mode 100644
index 0000000..bae8665
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/ic_media_route_connecting_dark_23_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/ic_media_route_connecting_dark_24_mtrl.png b/core/res/res/drawable-xxxhdpi/ic_media_route_connecting_dark_24_mtrl.png
new file mode 100644
index 0000000..fd7b1a8
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/ic_media_route_connecting_dark_24_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/ic_media_route_connecting_dark_25_mtrl.png b/core/res/res/drawable-xxxhdpi/ic_media_route_connecting_dark_25_mtrl.png
new file mode 100644
index 0000000..03f624e
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/ic_media_route_connecting_dark_25_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/ic_media_route_connecting_dark_26_mtrl.png b/core/res/res/drawable-xxxhdpi/ic_media_route_connecting_dark_26_mtrl.png
new file mode 100644
index 0000000..d2a06ed
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/ic_media_route_connecting_dark_26_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/ic_media_route_connecting_dark_27_mtrl.png b/core/res/res/drawable-xxxhdpi/ic_media_route_connecting_dark_27_mtrl.png
new file mode 100644
index 0000000..c5b2a16
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/ic_media_route_connecting_dark_27_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/ic_media_route_connecting_dark_28_mtrl.png b/core/res/res/drawable-xxxhdpi/ic_media_route_connecting_dark_28_mtrl.png
new file mode 100644
index 0000000..8b045cb
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/ic_media_route_connecting_dark_28_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/ic_media_route_connecting_dark_29_mtrl.png b/core/res/res/drawable-xxxhdpi/ic_media_route_connecting_dark_29_mtrl.png
new file mode 100644
index 0000000..522ec67
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/ic_media_route_connecting_dark_29_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/ic_media_route_connecting_dark_30_mtrl.png b/core/res/res/drawable-xxxhdpi/ic_media_route_connecting_dark_30_mtrl.png
new file mode 100644
index 0000000..5561c62
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/ic_media_route_connecting_dark_30_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/ic_media_route_connecting_light_00_mtrl.png b/core/res/res/drawable-xxxhdpi/ic_media_route_connecting_light_00_mtrl.png
new file mode 100644
index 0000000..6a656b8
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/ic_media_route_connecting_light_00_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/ic_media_route_connecting_light_01_mtrl.png b/core/res/res/drawable-xxxhdpi/ic_media_route_connecting_light_01_mtrl.png
new file mode 100644
index 0000000..7d3a3b6
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/ic_media_route_connecting_light_01_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/ic_media_route_connecting_light_02_mtrl.png b/core/res/res/drawable-xxxhdpi/ic_media_route_connecting_light_02_mtrl.png
new file mode 100644
index 0000000..6b22554
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/ic_media_route_connecting_light_02_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/ic_media_route_connecting_light_03_mtrl.png b/core/res/res/drawable-xxxhdpi/ic_media_route_connecting_light_03_mtrl.png
new file mode 100644
index 0000000..0a9245c
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/ic_media_route_connecting_light_03_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/ic_media_route_connecting_light_04_mtrl.png b/core/res/res/drawable-xxxhdpi/ic_media_route_connecting_light_04_mtrl.png
new file mode 100644
index 0000000..f9a7f6c
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/ic_media_route_connecting_light_04_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/ic_media_route_connecting_light_05_mtrl.png b/core/res/res/drawable-xxxhdpi/ic_media_route_connecting_light_05_mtrl.png
new file mode 100644
index 0000000..ac396ed
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/ic_media_route_connecting_light_05_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/ic_media_route_connecting_light_06_mtrl.png b/core/res/res/drawable-xxxhdpi/ic_media_route_connecting_light_06_mtrl.png
new file mode 100644
index 0000000..8c15241d
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/ic_media_route_connecting_light_06_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/ic_media_route_connecting_light_07_mtrl.png b/core/res/res/drawable-xxxhdpi/ic_media_route_connecting_light_07_mtrl.png
new file mode 100644
index 0000000..e6a75e2
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/ic_media_route_connecting_light_07_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/ic_media_route_connecting_light_08_mtrl.png b/core/res/res/drawable-xxxhdpi/ic_media_route_connecting_light_08_mtrl.png
new file mode 100644
index 0000000..90280a9
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/ic_media_route_connecting_light_08_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/ic_media_route_connecting_light_09_mtrl.png b/core/res/res/drawable-xxxhdpi/ic_media_route_connecting_light_09_mtrl.png
new file mode 100644
index 0000000..d9a4632
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/ic_media_route_connecting_light_09_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/ic_media_route_connecting_light_10_mtrl.png b/core/res/res/drawable-xxxhdpi/ic_media_route_connecting_light_10_mtrl.png
new file mode 100644
index 0000000..b1ae68b
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/ic_media_route_connecting_light_10_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/ic_media_route_connecting_light_11_mtrl.png b/core/res/res/drawable-xxxhdpi/ic_media_route_connecting_light_11_mtrl.png
new file mode 100644
index 0000000..e3ce3fd
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/ic_media_route_connecting_light_11_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/ic_media_route_connecting_light_12_mtrl.png b/core/res/res/drawable-xxxhdpi/ic_media_route_connecting_light_12_mtrl.png
new file mode 100644
index 0000000..42482a4
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/ic_media_route_connecting_light_12_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/ic_media_route_connecting_light_13_mtrl.png b/core/res/res/drawable-xxxhdpi/ic_media_route_connecting_light_13_mtrl.png
new file mode 100644
index 0000000..ff30f98
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/ic_media_route_connecting_light_13_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/ic_media_route_connecting_light_14_mtrl.png b/core/res/res/drawable-xxxhdpi/ic_media_route_connecting_light_14_mtrl.png
new file mode 100644
index 0000000..0ddcb55
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/ic_media_route_connecting_light_14_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/ic_media_route_connecting_light_15_mtrl.png b/core/res/res/drawable-xxxhdpi/ic_media_route_connecting_light_15_mtrl.png
new file mode 100644
index 0000000..46ec435
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/ic_media_route_connecting_light_15_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/ic_media_route_connecting_light_16_mtrl.png b/core/res/res/drawable-xxxhdpi/ic_media_route_connecting_light_16_mtrl.png
new file mode 100644
index 0000000..d95fd77
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/ic_media_route_connecting_light_16_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/ic_media_route_connecting_light_17_mtrl.png b/core/res/res/drawable-xxxhdpi/ic_media_route_connecting_light_17_mtrl.png
new file mode 100644
index 0000000..f116a1f
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/ic_media_route_connecting_light_17_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/ic_media_route_connecting_light_18_mtrl.png b/core/res/res/drawable-xxxhdpi/ic_media_route_connecting_light_18_mtrl.png
new file mode 100644
index 0000000..957db7f
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/ic_media_route_connecting_light_18_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/ic_media_route_connecting_light_19_mtrl.png b/core/res/res/drawable-xxxhdpi/ic_media_route_connecting_light_19_mtrl.png
new file mode 100644
index 0000000..624ec47
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/ic_media_route_connecting_light_19_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/ic_media_route_connecting_light_20_mtrl.png b/core/res/res/drawable-xxxhdpi/ic_media_route_connecting_light_20_mtrl.png
new file mode 100644
index 0000000..362fba4
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/ic_media_route_connecting_light_20_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/ic_media_route_connecting_light_21_mtrl.png b/core/res/res/drawable-xxxhdpi/ic_media_route_connecting_light_21_mtrl.png
new file mode 100644
index 0000000..24032b9
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/ic_media_route_connecting_light_21_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/ic_media_route_connecting_light_22_mtrl.png b/core/res/res/drawable-xxxhdpi/ic_media_route_connecting_light_22_mtrl.png
new file mode 100644
index 0000000..6e4c83c
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/ic_media_route_connecting_light_22_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/ic_media_route_connecting_light_23_mtrl.png b/core/res/res/drawable-xxxhdpi/ic_media_route_connecting_light_23_mtrl.png
new file mode 100644
index 0000000..1d2a09e
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/ic_media_route_connecting_light_23_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/ic_media_route_connecting_light_24_mtrl.png b/core/res/res/drawable-xxxhdpi/ic_media_route_connecting_light_24_mtrl.png
new file mode 100644
index 0000000..7b05dfa
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/ic_media_route_connecting_light_24_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/ic_media_route_connecting_light_25_mtrl.png b/core/res/res/drawable-xxxhdpi/ic_media_route_connecting_light_25_mtrl.png
new file mode 100644
index 0000000..30de74a1
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/ic_media_route_connecting_light_25_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/ic_media_route_connecting_light_26_mtrl.png b/core/res/res/drawable-xxxhdpi/ic_media_route_connecting_light_26_mtrl.png
new file mode 100644
index 0000000..b08b782
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/ic_media_route_connecting_light_26_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/ic_media_route_connecting_light_27_mtrl.png b/core/res/res/drawable-xxxhdpi/ic_media_route_connecting_light_27_mtrl.png
new file mode 100644
index 0000000..e52a1aa
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/ic_media_route_connecting_light_27_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/ic_media_route_connecting_light_28_mtrl.png b/core/res/res/drawable-xxxhdpi/ic_media_route_connecting_light_28_mtrl.png
new file mode 100644
index 0000000..2c311f5
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/ic_media_route_connecting_light_28_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/ic_media_route_connecting_light_29_mtrl.png b/core/res/res/drawable-xxxhdpi/ic_media_route_connecting_light_29_mtrl.png
new file mode 100644
index 0000000..efdf5fc
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/ic_media_route_connecting_light_29_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/ic_media_route_connecting_light_30_mtrl.png b/core/res/res/drawable-xxxhdpi/ic_media_route_connecting_light_30_mtrl.png
new file mode 100644
index 0000000..6a656b8
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/ic_media_route_connecting_light_30_mtrl.png
Binary files differ
diff --git a/core/res/res/drawable/ic_media_route_connected_dark_material.xml b/core/res/res/drawable/ic_media_route_connected_dark_material.xml
new file mode 100644
index 0000000..efde152
--- /dev/null
+++ b/core/res/res/drawable/ic_media_route_connected_dark_material.xml
@@ -0,0 +1,81 @@
+<?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.
+-->
+
+<animation-list xmlns:android="http://schemas.android.com/apk/res/android" android:oneshot="true">
+    <item android:drawable="@drawable/ic_media_route_connected_dark_00_mtrl"
+          android:duration="42" />
+    <item android:drawable="@drawable/ic_media_route_connected_dark_01_mtrl"
+          android:duration="42" />
+    <item android:drawable="@drawable/ic_media_route_connected_dark_02_mtrl"
+          android:duration="42" />
+    <item android:drawable="@drawable/ic_media_route_connected_dark_03_mtrl"
+          android:duration="42" />
+    <item android:drawable="@drawable/ic_media_route_connected_dark_04_mtrl"
+          android:duration="42" />
+    <item android:drawable="@drawable/ic_media_route_connected_dark_05_mtrl"
+          android:duration="42" />
+    <item android:drawable="@drawable/ic_media_route_connected_dark_06_mtrl"
+          android:duration="42" />
+    <item android:drawable="@drawable/ic_media_route_connected_dark_07_mtrl"
+          android:duration="42" />
+    <item android:drawable="@drawable/ic_media_route_connected_dark_08_mtrl"
+          android:duration="42" />
+    <item android:drawable="@drawable/ic_media_route_connected_dark_09_mtrl"
+          android:duration="42" />
+    <item android:drawable="@drawable/ic_media_route_connected_dark_10_mtrl"
+          android:duration="42" />
+    <item android:drawable="@drawable/ic_media_route_connected_dark_11_mtrl"
+          android:duration="42" />
+    <item android:drawable="@drawable/ic_media_route_connected_dark_12_mtrl"
+          android:duration="42" />
+    <item android:drawable="@drawable/ic_media_route_connected_dark_13_mtrl"
+          android:duration="42" />
+    <item android:drawable="@drawable/ic_media_route_connected_dark_14_mtrl"
+          android:duration="42" />
+    <item android:drawable="@drawable/ic_media_route_connected_dark_15_mtrl"
+          android:duration="42" />
+    <item android:drawable="@drawable/ic_media_route_connected_dark_16_mtrl"
+          android:duration="42" />
+    <item android:drawable="@drawable/ic_media_route_connected_dark_17_mtrl"
+          android:duration="42" />
+    <item android:drawable="@drawable/ic_media_route_connected_dark_18_mtrl"
+          android:duration="42" />
+    <item android:drawable="@drawable/ic_media_route_connected_dark_19_mtrl"
+          android:duration="42" />
+    <item android:drawable="@drawable/ic_media_route_connected_dark_20_mtrl"
+          android:duration="42" />
+    <item android:drawable="@drawable/ic_media_route_connected_dark_21_mtrl"
+          android:duration="42" />
+    <item android:drawable="@drawable/ic_media_route_connected_dark_22_mtrl"
+          android:duration="42" />
+    <item android:drawable="@drawable/ic_media_route_connected_dark_23_mtrl"
+          android:duration="42" />
+    <item android:drawable="@drawable/ic_media_route_connected_dark_24_mtrl"
+          android:duration="42" />
+    <item android:drawable="@drawable/ic_media_route_connected_dark_25_mtrl"
+          android:duration="42" />
+    <item android:drawable="@drawable/ic_media_route_connected_dark_26_mtrl"
+          android:duration="42" />
+    <item android:drawable="@drawable/ic_media_route_connected_dark_27_mtrl"
+          android:duration="42" />
+    <item android:drawable="@drawable/ic_media_route_connected_dark_28_mtrl"
+          android:duration="42" />
+    <item android:drawable="@drawable/ic_media_route_connected_dark_29_mtrl"
+          android:duration="42" />
+    <item android:drawable="@drawable/ic_media_route_connected_dark_30_mtrl"
+          android:duration="42" />
+</animation-list>
diff --git a/core/res/res/drawable/ic_media_route_connected_light_material.xml b/core/res/res/drawable/ic_media_route_connected_light_material.xml
new file mode 100644
index 0000000..466a43a
--- /dev/null
+++ b/core/res/res/drawable/ic_media_route_connected_light_material.xml
@@ -0,0 +1,81 @@
+<?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.
+-->
+
+<animation-list xmlns:android="http://schemas.android.com/apk/res/android" android:oneshot="true">
+    <item android:drawable="@drawable/ic_media_route_connected_light_00_mtrl"
+          android:duration="42" />
+    <item android:drawable="@drawable/ic_media_route_connected_light_01_mtrl"
+          android:duration="42" />
+    <item android:drawable="@drawable/ic_media_route_connected_light_02_mtrl"
+          android:duration="42" />
+    <item android:drawable="@drawable/ic_media_route_connected_light_03_mtrl"
+          android:duration="42" />
+    <item android:drawable="@drawable/ic_media_route_connected_light_04_mtrl"
+          android:duration="42" />
+    <item android:drawable="@drawable/ic_media_route_connected_light_05_mtrl"
+          android:duration="42" />
+    <item android:drawable="@drawable/ic_media_route_connected_light_06_mtrl"
+          android:duration="42" />
+    <item android:drawable="@drawable/ic_media_route_connected_light_07_mtrl"
+          android:duration="42" />
+    <item android:drawable="@drawable/ic_media_route_connected_light_08_mtrl"
+          android:duration="42" />
+    <item android:drawable="@drawable/ic_media_route_connected_light_09_mtrl"
+          android:duration="42" />
+    <item android:drawable="@drawable/ic_media_route_connected_light_10_mtrl"
+          android:duration="42" />
+    <item android:drawable="@drawable/ic_media_route_connected_light_11_mtrl"
+          android:duration="42" />
+    <item android:drawable="@drawable/ic_media_route_connected_light_12_mtrl"
+          android:duration="42" />
+    <item android:drawable="@drawable/ic_media_route_connected_light_13_mtrl"
+          android:duration="42" />
+    <item android:drawable="@drawable/ic_media_route_connected_light_14_mtrl"
+          android:duration="42" />
+    <item android:drawable="@drawable/ic_media_route_connected_light_15_mtrl"
+          android:duration="42" />
+    <item android:drawable="@drawable/ic_media_route_connected_light_16_mtrl"
+          android:duration="42" />
+    <item android:drawable="@drawable/ic_media_route_connected_light_17_mtrl"
+          android:duration="42" />
+    <item android:drawable="@drawable/ic_media_route_connected_light_18_mtrl"
+          android:duration="42" />
+    <item android:drawable="@drawable/ic_media_route_connected_light_19_mtrl"
+          android:duration="42" />
+    <item android:drawable="@drawable/ic_media_route_connected_light_20_mtrl"
+          android:duration="42" />
+    <item android:drawable="@drawable/ic_media_route_connected_light_21_mtrl"
+          android:duration="42" />
+    <item android:drawable="@drawable/ic_media_route_connected_light_22_mtrl"
+          android:duration="42" />
+    <item android:drawable="@drawable/ic_media_route_connected_light_23_mtrl"
+          android:duration="42" />
+    <item android:drawable="@drawable/ic_media_route_connected_light_24_mtrl"
+          android:duration="42" />
+    <item android:drawable="@drawable/ic_media_route_connected_light_25_mtrl"
+          android:duration="42" />
+    <item android:drawable="@drawable/ic_media_route_connected_light_26_mtrl"
+          android:duration="42" />
+    <item android:drawable="@drawable/ic_media_route_connected_light_27_mtrl"
+          android:duration="42" />
+    <item android:drawable="@drawable/ic_media_route_connected_light_28_mtrl"
+          android:duration="42" />
+    <item android:drawable="@drawable/ic_media_route_connected_light_29_mtrl"
+          android:duration="42" />
+    <item android:drawable="@drawable/ic_media_route_connected_light_30_mtrl"
+          android:duration="42" />
+</animation-list>
diff --git a/core/res/res/drawable/ic_media_route_connecting_dark_material.xml b/core/res/res/drawable/ic_media_route_connecting_dark_material.xml
new file mode 100644
index 0000000..1c4bae5
--- /dev/null
+++ b/core/res/res/drawable/ic_media_route_connecting_dark_material.xml
@@ -0,0 +1,81 @@
+<?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.
+-->
+
+<animation-list xmlns:android="http://schemas.android.com/apk/res/android" android:oneshot="false">
+    <item android:drawable="@drawable/ic_media_route_connecting_dark_00_mtrl"
+          android:duration="42" />
+    <item android:drawable="@drawable/ic_media_route_connecting_dark_01_mtrl"
+          android:duration="42" />
+    <item android:drawable="@drawable/ic_media_route_connecting_dark_02_mtrl"
+          android:duration="42" />
+    <item android:drawable="@drawable/ic_media_route_connecting_dark_03_mtrl"
+          android:duration="42" />
+    <item android:drawable="@drawable/ic_media_route_connecting_dark_04_mtrl"
+          android:duration="42" />
+    <item android:drawable="@drawable/ic_media_route_connecting_dark_05_mtrl"
+          android:duration="42" />
+    <item android:drawable="@drawable/ic_media_route_connecting_dark_06_mtrl"
+          android:duration="42" />
+    <item android:drawable="@drawable/ic_media_route_connecting_dark_07_mtrl"
+          android:duration="42" />
+    <item android:drawable="@drawable/ic_media_route_connecting_dark_08_mtrl"
+          android:duration="42" />
+    <item android:drawable="@drawable/ic_media_route_connecting_dark_09_mtrl"
+          android:duration="42" />
+    <item android:drawable="@drawable/ic_media_route_connecting_dark_10_mtrl"
+          android:duration="42" />
+    <item android:drawable="@drawable/ic_media_route_connecting_dark_11_mtrl"
+          android:duration="42" />
+    <item android:drawable="@drawable/ic_media_route_connecting_dark_12_mtrl"
+          android:duration="42" />
+    <item android:drawable="@drawable/ic_media_route_connecting_dark_13_mtrl"
+          android:duration="42" />
+    <item android:drawable="@drawable/ic_media_route_connecting_dark_14_mtrl"
+          android:duration="42" />
+    <item android:drawable="@drawable/ic_media_route_connecting_dark_15_mtrl"
+          android:duration="42" />
+    <item android:drawable="@drawable/ic_media_route_connecting_dark_16_mtrl"
+          android:duration="42" />
+    <item android:drawable="@drawable/ic_media_route_connecting_dark_17_mtrl"
+          android:duration="42" />
+    <item android:drawable="@drawable/ic_media_route_connecting_dark_18_mtrl"
+          android:duration="42" />
+    <item android:drawable="@drawable/ic_media_route_connecting_dark_19_mtrl"
+          android:duration="42" />
+    <item android:drawable="@drawable/ic_media_route_connecting_dark_20_mtrl"
+          android:duration="42" />
+    <item android:drawable="@drawable/ic_media_route_connecting_dark_21_mtrl"
+          android:duration="42" />
+    <item android:drawable="@drawable/ic_media_route_connecting_dark_22_mtrl"
+          android:duration="42" />
+    <item android:drawable="@drawable/ic_media_route_connecting_dark_23_mtrl"
+          android:duration="42" />
+    <item android:drawable="@drawable/ic_media_route_connecting_dark_24_mtrl"
+          android:duration="42" />
+    <item android:drawable="@drawable/ic_media_route_connecting_dark_25_mtrl"
+          android:duration="42" />
+    <item android:drawable="@drawable/ic_media_route_connecting_dark_26_mtrl"
+          android:duration="42" />
+    <item android:drawable="@drawable/ic_media_route_connecting_dark_27_mtrl"
+          android:duration="42" />
+    <item android:drawable="@drawable/ic_media_route_connecting_dark_28_mtrl"
+          android:duration="42" />
+    <item android:drawable="@drawable/ic_media_route_connecting_dark_29_mtrl"
+          android:duration="42" />
+    <item android:drawable="@drawable/ic_media_route_connecting_dark_30_mtrl"
+          android:duration="42" />
+</animation-list>
diff --git a/core/res/res/drawable/ic_media_route_connecting_light_material.xml b/core/res/res/drawable/ic_media_route_connecting_light_material.xml
new file mode 100644
index 0000000..4b7e807
--- /dev/null
+++ b/core/res/res/drawable/ic_media_route_connecting_light_material.xml
@@ -0,0 +1,81 @@
+<?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.
+-->
+
+<animation-list xmlns:android="http://schemas.android.com/apk/res/android" android:oneshot="false">
+    <item android:drawable="@drawable/ic_media_route_connecting_light_00_mtrl"
+          android:duration="42" />
+    <item android:drawable="@drawable/ic_media_route_connecting_light_01_mtrl"
+          android:duration="42" />
+    <item android:drawable="@drawable/ic_media_route_connecting_light_02_mtrl"
+          android:duration="42" />
+    <item android:drawable="@drawable/ic_media_route_connecting_light_03_mtrl"
+          android:duration="42" />
+    <item android:drawable="@drawable/ic_media_route_connecting_light_04_mtrl"
+          android:duration="42" />
+    <item android:drawable="@drawable/ic_media_route_connecting_light_05_mtrl"
+          android:duration="42" />
+    <item android:drawable="@drawable/ic_media_route_connecting_light_06_mtrl"
+          android:duration="42" />
+    <item android:drawable="@drawable/ic_media_route_connecting_light_07_mtrl"
+          android:duration="42" />
+    <item android:drawable="@drawable/ic_media_route_connecting_light_08_mtrl"
+          android:duration="42" />
+    <item android:drawable="@drawable/ic_media_route_connecting_light_09_mtrl"
+          android:duration="42" />
+    <item android:drawable="@drawable/ic_media_route_connecting_light_10_mtrl"
+          android:duration="42" />
+    <item android:drawable="@drawable/ic_media_route_connecting_light_11_mtrl"
+          android:duration="42" />
+    <item android:drawable="@drawable/ic_media_route_connecting_light_12_mtrl"
+          android:duration="42" />
+    <item android:drawable="@drawable/ic_media_route_connecting_light_13_mtrl"
+          android:duration="42" />
+    <item android:drawable="@drawable/ic_media_route_connecting_light_14_mtrl"
+          android:duration="42" />
+    <item android:drawable="@drawable/ic_media_route_connecting_light_15_mtrl"
+          android:duration="42" />
+    <item android:drawable="@drawable/ic_media_route_connecting_light_16_mtrl"
+          android:duration="42" />
+    <item android:drawable="@drawable/ic_media_route_connecting_light_17_mtrl"
+          android:duration="42" />
+    <item android:drawable="@drawable/ic_media_route_connecting_light_18_mtrl"
+          android:duration="42" />
+    <item android:drawable="@drawable/ic_media_route_connecting_light_19_mtrl"
+          android:duration="42" />
+    <item android:drawable="@drawable/ic_media_route_connecting_light_20_mtrl"
+          android:duration="42" />
+    <item android:drawable="@drawable/ic_media_route_connecting_light_21_mtrl"
+          android:duration="42" />
+    <item android:drawable="@drawable/ic_media_route_connecting_light_22_mtrl"
+          android:duration="42" />
+    <item android:drawable="@drawable/ic_media_route_connecting_light_23_mtrl"
+          android:duration="42" />
+    <item android:drawable="@drawable/ic_media_route_connecting_light_24_mtrl"
+          android:duration="42" />
+    <item android:drawable="@drawable/ic_media_route_connecting_light_25_mtrl"
+          android:duration="42" />
+    <item android:drawable="@drawable/ic_media_route_connecting_light_26_mtrl"
+          android:duration="42" />
+    <item android:drawable="@drawable/ic_media_route_connecting_light_27_mtrl"
+          android:duration="42" />
+    <item android:drawable="@drawable/ic_media_route_connecting_light_28_mtrl"
+          android:duration="42" />
+    <item android:drawable="@drawable/ic_media_route_connecting_light_29_mtrl"
+          android:duration="42" />
+    <item android:drawable="@drawable/ic_media_route_connecting_light_30_mtrl"
+          android:duration="42" />
+</animation-list>
diff --git a/core/res/res/drawable/ic_media_route_connecting_material.xml b/core/res/res/drawable/ic_media_route_connecting_material.xml
deleted file mode 100644
index 51decd3..0000000
--- a/core/res/res/drawable/ic_media_route_connecting_material.xml
+++ /dev/null
@@ -1,36 +0,0 @@
-<?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.
--->
-
-<animation-list xmlns:android="http://schemas.android.com/apk/res/android"
-    android:oneshot="false">
-    <item android:duration="500">
-        <bitmap android:src="@drawable/ic_media_route_on_0_mtrl_alpha"
-            android:tint="?attr/colorControlNormal" />
-    </item>
-    <item android:duration="500">
-        <bitmap android:src="@drawable/ic_media_route_on_1_mtrl_alpha"
-            android:tint="?attr/colorControlNormal" />
-    </item>
-    <item android:duration="500">
-        <bitmap android:src="@drawable/ic_media_route_on_2_mtrl_alpha"
-            android:tint="?attr/colorControlNormal" />
-    </item>
-    <item android:duration="500">
-        <bitmap android:src="@drawable/ic_media_route_on_1_mtrl_alpha"
-            android:tint="?attr/colorControlNormal" />
-    </item>
-</animation-list>
diff --git a/core/res/res/drawable/ic_media_route_dark_material.xml b/core/res/res/drawable/ic_media_route_dark_material.xml
new file mode 100644
index 0000000..2a60f58
--- /dev/null
+++ b/core/res/res/drawable/ic_media_route_dark_material.xml
@@ -0,0 +1,30 @@
+<?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.
+-->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:state_checked="true" android:state_enabled="true"
+        android:drawable="@android:drawable/ic_media_route_connecting_dark_material" />
+    <item android:state_activated="true" android:state_enabled="true"
+        android:drawable="@android:drawable/ic_media_route_connected_dark_material" />
+    <item android:state_enabled="true">
+        <bitmap android:src="@android:drawable/ic_media_route_off_dark_mtrl"/>
+    </item>
+    <item>
+        <bitmap android:src="@android:drawable/ic_media_route_off_dark_mtrl"
+            android:alpha="0.5" />
+    </item>
+</selector>
diff --git a/core/res/res/drawable/ic_media_route_light_material.xml b/core/res/res/drawable/ic_media_route_light_material.xml
new file mode 100644
index 0000000..f35187e
--- /dev/null
+++ b/core/res/res/drawable/ic_media_route_light_material.xml
@@ -0,0 +1,30 @@
+<?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.
+-->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:state_checked="true" android:state_enabled="true"
+          android:drawable="@android:drawable/ic_media_route_connecting_light_material" />
+    <item android:state_activated="true" android:state_enabled="true"
+          android:drawable="@android:drawable/ic_media_route_connected_light_material" />
+    <item android:state_enabled="true">
+        <bitmap android:src="@android:drawable/ic_media_route_off_light_mtrl"/>
+    </item>
+    <item>
+        <bitmap android:src="@android:drawable/ic_media_route_off_light_mtrl"
+            android:alpha="0.62" />
+    </item>
+</selector>
\ No newline at end of file
diff --git a/core/res/res/drawable/ic_media_route_material.xml b/core/res/res/drawable/ic_media_route_material.xml
deleted file mode 100644
index 3e3f388..0000000
--- a/core/res/res/drawable/ic_media_route_material.xml
+++ /dev/null
@@ -1,33 +0,0 @@
-<?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.
--->
-
-<selector xmlns:android="http://schemas.android.com/apk/res/android">
-    <item android:state_checked="true" android:state_enabled="true"
-        android:drawable="@android:drawable/ic_media_route_connecting_material" />
-    <item android:state_activated="true" android:state_enabled="true">
-        <bitmap android:src="@android:drawable/ic_media_route_on_mtrl_alpha"
-            android:tint="?attr/colorControlNormal" />
-    </item>
-    <item android:state_enabled="true">
-        <bitmap android:src="@android:drawable/ic_media_route_off_mtrl_alpha"
-            android:tint="?attr/colorControlNormal" />
-    </item>
-    <item>
-        <bitmap android:src="@android:drawable/ic_media_route_disabled_mtrl_alpha"
-            android:tint="?attr/colorControlNormal" />
-    </item>
-</selector>
diff --git a/core/res/res/drawable/scrollbar_handle_material.xml b/core/res/res/drawable/scrollbar_handle_material.xml
index 33efbba..f020112 100644
--- a/core/res/res/drawable/scrollbar_handle_material.xml
+++ b/core/res/res/drawable/scrollbar_handle_material.xml
@@ -19,7 +19,4 @@
        android:shape="rectangle">
     <solid
         android:color="#84ffffff" />
-    <size
-        android:width="4dp"
-        android:height="4dp" />
 </shape>
diff --git a/core/res/res/layout/notification_template_material_big_text.xml b/core/res/res/layout/notification_template_material_big_text.xml
index 1aca99f..0cfe689 100644
--- a/core/res/res/layout/notification_template_material_big_text.xml
+++ b/core/res/res/layout/notification_template_material_big_text.xml
@@ -55,6 +55,7 @@
                 android:singleLine="false"
                 android:gravity="top"
                 android:visibility="gone"
+                android:textAlignment="viewStart"
                 />
         </LinearLayout>
 
diff --git a/core/res/res/layout/notification_template_part_line1.xml b/core/res/res/layout/notification_template_part_line1.xml
index 308b30f..ca35de3 100644
--- a/core/res/res/layout/notification_template_part_line1.xml
+++ b/core/res/res/layout/notification_template_part_line1.xml
@@ -28,6 +28,7 @@
         android:singleLine="true"
         android:ellipsize="marquee"
         android:fadingEdge="horizontal"
+        android:textAlignment="viewStart"
         />
     <TextView android:id="@+id/text_line_1"
         android:textAppearance="@style/TextAppearance.Material.Notification"
diff --git a/core/res/res/layout/notification_template_text.xml b/core/res/res/layout/notification_template_text.xml
index 20c2896..68f34c8 100644
--- a/core/res/res/layout/notification_template_text.xml
+++ b/core/res/res/layout/notification_template_text.xml
@@ -24,5 +24,6 @@
     android:fadingEdge="horizontal"
     android:gravity="top"
     android:singleLine="true"
+    android:textAlignment="viewStart"
     android:textAppearance="@style/TextAppearance.Material.Notification"
     />
diff --git a/core/res/res/values-bs/strings.xml b/core/res/res/values-bs/strings.xml
index 186cbbf..1c1ec25 100644
--- a/core/res/res/values-bs/strings.xml
+++ b/core/res/res/values-bs/strings.xml
@@ -1509,7 +1509,7 @@
     <string name="leave_accessibility_shortcut_on" msgid="7653111894438512680">"Koristi prečicu"</string>
     <string name="accessibility_shortcut_enabling_service" msgid="7771852911861522636">"Prečica za pristupačnost je uključila uslugu <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
     <string name="accessibility_shortcut_disabling_service" msgid="2747243438223109821">"Prečica za pristupačnost je isključila uslugu <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
-    <string name="accessibility_button_prompt_text" msgid="4234556536456854251">"Izaberite funkciju koja će se koristiti kada dodirnete dugme Pristupačnost:"</string>
+    <string name="accessibility_button_prompt_text" msgid="4234556536456854251">"Odaberite funkciju koja će se koristiti kada dodirnete dugme Pristupačnost:"</string>
     <string name="accessibility_button_instructional_text" msgid="6942300463612999993">"Da promijenite funkcije, dodirnite i držite dugme Pristupačnost."</string>
     <string name="accessibility_magnification_chooser_text" msgid="1227146738764986237">"Uvećanje"</string>
     <string name="user_switched" msgid="3768006783166984410">"Trenutni korisnik <xliff:g id="NAME">%1$s</xliff:g>."</string>
@@ -1736,7 +1736,7 @@
     <string name="user_creation_adding" msgid="4482658054622099197">"Da li dozvoljavate da <xliff:g id="APP">%1$s</xliff:g> kreira novog korisnika za <xliff:g id="ACCOUNT">%2$s</xliff:g> (Korisnik sa ovim nalogom već postoji)?"</string>
     <string name="language_selection_title" msgid="2680677278159281088">"Dodaj jezik"</string>
     <string name="country_selection_title" msgid="2954859441620215513">"Izbor regije"</string>
-    <string name="search_language_hint" msgid="7042102592055108574">"Ukucajte ime jezika"</string>
+    <string name="search_language_hint" msgid="7042102592055108574">"Upišite ime jezika"</string>
     <string name="language_picker_section_suggested" msgid="8414489646861640885">"Predloženo"</string>
     <string name="language_picker_section_all" msgid="3097279199511617537">"Svi jezici"</string>
     <string name="region_picker_section_all" msgid="8966316787153001779">"Sve regije"</string>
diff --git a/core/res/res/values-es/strings.xml b/core/res/res/values-es/strings.xml
index e8df21f..6c5e7e5 100644
--- a/core/res/res/values-es/strings.xml
+++ b/core/res/res/values-es/strings.xml
@@ -1296,7 +1296,7 @@
     <string name="submit" msgid="1602335572089911941">"Enviar"</string>
     <string name="car_mode_disable_notification_title" msgid="3164768212003864316">"Se ha habilitado el modo coche"</string>
     <string name="car_mode_disable_notification_message" msgid="6301524980144350051">"Toca para salir del modo coche."</string>
-    <string name="tethered_notification_title" msgid="3146694234398202601">"Compartir Internet/Zona Wi-Fi activado"</string>
+    <string name="tethered_notification_title" msgid="3146694234398202601">"Compartir conexión/Zona Wi-Fi activada"</string>
     <string name="tethered_notification_message" msgid="2113628520792055377">"Toca para configurar."</string>
     <string name="back_button_label" msgid="2300470004503343439">"Atrás"</string>
     <string name="next_button_label" msgid="1080555104677992408">"Siguiente"</string>
@@ -1383,7 +1383,7 @@
     <string name="data_usage_mobile_limit_snoozed_title" msgid="279240572165412168">"Límite de datos móviles superado"</string>
     <string name="data_usage_wifi_limit_snoozed_title" msgid="8743856006384825974">"Límite de datos Wi-Fi superado"</string>
     <string name="data_usage_limit_snoozed_body" msgid="7035490278298441767">"Límite superado en <xliff:g id="SIZE">%s</xliff:g>"</string>
-    <string name="data_usage_restricted_title" msgid="5965157361036321914">"Conexiones automáticas restringidas"</string>
+    <string name="data_usage_restricted_title" msgid="5965157361036321914">"Datos en segundo plano restringidos"</string>
     <string name="data_usage_restricted_body" msgid="469866376337242726">"Toca para quitar la restricción."</string>
     <string name="ssl_certificate" msgid="6510040486049237639">"Certificado de seguridad"</string>
     <string name="ssl_certificate_is_valid" msgid="6825263250774569373">"Este certificado es válido."</string>
@@ -1618,7 +1618,7 @@
     <string name="package_installed_device_owner" msgid="6875717669960212648">"Instalado por el administrador"</string>
     <string name="package_updated_device_owner" msgid="1847154566357862089">"Actualizado por el administrador"</string>
     <string name="package_deleted_device_owner" msgid="2307122077550236438">"Eliminado por el administrador"</string>
-    <string name="battery_saver_description" msgid="1960431123816253034">"Para mejorar la duración de la batería, la función de ahorro de batería reduce el rendimiento del dispositivo y limita la vibración, los servicios de ubicación y la mayor parte de la transmisión de datos en segundo plano. Es posible que las aplicaciones que se sincronizan, como las de correo y mensajes, no se actualicen a menos que las abras.\n\nLa función de ahorro de batería se desactiva automáticamente cuando el dispositivo se está cargando."</string>
+    <string name="battery_saver_description" msgid="1960431123816253034">"Para mejorar la duración de la batería, la función de ahorro de batería reduce el rendimiento del dispositivo y limita la vibración, los servicios de ubicación y la mayor parte de los datos en segundo plano. Es posible que las aplicaciones que se sincronizan, como las de correo y mensajes, no se actualicen a menos que las abras.\n\nLa función de ahorro de batería se desactiva automáticamente cuando el dispositivo se está cargando."</string>
     <string name="data_saver_description" msgid="6015391409098303235">"El ahorro de datos evita que algunas aplicaciones envíen o reciban datos en segundo plano, lo que permite reducir el uso de datos. Una aplicación activa podrá acceder a los datos, aunque con menos frecuencia. Esto significa que, por ejemplo, algunas imágenes no se muestren hasta que no las toques."</string>
     <string name="data_saver_enable_title" msgid="4674073932722787417">"¿Activar ahorro de datos?"</string>
     <string name="data_saver_enable_button" msgid="7147735965247211818">"Activar"</string>
diff --git a/core/res/res/values-eu/strings.xml b/core/res/res/values-eu/strings.xml
index 36922d9..b5234a8 100644
--- a/core/res/res/values-eu/strings.xml
+++ b/core/res/res/values-eu/strings.xml
@@ -1770,8 +1770,7 @@
     <string name="etws_primary_default_message_tsunami" msgid="1887685943498368548">"Ebakuatu kostaldeak eta ibaialdeak berehala eta joan toki seguru batera, adibidez, toki garai batera."</string>
     <string name="etws_primary_default_message_earthquake_and_tsunami" msgid="998797956848445862">"Ez larritu eta bilatu babesleku bat inguruan."</string>
     <string name="etws_primary_default_message_test" msgid="2709597093560037455">"Larrialdi-mezuen proba"</string>
-    <!-- no translation found for notification_reply_button_accessibility (3621714652387814344) -->
-    <skip />
+    <string name="notification_reply_button_accessibility" msgid="3621714652387814344">"Erantzun"</string>
     <string name="etws_primary_default_message_others" msgid="6293148756130398971"></string>
     <string name="mmcc_authentication_reject" msgid="7729819349669603406">"Ez da onartzen SIM txartela"</string>
     <string name="mmcc_imsi_unknown_in_hlr" msgid="6321202257374418726">"Ez dago SIM txartelik"</string>
diff --git a/core/res/res/values-fr-rCA/strings.xml b/core/res/res/values-fr-rCA/strings.xml
index a39bc3f..c688182 100644
--- a/core/res/res/values-fr-rCA/strings.xml
+++ b/core/res/res/values-fr-rCA/strings.xml
@@ -1769,8 +1769,7 @@
     <string name="etws_primary_default_message_tsunami" msgid="1887685943498368548">"Évacuez immédiatement les zones côtières et les rives des fleuves, et réfugiez-vous dans un endroit plus sécuritaire, comme un terrain surélevé."</string>
     <string name="etws_primary_default_message_earthquake_and_tsunami" msgid="998797956848445862">"Restez calme et cherchez un abri à proximité."</string>
     <string name="etws_primary_default_message_test" msgid="2709597093560037455">"Test de messages d\'urgence"</string>
-    <!-- no translation found for notification_reply_button_accessibility (3621714652387814344) -->
-    <skip />
+    <string name="notification_reply_button_accessibility" msgid="3621714652387814344">"Répondre"</string>
     <string name="etws_primary_default_message_others" msgid="6293148756130398971"></string>
     <string name="mmcc_authentication_reject" msgid="7729819349669603406">"Carte SIM non autorisée"</string>
     <string name="mmcc_imsi_unknown_in_hlr" msgid="6321202257374418726">"Carte SIM non configurée"</string>
diff --git a/core/res/res/values-fr/strings.xml b/core/res/res/values-fr/strings.xml
index 728de05..a5c9b0f 100644
--- a/core/res/res/values-fr/strings.xml
+++ b/core/res/res/values-fr/strings.xml
@@ -1420,7 +1420,7 @@
     <string name="media_route_button_content_description" msgid="591703006349356016">"Caster"</string>
     <string name="media_route_chooser_title" msgid="1751618554539087622">"Connexion à l\'appareil"</string>
     <string name="media_route_chooser_title_for_remote_display" msgid="3395541745872017583">"Caster l\'écran sur l\'appareil"</string>
-    <string name="media_route_chooser_searching" msgid="4776236202610828706">"Recherche d\'appareils en cours…"</string>
+    <string name="media_route_chooser_searching" msgid="4776236202610828706">"Recherche d\'appareils…"</string>
     <string name="media_route_chooser_extended_settings" msgid="87015534236701604">"Paramètres"</string>
     <string name="media_route_controller_disconnect" msgid="8966120286374158649">"Déconnecter"</string>
     <string name="media_route_status_scanning" msgid="7279908761758293783">"Analyse en cours..."</string>
diff --git a/core/res/res/values-gl/strings.xml b/core/res/res/values-gl/strings.xml
index 09104db..8db169b 100644
--- a/core/res/res/values-gl/strings.xml
+++ b/core/res/res/values-gl/strings.xml
@@ -1770,8 +1770,7 @@
     <string name="etws_primary_default_message_tsunami" msgid="1887685943498368548">"Abandona de inmediato rexións costeiras e situadas na beira de ríos para dirixirte a un lugar máis seguro, como un terreo elevado."</string>
     <string name="etws_primary_default_message_earthquake_and_tsunami" msgid="998797956848445862">"Mantén a calma e busca refuxio cerca."</string>
     <string name="etws_primary_default_message_test" msgid="2709597093560037455">"Proba de mensaxes de emerxencia"</string>
-    <!-- no translation found for notification_reply_button_accessibility (3621714652387814344) -->
-    <skip />
+    <string name="notification_reply_button_accessibility" msgid="3621714652387814344">"Responder"</string>
     <string name="etws_primary_default_message_others" msgid="6293148756130398971"></string>
     <string name="mmcc_authentication_reject" msgid="7729819349669603406">"Non se admite a tarxeta SIM"</string>
     <string name="mmcc_imsi_unknown_in_hlr" msgid="6321202257374418726">"Non se introduciu ningunha tarxeta SIM"</string>
diff --git a/core/res/res/values-hi/strings.xml b/core/res/res/values-hi/strings.xml
index f0732d4..bffede4 100644
--- a/core/res/res/values-hi/strings.xml
+++ b/core/res/res/values-hi/strings.xml
@@ -837,7 +837,7 @@
     <string name="save_password_remember" msgid="6491879678996749466">"याद रखें"</string>
     <string name="save_password_never" msgid="8274330296785855105">"कभी नहीं"</string>
     <string name="open_permission_deny" msgid="7374036708316629800">"आपके पास इस पेज को खोलने की अनुमति नहीं है."</string>
-    <string name="text_copied" msgid="4985729524670131385">"लेख की क्‍लिपबोर्ड पर प्रतिलिपि बनाई गई."</string>
+    <string name="text_copied" msgid="4985729524670131385">"लेख को क्‍लिपबोर्ड पर कॉपी किया गया."</string>
     <string name="more_item_label" msgid="4650918923083320495">"अधिक"</string>
     <string name="prepend_shortcut_label" msgid="2572214461676015642">"मेनू+"</string>
     <string name="menu_space_shortcut_label" msgid="2410328639272162537">"space"</string>
@@ -954,12 +954,12 @@
     <string name="elapsed_time_short_format_h_mm_ss" msgid="1846071997616654124">"<xliff:g id="HOURS">%1$d</xliff:g>:<xliff:g id="MINUTES">%2$02d</xliff:g>:<xliff:g id="SECONDS">%3$02d</xliff:g>"</string>
     <string name="selectAll" msgid="6876518925844129331">"सभी को चुनें"</string>
     <string name="cut" msgid="3092569408438626261">"काटें"</string>
-    <string name="copy" msgid="2681946229533511987">"प्रतिलिपि बनाएं"</string>
+    <string name="copy" msgid="2681946229533511987">"कॉपी करें"</string>
     <string name="paste" msgid="5629880836805036433">"चिपकाएं"</string>
     <string name="paste_as_plain_text" msgid="5427792741908010675">"सादे पाठ के रूप में चिपकाएं"</string>
     <string name="replace" msgid="5781686059063148930">"बदलें•"</string>
     <string name="delete" msgid="6098684844021697789">"हटाएं"</string>
-    <string name="copyUrl" msgid="2538211579596067402">"URL की प्रतिलिपि बनाएं"</string>
+    <string name="copyUrl" msgid="2538211579596067402">"URL को कॉपी करें"</string>
     <string name="selectTextMode" msgid="1018691815143165326">"लेख को चुनें"</string>
     <string name="undo" msgid="7905788502491742328">"वापस लाएं"</string>
     <string name="redo" msgid="7759464876566803888">"फिर से करें"</string>
diff --git a/core/res/res/values-hy/strings.xml b/core/res/res/values-hy/strings.xml
index 5b741ca..7cf7507 100644
--- a/core/res/res/values-hy/strings.xml
+++ b/core/res/res/values-hy/strings.xml
@@ -567,8 +567,8 @@
     <string name="policydesc_setGlobalProxy" msgid="8459859731153370499">"Կարգավորել, որ սարքի համընդհանուր պրոքսի-սերվերն օգտագործվի, երբ քաղաքականությունը միացված է: Միայն սարքի սեփականատերը կարող է կարգավորել համընդհանուր պրոքսի-սերվերը:"</string>
     <string name="policylab_expirePassword" msgid="5610055012328825874">"Նշել էկր կողպ գաղտնաբ սպառումը"</string>
     <string name="policydesc_expirePassword" msgid="5367525762204416046">"Փոխել էկրանի կողպման գաղտնաբառի, PIN-ի կամ նախշի փոփոխման հաճախականությունը:"</string>
-    <string name="policylab_encryptedStorage" msgid="8901326199909132915">"Կարգավորել պահոցի կոդավորումը"</string>
-    <string name="policydesc_encryptedStorage" msgid="2637732115325316992">"Պահանջել, որ պահվող հավելվածների տվյալները լինեն կոդավորված:"</string>
+    <string name="policylab_encryptedStorage" msgid="8901326199909132915">"Կարգավորել պահոցի գաղտնագրումը"</string>
+    <string name="policydesc_encryptedStorage" msgid="2637732115325316992">"Պահանջել, որ պահվող հավելվածների տվյալները լինեն գաղտնագրված:"</string>
     <string name="policylab_disableCamera" msgid="6395301023152297826">"Կասեցնել տեսախցիկները"</string>
     <string name="policydesc_disableCamera" msgid="2306349042834754597">"Կանխել բոլոր սարքերի ֆոտոխցիկների օգտագործումը:"</string>
     <string name="policylab_disableKeyguardFeatures" msgid="8552277871075367771">"Անջատել կողպման գործառույթները"</string>
diff --git a/core/res/res/values-iw/strings.xml b/core/res/res/values-iw/strings.xml
index 938b03a..426442c 100644
--- a/core/res/res/values-iw/strings.xml
+++ b/core/res/res/values-iw/strings.xml
@@ -1837,8 +1837,7 @@
     <string name="etws_primary_default_message_tsunami" msgid="1887685943498368548">"יש להתפנות מיידית מאזורים הסמוכים לחופים ולנהרות למקום בטוח יותר, כגון שטח גבוה יותר."</string>
     <string name="etws_primary_default_message_earthquake_and_tsunami" msgid="998797956848445862">"הישאר רגוע וחפש מחסה בקרבת מקום."</string>
     <string name="etws_primary_default_message_test" msgid="2709597093560037455">"בדיקה של הודעות חירום"</string>
-    <!-- no translation found for notification_reply_button_accessibility (3621714652387814344) -->
-    <skip />
+    <string name="notification_reply_button_accessibility" msgid="3621714652387814344">"השב"</string>
     <string name="etws_primary_default_message_others" msgid="6293148756130398971"></string>
     <string name="mmcc_authentication_reject" msgid="7729819349669603406">"‏כרטיס ה-SIM לא מורשה"</string>
     <string name="mmcc_imsi_unknown_in_hlr" msgid="6321202257374418726">"‏כרטיס ה-SIM לא מזוהה"</string>
diff --git a/core/res/res/values-lo/strings.xml b/core/res/res/values-lo/strings.xml
index affd287..52d5b0b 100644
--- a/core/res/res/values-lo/strings.xml
+++ b/core/res/res/values-lo/strings.xml
@@ -1769,8 +1769,7 @@
     <string name="etws_primary_default_message_tsunami" msgid="1887685943498368548">"ອົບພະຍົບອອກຈາກເຂດຊາຍຝັ່ງທະເລ ແລະ ບໍລິເວນແມ່ນ້ຳໄປບ່ອນທີ່ປອດໄພກວ່າ ເຊັ່ນ: ບ່ອນສູງ ໂດຍທັນທີ."</string>
     <string name="etws_primary_default_message_earthquake_and_tsunami" msgid="998797956848445862">"ໃຈເຢັນໆ ແລະ ຊອກຫາບ່ອນພັກຢູ່ໃກ້ໆ."</string>
     <string name="etws_primary_default_message_test" msgid="2709597093560037455">"ທົດສອບຂໍ້ຄວາມສຸກເສີນ"</string>
-    <!-- no translation found for notification_reply_button_accessibility (3621714652387814344) -->
-    <skip />
+    <string name="notification_reply_button_accessibility" msgid="3621714652387814344">"ຕອບກັບ"</string>
     <string name="etws_primary_default_message_others" msgid="6293148756130398971"></string>
     <string name="mmcc_authentication_reject" msgid="7729819349669603406">"ບໍ່ອະນຸຍາດໃຫ້ໃຊ້ SIM"</string>
     <string name="mmcc_imsi_unknown_in_hlr" msgid="6321202257374418726">"ບໍ່ມີການນຳໃຊ້ SIM"</string>
diff --git a/core/res/res/values-sl/strings.xml b/core/res/res/values-sl/strings.xml
index 48baf05..ac24366 100644
--- a/core/res/res/values-sl/strings.xml
+++ b/core/res/res/values-sl/strings.xml
@@ -1837,8 +1837,7 @@
     <string name="etws_primary_default_message_tsunami" msgid="1887685943498368548">"Takoj se umaknite z obalnih območij in bregov rek na varnejše mesto, na primer na višje ležeča mesta."</string>
     <string name="etws_primary_default_message_earthquake_and_tsunami" msgid="998797956848445862">"Ostanite mirni in poiščite zavetje v bližini."</string>
     <string name="etws_primary_default_message_test" msgid="2709597093560037455">"Preskus sporočil v sili"</string>
-    <!-- no translation found for notification_reply_button_accessibility (3621714652387814344) -->
-    <skip />
+    <string name="notification_reply_button_accessibility" msgid="3621714652387814344">"Odgovor"</string>
     <string name="etws_primary_default_message_others" msgid="6293148756130398971"></string>
     <string name="mmcc_authentication_reject" msgid="7729819349669603406">"Kartica SIM ni dovoljena"</string>
     <string name="mmcc_imsi_unknown_in_hlr" msgid="6321202257374418726">"Kartica SIM ni omogočena za uporabo"</string>
diff --git a/core/res/res/values-tr/strings.xml b/core/res/res/values-tr/strings.xml
index aa63562..48c714b 100644
--- a/core/res/res/values-tr/strings.xml
+++ b/core/res/res/values-tr/strings.xml
@@ -1769,8 +1769,7 @@
     <string name="etws_primary_default_message_tsunami" msgid="1887685943498368548">"Kıyı kesimlerini ve nehir kenarlarını hemen boşaltarak yüksek yerler gibi daha güvenli bölgelere gidin."</string>
     <string name="etws_primary_default_message_earthquake_and_tsunami" msgid="998797956848445862">"Sakin olun ve yakınlarda sığınabileceğiniz bir yer bulun."</string>
     <string name="etws_primary_default_message_test" msgid="2709597093560037455">"Acil durum mesajları testi"</string>
-    <!-- no translation found for notification_reply_button_accessibility (3621714652387814344) -->
-    <skip />
+    <string name="notification_reply_button_accessibility" msgid="3621714652387814344">"Yanıtla"</string>
     <string name="etws_primary_default_message_others" msgid="6293148756130398971"></string>
     <string name="mmcc_authentication_reject" msgid="7729819349669603406">"SIM\'e izin verilmiyor"</string>
     <string name="mmcc_imsi_unknown_in_hlr" msgid="6321202257374418726">"SIM için temel hazırlık yapılmadı"</string>
diff --git a/core/res/res/values-zh-rCN/strings.xml b/core/res/res/values-zh-rCN/strings.xml
index 0a39be0..dc0395a 100644
--- a/core/res/res/values-zh-rCN/strings.xml
+++ b/core/res/res/values-zh-rCN/strings.xml
@@ -999,7 +999,7 @@
     <string name="whichSendApplicationNamed" msgid="2799370240005424391">"使用%1$s分享"</string>
     <string name="whichSendApplicationLabel" msgid="4579076294675975354">"分享"</string>
     <string name="whichSendToApplication" msgid="8272422260066642057">"通过以下应用发送"</string>
-    <string name="whichSendToApplicationNamed" msgid="7768387871529295325">"通过1$s发送"</string>
+    <string name="whichSendToApplicationNamed" msgid="7768387871529295325">"通过%1$s发送"</string>
     <string name="whichSendToApplicationLabel" msgid="8878962419005813500">"发送"</string>
     <string name="whichHomeApplication" msgid="4307587691506919691">"选择主屏幕应用"</string>
     <string name="whichHomeApplicationNamed" msgid="4493438593214760979">"将“%1$s”设为主屏幕应用"</string>
@@ -1769,8 +1769,7 @@
     <string name="etws_primary_default_message_tsunami" msgid="1887685943498368548">"请立即从沿海和河滨区域撤离到高地等较安全的地方。"</string>
     <string name="etws_primary_default_message_earthquake_and_tsunami" msgid="998797956848445862">"请保持冷静,并寻找附近的避难地点。"</string>
     <string name="etws_primary_default_message_test" msgid="2709597093560037455">"紧急消息测试"</string>
-    <!-- no translation found for notification_reply_button_accessibility (3621714652387814344) -->
-    <skip />
+    <string name="notification_reply_button_accessibility" msgid="3621714652387814344">"回复"</string>
     <string name="etws_primary_default_message_others" msgid="6293148756130398971"></string>
     <string name="mmcc_authentication_reject" msgid="7729819349669603406">"不受允许的 SIM 卡"</string>
     <string name="mmcc_imsi_unknown_in_hlr" msgid="6321202257374418726">"未配置的 SIM 卡"</string>
diff --git a/core/res/res/values/arrays.xml b/core/res/res/values/arrays.xml
index e190bd1..733878b 100644
--- a/core/res/res/values/arrays.xml
+++ b/core/res/res/values/arrays.xml
@@ -22,66 +22,39 @@
     <!-- Do not translate. These are all of the drawable resources that should be preloaded by
          the zygote process before it starts forking application processes. -->
     <array name="preloaded_drawables">
-        <item>@drawable/ab_share_pack_material</item>
-        <item>@drawable/ab_solid_shadow_material</item>
         <item>@drawable/action_bar_item_background_material</item>
         <item>@drawable/activated_background_material</item>
         <item>@drawable/btn_borderless_material</item>
         <item>@drawable/btn_check_material_anim</item>
         <item>@drawable/btn_colored_material</item>
         <item>@drawable/btn_default_material</item>
-        <item>@drawable/btn_group_holo_dark</item>
-        <item>@drawable/btn_group_holo_light</item>
         <item>@drawable/btn_radio_material_anim</item>
-        <item>@drawable/btn_star_material</item>
         <item>@drawable/btn_toggle_material</item>
-        <item>@drawable/button_inset</item>
-        <item>@drawable/cab_background_bottom_material</item>
-        <item>@drawable/cab_background_top_material</item>
         <item>@drawable/control_background_32dp_material</item>
         <item>@drawable/control_background_40dp_material</item>
         <item>@drawable/dialog_background_material</item>
-        <item>@drawable/editbox_dropdown_background_dark</item>
-        <item>@drawable/edit_text_material</item>
-        <item>@drawable/expander_group_material</item>
         <item>@drawable/fastscroll_label_left_material</item>
         <item>@drawable/fastscroll_label_right_material</item>
         <item>@drawable/fastscroll_thumb_material</item>
         <item>@drawable/fastscroll_track_material</item>
         <item>@drawable/floating_popup_background_dark</item>
         <item>@drawable/floating_popup_background_light</item>
-        <item>@drawable/gallery_item_background</item>
         <item>@drawable/ic_ab_back_material</item>
         <item>@drawable/ic_ab_back_material_dark</item>
         <item>@drawable/ic_ab_back_material_light</item>
         <item>@drawable/ic_account_circle</item>
         <item>@drawable/ic_arrow_drop_right_black_24dp</item>
-        <item>@drawable/ic_clear</item>
-        <item>@drawable/ic_clear_disabled</item>
         <item>@drawable/ic_clear_material</item>
-        <item>@drawable/ic_clear_normal</item>
-        <item>@drawable/ic_commit_search_api_material</item>
         <item>@drawable/ic_dialog_alert_material</item>
-        <item>@drawable/ic_find_next_material</item>
-        <item>@drawable/ic_find_previous_material</item>
-        <item>@drawable/ic_go</item>
         <item>@drawable/ic_go_search_api_material</item>
-        <item>@drawable/ic_media_route_connecting_material</item>
-        <item>@drawable/ic_media_route_material</item>
-        <item>@drawable/ic_menu_close_clear_cancel</item>
         <item>@drawable/ic_menu_copy_material</item>
         <item>@drawable/ic_menu_cut_material</item>
-        <item>@drawable/ic_menu_find_material</item>
-        <item>@drawable/ic_menu_more</item>
         <item>@drawable/ic_menu_moreoverflow_material</item>
         <item>@drawable/ic_menu_paste_material</item>
-        <item>@drawable/ic_menu_search_material</item>
         <item>@drawable/ic_menu_selectall_material</item>
         <item>@drawable/ic_menu_share_material</item>
         <item>@drawable/ic_search_api_material</item>
         <item>@drawable/ic_voice_search_api_material</item>
-        <item>@drawable/indicator_check_mark_dark</item>
-        <item>@drawable/indicator_check_mark_light</item>
         <item>@drawable/item_background_borderless_material</item>
         <item>@drawable/item_background_borderless_material_dark</item>
         <item>@drawable/item_background_borderless_material_light</item>
@@ -90,30 +63,15 @@
         <item>@drawable/item_background_material_light</item>
         <item>@drawable/list_choice_background_material</item>
         <item>@drawable/list_divider_material</item>
-        <item>@drawable/list_section_divider_material</item>
-        <item>@drawable/menu_background_fill_parent_width</item>
         <item>@drawable/notification_material_action_background</item>
         <item>@drawable/notification_material_media_action_background</item>
         <item>@drawable/number_picker_divider_material</item>
         <item>@drawable/popup_background_material</item>
-        <item>@drawable/popup_inline_error_above_holo_dark</item>
-        <item>@drawable/popup_inline_error_above_holo_light</item>
-        <item>@drawable/popup_inline_error_holo_dark</item>
-        <item>@drawable/popup_inline_error_holo_light</item>
         <item>@drawable/progress_horizontal_material</item>
         <item>@drawable/progress_indeterminate_horizontal_material</item>
         <item>@drawable/progress_large_material</item>
         <item>@drawable/progress_medium_material</item>
         <item>@drawable/progress_small_material</item>
-        <item>@drawable/quickcontact_badge_overlay_dark</item>
-        <item>@drawable/quickcontact_badge_overlay_light</item>
-        <item>@drawable/quickcontact_badge_overlay_normal_dark</item>
-        <item>@drawable/quickcontact_badge_overlay_normal_light</item>
-        <item>@drawable/quickcontact_badge_overlay_pressed_dark</item>
-        <item>@drawable/quickcontact_badge_overlay_pressed_light</item>
-        <item>@drawable/ratingbar_indicator_material</item>
-        <item>@drawable/ratingbar_material</item>
-        <item>@drawable/ratingbar_small_material</item>
         <item>@drawable/screen_background_dark</item>
         <item>@drawable/screen_background_dark_transparent</item>
         <item>@drawable/screen_background_light</item>
@@ -125,17 +83,9 @@
         <item>@drawable/seekbar_tick_mark_material</item>
         <item>@drawable/seekbar_track_material</item>
         <item>@drawable/spinner_background_material</item>
-        <item>@drawable/spinner_textfield_background_material</item>
-        <item>@drawable/switch_thumb_material_anim</item>
         <item>@drawable/switch_track_material</item>
         <item>@drawable/tab_indicator_material</item>
         <item>@drawable/text_cursor_material</item>
-        <item>@drawable/text_edit_paste_window</item>
-        <item>@drawable/textfield_search_material</item>
-        <item>@drawable/text_select_handle_left_material</item>
-        <item>@drawable/text_select_handle_middle_material</item>
-        <item>@drawable/text_select_handle_right_material</item>
-        <item>@drawable/toast_frame</item>
     </array>
 
     <!-- Do not translate. These are all of the color state list resources that should be
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index e10570f..be1c4b8 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -1884,6 +1884,9 @@
     <!-- Type of the double tap sensor. Empty if double tap is not supported. -->
     <string name="config_dozeDoubleTapSensorType" translatable="false"></string>
 
+    <!-- Type of the long press sensor. Empty if long press is not supported. -->
+    <string name="config_dozeLongPressSensorType" translatable="false"></string>
+
     <!-- Control whether the always on display mode is available. This should only be enabled on
          devices where the display has be tuned to be power efficient in DOZE and/or DOZE_SUSPEND
          states. -->
@@ -1987,6 +1990,10 @@
     <!-- Amount of time in ms the user needs to press the relevant key to bring up the global actions dialog -->
     <integer name="config_globalActionsKeyTimeout">500</integer>
 
+    <!-- Default width of a vertical scrollbar and height of a horizontal scrollbar.
+         Takes effect only if the scrollbar drawables have no intrinsic size. -->
+    <dimen name="config_scrollbarSize">4dp</dimen>
+
     <!-- Distance that should be scrolled, per axis value, in response to a horizontal
          {@link MotionEvent#ACTION_SCROLL} event. -->
     <dimen name="config_horizontalScrollFactor">64dp</dimen>
@@ -2223,11 +2230,19 @@
     <string-array name="config_disabledUntilUsedPreinstalledCarrierApps" translatable="false" />
 
     <!-- The list of classes that should be added to the notification ranking pipline.
-     See {@link com.android.server.notification.NotificationSignalExtractor} -->
+     See {@link com.android.server.notification.NotificationSignalExtractor}
+      If you add a new extractor to this list make sure to update
+      NotificationManagerService.handleRankingSort()-->
     <string-array name="config_notificationSignalExtractors">
+        <!-- many of the following extractors depend on the notification channel, so this
+        extractor must come first -->
+        <item>com.android.server.notification.NotificationChannelExtractor</item>
+        <item>com.android.server.notification.NotificationAdjustmentExtractor</item>
+        <!-- depends on AdjustmentExtractor-->
         <item>com.android.server.notification.ValidateNotificationPeople</item>
         <item>com.android.server.notification.PriorityExtractor</item>
         <item>com.android.server.notification.ImportanceExtractor</item>
+        <!-- depends on ImportanceExtractor-->
         <item>com.android.server.notification.NotificationIntrusivenessExtractor</item>
         <item>com.android.server.notification.VisibilityExtractor</item>
         <item>com.android.server.notification.BadgeExtractor</item>
diff --git a/core/res/res/values/locale_config.xml b/core/res/res/values/locale_config.xml
index ba14843..0ba8b57 100644
--- a/core/res/res/values/locale_config.xml
+++ b/core/res/res/values/locale_config.xml
@@ -463,8 +463,6 @@
         <item>teo-KE</item> <!-- Teso (Kenya) -->
         <item>teo-UG</item> <!-- Teso (Uganda) -->
         <item>th-TH</item> <!-- Thai (Thailand) -->
-        <item>ti-ER</item> <!-- Tigrinya (Eritrea) -->
-        <item>ti-ET</item> <!-- Tigrinya (Ethiopia) -->
         <item>to-TO</item> <!-- Tongan (Tonga) -->
         <item>tr-CY</item> <!-- Turkish (Cyprus) -->
         <item>tr-TR</item> <!-- Turkish (Turkey) -->
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 18e8af7..8c26db4 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -20,23 +20,13 @@
 <resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <!-- Suffix added to a number to signify size in bytes. -->
     <string name="byteShort">B</string>
-    <!-- Suffix added to a number to signify size in kilobytes (1000 bytes).
-        If you retain the Latin script for the localization, please use the lowercase
-        'k', as it signifies 1000 bytes as opposed to 1024 bytes. -->
-    <string name="kilobyteShort">kB</string>
-    <!-- Suffix added to a number to signify size in megabytes. -->
-    <string name="megabyteShort">MB</string>
-    <!-- Suffix added to a number to signify size in gigabytes. -->
-    <string name="gigabyteShort">GB</string>
-    <!-- Suffix added to a number to signify size in terabytes. -->
-    <string name="terabyteShort">TB</string>
     <!-- Suffix added to a number to signify size in petabytes. -->
     <string name="petabyteShort">PB</string>
-    <!-- Format string used to add a suffix like "kB" or "MB" to a number
-         to display a size in kilobytes, megabytes, or other size units.
-         Some languages (like French) will want to add a space between
-         the placeholders. -->
-    <string name="fileSizeSuffix"><xliff:g id="number" example="123">%1$s</xliff:g> <xliff:g id="unit" example="MB">%2$s</xliff:g></string>
+    <!-- Format string used to add a suffix like "B" or "PB" to a number
+         to display a size in bytes or petabytes.
+         Some languages may want to remove the space between the placeholders
+         or replace it with a non-breaking space. -->
+    <string name="fileSizeSuffix"><xliff:g id="number" example="123">%1$s</xliff:g> <xliff:g id="unit" example="B">%2$s</xliff:g></string>
 
     <!-- Used in Contacts for a field that has no label and in Note Pad
          for a note with no name. -->
diff --git a/core/res/res/values/styles_material.xml b/core/res/res/values/styles_material.xml
index c783ea8..470ac52 100644
--- a/core/res/res/values/styles_material.xml
+++ b/core/res/res/values/styles_material.xml
@@ -1054,7 +1054,7 @@
     </style>
 
     <style name="Widget.Material.MediaRouteButton" parent ="Widget.Material.ActionButton">
-        <item name="externalRouteEnabledDrawable">@drawable/ic_media_route_material</item>
+        <item name="externalRouteEnabledDrawable">@drawable/ic_media_route_dark_material</item>
         <item name="contentDescription">@string/media_route_button_content_description</item>
     </style>
 
@@ -1203,7 +1203,10 @@
     <style name="Widget.Material.Light.ActionBar" parent="Widget.Material.ActionBar" />
     <style name="Widget.Material.Light.ActionBar.Solid" parent="Widget.Material.ActionBar.Solid" />
     <style name="Widget.Material.Light.FastScroll" parent="Widget.Material.FastScroll"/>
-    <style name="Widget.Material.Light.MediaRouteButton" parent="Widget.Material.MediaRouteButton" />
+
+    <style name="Widget.Material.Light.MediaRouteButton" parent="Widget.Material.MediaRouteButton">
+        <item name="externalRouteEnabledDrawable">@drawable/ic_media_route_light_material</item>
+    </style>
 
     <!-- Animation Styles -->
 
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index f8b6904..beab29a 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -451,6 +451,7 @@
   <java-symbol type="dimen" name="config_viewConfigurationTouchSlop" />
   <java-symbol type="dimen" name="config_viewMinFlingVelocity" />
   <java-symbol type="dimen" name="config_viewMaxFlingVelocity" />
+  <java-symbol type="dimen" name="config_scrollbarSize" />
   <java-symbol type="dimen" name="config_horizontalScrollFactor" />
   <java-symbol type="dimen" name="config_verticalScrollFactor" />
   <java-symbol type="dimen" name="config_scrollFactor" />
@@ -677,7 +678,6 @@
   <java-symbol type="string" name="fileSizeSuffix" />
   <java-symbol type="string" name="force_close" />
   <java-symbol type="string" name="gadget_host_error_inflating" />
-  <java-symbol type="string" name="gigabyteShort" />
   <java-symbol type="string" name="gpsNotifMessage" />
   <java-symbol type="string" name="gpsNotifTicker" />
   <java-symbol type="string" name="gpsNotifTitle" />
@@ -733,7 +733,6 @@
   <java-symbol type="string" name="keyboardview_keycode_enter" />
   <java-symbol type="string" name="keyboardview_keycode_mode_change" />
   <java-symbol type="string" name="keyboardview_keycode_shift" />
-  <java-symbol type="string" name="kilobyteShort" />
   <java-symbol type="string" name="last_month" />
   <java-symbol type="string" name="launchBrowserDefault" />
   <java-symbol type="string" name="lock_to_app_toast" />
@@ -754,7 +753,6 @@
   <java-symbol type="string" name="lockscreen_emergency_call" />
   <java-symbol type="string" name="lockscreen_return_to_call" />
   <java-symbol type="string" name="low_memory" />
-  <java-symbol type="string" name="megabyteShort" />
   <java-symbol type="string" name="midnight" />
   <java-symbol type="string" name="mismatchPin" />
   <java-symbol type="string" name="mmiComplete" />
@@ -957,7 +955,6 @@
   <java-symbol type="string" name="sync_really_delete" />
   <java-symbol type="string" name="sync_too_many_deletes_desc" />
   <java-symbol type="string" name="sync_undo_deletes" />
-  <java-symbol type="string" name="terabyteShort" />
   <java-symbol type="string" name="text_copied" />
   <java-symbol type="string" name="time_of_day" />
   <java-symbol type="string" name="time_picker_decrement_hour_button" />
@@ -1472,6 +1469,7 @@
   <java-symbol type="xml" name="global_keys" />
   <java-symbol type="xml" name="default_zen_mode_config" />
   <java-symbol type="xml" name="sms_7bit_translation_table" />
+  <java-symbol type="xml" name="color_extraction" />
 
   <java-symbol type="raw" name="color_fade_vert" />
   <java-symbol type="raw" name="color_fade_frag" />
@@ -3034,6 +3032,8 @@
 
   <java-symbol type="array" name="config_hideWhenDisabled_packageNames" />
 
+  <java-symbol type="string" name="config_dozeLongPressSensorType" />
+
   <java-symbol type="array" name="config_allowedGlobalInstantAppSettings" />
   <java-symbol type="array" name="config_allowedSystemInstantAppSettings" />
   <java-symbol type="array" name="config_allowedSecureInstantAppSettings" />
diff --git a/core/res/res/values/themes_material.xml b/core/res/res/values/themes_material.xml
index 86abe97..9dafa7a 100644
--- a/core/res/res/values/themes_material.xml
+++ b/core/res/res/values/themes_material.xml
@@ -214,7 +214,7 @@
         <!-- Scrollbar attributes -->
         <item name="scrollbarFadeDuration">250</item>
         <item name="scrollbarDefaultDelayBeforeFade">400</item>
-        <item name="scrollbarSize">10dp</item>
+        <item name="scrollbarSize">@dimen/config_scrollbarSize</item>
         <item name="scrollbarThumbHorizontal">@drawable/scrollbar_handle_material</item>
         <item name="scrollbarThumbVertical">@drawable/config_scrollbarThumbVertical</item>
         <item name="scrollbarTrackHorizontal">@null</item>
@@ -582,7 +582,7 @@
         <!-- Scrollbar attributes -->
         <item name="scrollbarFadeDuration">250</item>
         <item name="scrollbarDefaultDelayBeforeFade">400</item>
-        <item name="scrollbarSize">10dp</item>
+        <item name="scrollbarSize">@dimen/config_scrollbarSize</item>
         <item name="scrollbarThumbHorizontal">@drawable/scrollbar_handle_material</item>
         <item name="scrollbarThumbVertical">@drawable/config_scrollbarThumbVertical</item>
         <item name="scrollbarTrackHorizontal">@null</item>
diff --git a/core/res/res/xml/color_extraction.xml b/core/res/res/xml/color_extraction.xml
new file mode 100644
index 0000000..7d52b20
--- /dev/null
+++ b/core/res/res/xml/color_extraction.xml
@@ -0,0 +1,348 @@
+<?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
+  -->
+
+<colorextraction>
+    <!-- List of material color palettes in HSL -->
+    <palettes>
+        <!-- Grey scale -->
+        <palette h="0f, 0f, 0f, 0f, 0f, 0f, 0f, 0f, 0f, 0f, 0f, 0f"
+                 s="0f, 0f, 0f, 0f, 0f, 0f, 0f, 0f, 0f, 0f, 0f, 0f"
+                 l="0.08f, 0.11f, 0.14901960784313725f, 0.2f, 0.2980392156862745f, 0.4f,
+                0.4980392156862745f, 0.6196078431372549f, 0.7176470588235294f,
+                0.8196078431372549f, 0.9176470588235294f, 0.9490196078431372f"/>
+        <!-- All colors -->
+        <palette h="1,1,0.991,0.991,0.9833333333333333,0,0,0,
+                0.01134380453752181,0.015625000000000003,0.024193548387096798,
+                0.027397260273972573,0.017543859649122865"
+                 s="1,1,1,1,1,1,1,0.8434782608695652,1,1,1,1,1"
+                 l="0.04,0.09,0.14,0.2,0.27450980392156865,
+                0.34901960784313724,0.4235294117647059,0.5490196078431373,
+                0.6254901960784314,0.6862745098039216,0.7568627450980392,
+                0.8568627450980393,0.9254901960784314"/>
+        <palette h="0.638,0.638,0.6385767790262171,0.6301169590643275,
+                0.6223958333333334,0.6151079136690647,0.6065400843881856,
+                0.5986964618249534,0.5910746812386157,0.5833333333333334,
+                0.5748031496062993,0.5582010582010583"
+                 s="1,1,1,1,0.9014084507042253,0.8128654970760234,
+                0.7979797979797981,0.7816593886462883,0.778723404255319,1,1,1"
+                 l="0.05,0.12,0.17450980392156862,0.2235294117647059,
+                0.2784313725490196,0.3352941176470588,0.388235294117647,
+                0.44901960784313727,0.5392156862745098,0.6509803921568628,
+                0.7509803921568627,0.8764705882352941"/>
+        <palette h="0.563,0.569,0.5666,0.5669934640522876,0.5748031496062993,
+                0.5595238095238095,0.5473118279569893,0.5393258426966292,
+                0.5315955766192734,0.524031007751938,0.5154711673699016,
+                0.508080808080808,0.5"
+                 s="1,1,1,1,1,1,1,1,1,0.8847736625514403,1,1,1"
+                 l="0.07,0.12,0.16,0.2,0.24901960784313726,
+                0.27450980392156865,0.30392156862745096,0.34901960784313724,
+                0.4137254901960784,0.47647058823529415,0.5352941176470588,
+                0.6764705882352942,0.8"/>
+        <palette h="0.508,0.511,0.508,0.508,0.5082304526748972,
+                0.5069444444444444,0.5,0.5,0.5,0.48724954462659376,
+                0.4800347222222222,0.4755134281200632,0.4724409448818897,
+                0.4671052631578947"
+                 s="1,1,1,1,1,0.8888888888888887,0.9242424242424242,1,
+                1,0.8133333333333332,0.7868852459016393,1,1,1"
+                 l="0.04,0.06,0.08,0.12,0.1588235294117647,
+                0.21176470588235297,0.25882352941176473,0.3,0.34901960784313724,
+                0.44117647058823534,0.5215686274509804,0.5862745098039216,
+                0.7509803921568627,0.8509803921568627"/>
+        <palette h="0.333,0.333,0.333,0.3333333333333333,0.3333333333333333,
+                0.34006734006734,0.34006734006734,0.34006734006734,
+                0.34259259259259256,0.3475783475783476,0.34767025089605735,
+                0.3467741935483871,0.3703703703703704"
+                 s="0.70,0.72,0.69,0.6703296703296703,0.728813559322034,
+                0.5657142857142856,0.5076923076923077,0.3944223107569721,
+                0.6206896551724138,0.8931297709923666,1,1,1"
+                 l="0.05,0.08,0.14,0.1784313725490196,0.23137254901960785,
+                0.3431372549019608,0.38235294117647056,0.49215686274509807,
+                0.6588235294117647,0.7431372549019608,0.8176470588235294,
+                0.8784313725490196,0.9294117647058824"/>
+        <palette h="0.161,0.163,0.163,0.162280701754386,0.15032679738562088,
+                0.15879265091863518,0.16236559139784948,0.17443868739205526,
+                0.17824074074074076,0.18674698795180725,0.18692449355432778,
+                0.1946778711484594,0.18604651162790695"
+                 s="1,1,1,1,1,1,1,1,1,1,1,1,1"
+                 l="0.05,0.08,0.11,0.14901960784313725,0.2,
+                0.24901960784313726,0.30392156862745096,0.3784313725490196,
+                0.4235294117647059,0.48823529411764705,0.6450980392156863,
+                0.7666666666666666,0.8313725490196078"/>
+        <palette h="0.108,0.105,0.105,0.105,0.10619469026548674,
+                0.11924686192468618,0.13046448087431692,0.14248366013071895,
+                0.1506024096385542,0.16220238095238093,0.16666666666666666,
+                0.16666666666666666,0.162280701754386,0.15686274509803924"
+                 s="1,1,1,1,1,1,1,1,1,1,1,1,1,1"
+                 l="0.17,0.22,0.28,0.35,0.44313725490196076,
+                0.46862745098039216,0.47843137254901963,0.5,0.5117647058823529,
+                0.5607843137254902,0.6509803921568628,0.7509803921568627,
+                0.8509803921568627,0.9"/>
+        <palette h="0.036,0.036,0.036,0.036,0.03561253561253561,
+                0.05098039215686275,0.07516339869281045,0.09477124183006536,
+                0.1150326797385621,0.134640522875817,0.14640522875816991,
+                0.1582397003745319,0.15773809523809523,0.15359477124183002"
+                 s="1,1,1,1,1,1,1,1,1,1,1,1,1,1"
+                 l="0.19,0.26,0.34,0.39,0.4588235294117647,0.5,0.5,0.5,
+                0.5,0.5,0.5,0.6509803921568628,0.7803921568627451,0.9"/>
+        <palette h="0.955,0.961,0.958,0.9596491228070175,0.9593837535014005,
+                0.9514767932489452,0.943859649122807,0.9396825396825397,
+                0.9395424836601307,0.9393939393939394,0.9362745098039216,
+                0.9754098360655739,0.9824561403508771"
+                 s="0.87,0.85,0.85,0.84070796460177,0.8206896551724138,
+                0.7979797979797981,0.7661290322580644,0.9051724137931036,
+                1,1,1,1,1"
+                 l="0.06,0.11,0.16,0.22156862745098038,0.2843137254901961,
+                0.388235294117647,0.48627450980392156,0.5450980392156863,
+                0.6,0.6764705882352942,0.8,0.8803921568627451,
+                0.9254901960784314"/>
+        <palette h="0.866,0.855,0.841025641025641,0.8333333333333334,
+                0.8285256410256411,0.821522309711286,0.8083333333333333,
+                0.8046594982078853,0.8005822416302766,0.7842377260981912,
+                0.7771084337349398,0.7747747747747749"
+                 s="1,1,1,1,1,1,1,1,1,
+                0.737142857142857,0.6434108527131781,0.46835443037974644"
+                 l="0.05,0.08,0.12745098039215685,0.15490196078431373,
+                0.20392156862745098,0.24901960784313726,0.3137254901960784,
+                0.36470588235294116,0.44901960784313727,0.6568627450980392,
+                0.7470588235294118,0.8450980392156863"/>
+        <palette h="0.925,0.93,0.938,0.947,0.955952380952381,
+                0.9681069958847737,0.9760479041916167,0.9873563218390804,0,0,
+                0.009057971014492771,0.026748971193415648,
+                0.041666666666666616,0.05303030303030304"
+                 s="1,1,1,1,1,0.8350515463917526,0.6929460580912863,
+                0.6387665198237885,0.6914893617021276,0.7583892617449666,
+                0.8070175438596495,0.9310344827586209,1,1"
+                 l="0.10,0.13,0.17,0.2,0.27450980392156865,
+                0.3803921568627451,0.4725490196078432,0.5549019607843138,
+                0.6313725490196078,0.707843137254902,0.7764705882352941,
+                0.8294117647058823,0.9058823529411765,0.9568627450980391"/>
+        <palette h="0.733,0.736,0.744,0.7514619883040936,0.7679738562091503,
+                0.7802083333333333,0.7844311377245509,0.796875,
+                0.8165618448637316,0.8487179487179487,0.8582375478927203,
+                0.8562091503267975,0.8666666666666667"
+                 s="1,1,1,1,1,0.8163265306122449,0.6653386454183268,
+                0.7547169811320753,0.929824561403509,0.9558823529411766,
+                0.9560439560439562,1,1"
+                 l="0.07,0.12,0.17,0.2235294117647059,0.3,
+                0.38431372549019605,0.492156862745098,0.5843137254901961,
+                0.6647058823529411,0.7333333333333334,0.8215686274509804,0.9,
+                0.9411764705882353"/>
+        <palette h="0.6666666666666666,0.6666666666666666,0.6666666666666666,
+                0.6666666666666666,0.6666666666666666,0.6666666666666666,
+                0.6666666666666666,0.6666666666666666,0.6666666666666666,
+                0.6666666666666666,0.6666666666666666"
+                 s="0.25,0.24590163934426232,0.17880794701986752,
+                0.14606741573033713,0.13761467889908252,0.14893617021276592,
+                0.16756756756756758,0.20312500000000017,0.26086956521739135,
+                0.29999999999999966,0.5000000000000004"
+                 l="0.18,0.2392156862745098,0.296078431372549,
+                0.34901960784313724,0.4274509803921569,0.5392156862745098,
+                0.6372549019607843,0.7490196078431373,0.8196078431372549,
+                0.8823529411764706,0.9372549019607843"/>
+        <palette h="0.938,0.944,0.952,0.961,0.9678571428571429,
+                0.9944812362030905,0,0,
+                0.0047348484848484815,0.00316455696202532,0,
+                0.9980392156862745,0.9814814814814816,0.9722222222222221"
+                 s="1,1,1,1,1,0.7023255813953488,0.6638655462184874,
+                0.6521739130434782,0.7719298245614035,0.8315789473684211,
+                0.6867469879518071,0.7264957264957265,0.8181818181818182,
+                0.8181818181818189"
+                 l="0.08,0.13,0.18,0.23,0.27450980392156865,
+                0.4215686274509804,
+                0.4666666666666667,0.503921568627451,0.5529411764705883,
+                0.6274509803921569,0.6745098039215687,0.7705882352941176,
+                0.892156862745098,0.9568627450980391"/>
+        <palette h="0.88,0.888,0.897,0.9052287581699346,0.9112021857923498,
+                0.9270152505446624,0.9343137254901961,0.9391534391534391,
+                0.9437984496124031,0.943661971830986,0.9438943894389439,
+                0.9426229508196722,0.9444444444444444"
+                 s="1,1,1,1,0.8133333333333332,0.7927461139896375,
+                0.7798165137614679,0.7777777777777779,0.8190476190476191,
+                0.8255813953488372,0.8211382113821142,0.8133333333333336,
+                0.8000000000000006"
+                 l="0.08,0.12,0.16,0.2,0.29411764705882354,
+                0.3784313725490196,0.42745098039215684,0.4764705882352941,
+                0.5882352941176471,0.6627450980392157,0.7588235294117647,
+                0.8529411764705882,0.9411764705882353"/>
+        <palette h="0.669,0.680,0.6884057971014492,0.6974789915966387,
+                0.7079889807162534,0.7154471544715447,0.7217741935483872,
+                0.7274143302180687,0.7272727272727273,0.7258064516129031,
+                0.7252252252252251,0.7333333333333333"
+                 s="0.81,0.81,0.8214285714285715,0.6878612716763006,
+                0.6080402010050251,0.5774647887323943,0.5391304347826086,
+                0.46724890829694316,0.4680851063829788,0.462686567164179,
+                0.45679012345678977,0.4545454545454551"
+                 l="0.12,0.16,0.2196078431372549,0.33921568627450976,
+                0.39019607843137255,0.4176470588235294,0.45098039215686275,
+                0.5509803921568628,0.6313725490196078,0.7372549019607844,
+                0.8411764705882353,0.9352941176470588"/>
+        <palette h="0.6470588235294118,0.6516666666666667,0.6464174454828661,
+                0.6441441441441442,0.6432748538011696,0.6416666666666667,
+                0.6402439024390243,0.6412429378531074,0.6435185185185186,
+                0.6428571428571429"
+                 s="0.8095238095238095,0.6578947368421053,0.5721925133689839,
+                0.5362318840579711,0.5,0.4424778761061947,0.44086021505376327,
+                0.44360902255639095,0.4499999999999997,0.4375000000000006"
+                 l="0.16470588235294117,0.2980392156862745,0.36666666666666664,
+                0.40588235294117647,0.44705882352941173,
+                0.5568627450980392,0.6352941176470588,0.7392156862745098,
+                0.8431372549019608,0.9372549019607843"/>
+        <palette h="0.469,0.46732026143790845,0.4718614718614719,
+                0.4793650793650794,0.48071625344352614,0.4829683698296837,
+                0.484375,0.4841269841269842,0.48444444444444457,
+                0.48518518518518516,0.4907407407407408"
+                 s="1,1,1,1,1,1,0.6274509803921569,0.41832669322709176,
+                0.41899441340782106,0.4128440366972478,0.4090909090909088"
+                 l="0.07,0.1,0.15098039215686274,0.20588235294117646,
+                0.2372549019607843,0.26862745098039215,0.4,0.5078431372549019,
+                0.6490196078431372,0.7862745098039216,0.9137254901960784"/>
+        <palette h="0.542,0.5444444444444444,0.5555555555555556,
+                0.5555555555555556,0.553763440860215,0.5526315789473684,
+                0.5555555555555556,0.5555555555555555,0.5555555555555556,
+                0.5512820512820514,0.5666666666666667"
+                 s="0.25,0.24590163934426232,0.19148936170212766,
+                0.1791044776119403,0.18343195266272191,0.18446601941747576,
+                0.1538461538461539,0.15625000000000003,0.15328467153284678,
+                0.15662650602409653,0.151515151515151"
+                 l="0.05,0.1196078431372549,0.1843137254901961,
+                0.2627450980392157,
+                0.33137254901960783,0.403921568627451,0.5411764705882354,
+                0.6235294117647059,0.7313725490196079,0.8372549019607843,
+                0.9352941176470588"/>
+        <palette h="0.022222222222222223,0.02469135802469136,0.031249999999999997,
+                0.03947368421052631,0.04166666666666668,
+                0.043650793650793655,0.04411764705882352,0.04166666666666652,
+                0.04444444444444459,0.05555555555555529"
+                 s="0.33333333333333337,0.2783505154639175,0.2580645161290323,
+                0.25675675675675674,0.2528735632183908,0.17500000000000002,
+                0.15315315315315312,0.15189873417721522,
+                0.15789473684210534,0.15789473684210542"
+                 l="0.08823529411764705,0.19019607843137254,0.2431372549019608,
+                0.2901960784313725,0.3411764705882353,0.47058823529411764,
+                0.5647058823529412,0.6901960784313725,0.8137254901960784,
+                0.9254901960784314"/>
+        <palette h="0.027,0.03,0.038,0.044,0.050884955752212385,
+                0.07254901960784313,0.0934640522875817,
+                0.10457516339869281,0.11699346405228758,
+                0.1255813953488372,0.1268939393939394,0.12533333333333332,
+                0.12500000000000003,0.12777777777777777"
+                 s="1,1,1,1,1,1,1,1,1,1,1,1,1,1"
+                 l="0.25,0.3,0.35,0.4,0.44313725490196076,0.5,0.5,0.5,
+                0.5,0.5784313725490196,
+                0.6549019607843137,0.7549019607843137,0.8509803921568627,
+                0.9411764705882353"/>
+    </palettes>
+    <blacklist>
+        <!-- Red -->
+        <range h="0, 20"
+               s="0.7, 1"
+               l="0.21, 0.79"/>
+        <range h="0, 20"
+               s="0.3, 0.7"
+               l="0.355, 0.653"/>
+        <!-- Red Orange -->
+        <range h="20, 40"
+               s="0.7, 1"
+               l="0.28, 0.643"/>
+        <range h="20, 40"
+               s="0.3, 0.7"
+               l="0.414, 0.561"/>
+        <range h="20, 40"
+               s="0, 3"
+               l="0.343, 0.584"/>
+        <!-- Orange -->
+        <range h="40, 60"
+               s="0.7, 1"
+               l="0.173, 0.349"/>
+        <range h="40, 60"
+               s="0.3, 0.7"
+               l="0.233, 0.427"/>
+        <range h="40, 60"
+               s="0, 0.3"
+               l="0.231, 0.484"/>
+        <!-- Yellow 60 -->
+        <range h="60, 80"
+               s="0.7, 1"
+               l="0.488, 0.737"/>
+        <range h="60, 80"
+               s="0.3, 0.7"
+               l="0.673, 0.837"/>
+        <!-- Yellow Green 80 -->
+        <range h="80, 100"
+               s="0.7, 1"
+               l="0.469, 0.61"/>
+        <!-- Yellow green 100 -->
+        <range h="100, 120"
+               s="0.7, 1"
+               l="0.388, 0.612"/>
+        <range h="100, 120"
+               s="0.3, 0.7"
+               l="0.424, 0.541"/>
+        <!-- Green -->
+        <range h="120, 140"
+               s="0.7, 1"
+               l="0.375, 0.52"/>
+        <range h="120, 140"
+               s="0.3, 0.7"
+               l="0.435, 0.524"/>
+        <!-- Green Blue 140 -->
+        <range h="140, 160"
+               s="0.7, 1"
+               l="0.496, 0.641"/>
+        <!-- Seaoam -->
+        <range h="160, 180"
+               s="0.7, 1"
+               l="0.496, 0.567"/>
+        <!-- Cyan -->
+        <range h="180, 200"
+               s="0.7, 1"
+               l="0.52, 0.729"/>
+        <!-- Blue -->
+        <range h="220, 240"
+               s="0.7, 1"
+               l="0.396, 0.571"/>
+        <range h="220, 240"
+               s="0.3, 0.7"
+               l="0.425, 0.551"/>
+        <!-- Blue Purple 240 -->
+        <range h="240, 260"
+               s="0.7, 1"
+               l="0.418, 0.639"/>
+        <range h="220, 240"
+               s="0.3, 0.7"
+               l="0.441, 0.576"/>
+        <!-- Blue Purple 260 -->
+        <range h="260, 280"
+               s="0.3, 1"
+               l="0.461, 0.553"/>
+        <!-- Fuchsia -->
+        <range h="300, 320"
+               s="0.7, 1"
+               l="0.484, 0.588"/>
+        <range h="300, 320"
+               s="0.3, 0.7"
+               l="0.48, 0.592"/>
+        <!-- Pink -->
+        <range h="320, 340"
+               s="0.7, 1"
+               l="0.466, 0.629"/>
+        <!-- Soft red -->
+        <range h="340, 360"
+               s="0.7, 1"
+               l="0.437, 0.596"/>
+    </blacklist>
+</colorextraction>
\ No newline at end of file
diff --git a/core/tests/coretests/README b/core/tests/coretests/README
index aced441..ea282a0 100644
--- a/core/tests/coretests/README
+++ b/core/tests/coretests/README
@@ -28,7 +28,7 @@
 
 Next, install the resulting APK and run tests as you would normal JUnit tests:
 
-  adb install out/target/product/.../data/app/FrameworksCoreTests/FrameworksCoreTests.apk
+  adb install -r ${ANDROID_PRODUCT_OUT}/data/app/FrameworksCoreTests/FrameworksCoreTests.apk
   adb shell am instrument -w \
     com.android.frameworks.coretests/android.support.test.runner.AndroidJUnitRunner
 
diff --git a/core/tests/coretests/assets/fonts/a3em.ttf b/core/tests/coretests/assets/fonts/a3em.ttf
new file mode 100644
index 0000000..a601ce2
--- /dev/null
+++ b/core/tests/coretests/assets/fonts/a3em.ttf
Binary files differ
diff --git a/core/tests/coretests/assets/fonts/a3em.ttx b/core/tests/coretests/assets/fonts/a3em.ttx
new file mode 100644
index 0000000..d3b9e16
--- /dev/null
+++ b/core/tests/coretests/assets/fonts/a3em.ttx
@@ -0,0 +1,187 @@
+<?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.
+-->
+<ttFont sfntVersion="\x00\x01\x00\x00" ttLibVersion="3.0">
+
+  <GlyphOrder>
+    <GlyphID id="0" name=".notdef"/>
+    <GlyphID id="1" name="1em"/>
+    <GlyphID id="2" name="3em"/>
+  </GlyphOrder>
+
+  <head>
+    <tableVersion value="1.0"/>
+    <fontRevision value="1.0"/>
+    <checkSumAdjustment value="0x640cdb2f"/>
+    <magicNumber value="0x5f0f3cf5"/>
+    <flags value="00000000 00000011"/>
+    <unitsPerEm value="1000"/>
+    <created value="Fri Mar 17 07:26:00 2017"/>
+    <macStyle value="00000000 00000000"/>
+    <lowestRecPPEM value="7"/>
+    <fontDirectionHint value="2"/>
+    <glyphDataFormat value="0"/>
+  </head>
+
+  <hhea>
+    <tableVersion value="1.0"/>
+    <ascent value="1000"/>
+    <descent value="-200"/>
+    <lineGap value="0"/>
+    <caretSlopeRise value="1"/>
+    <caretSlopeRun value="0"/>
+    <caretOffset value="0"/>
+    <reserved0 value="0"/>
+    <reserved1 value="0"/>
+    <reserved2 value="0"/>
+    <reserved3 value="0"/>
+    <metricDataFormat value="0"/>
+  </hhea>
+
+  <maxp>
+    <tableVersion value="0x10000"/>
+    <maxZones value="0"/>
+    <maxTwilightPoints value="0"/>
+    <maxStorage value="0"/>
+    <maxFunctionDefs value="0"/>
+    <maxInstructionDefs value="0"/>
+    <maxStackElements value="0"/>
+    <maxSizeOfInstructions value="0"/>
+    <maxComponentElements value="0"/>
+  </maxp>
+
+  <OS_2>
+    <!-- The fields 'usFirstCharIndex' and 'usLastCharIndex'
+         will be recalculated by the compiler -->
+    <version value="3"/>
+    <xAvgCharWidth value="594"/>
+    <usWeightClass value="400"/>
+    <usWidthClass value="5"/>
+    <fsType value="00000000 00001000"/>
+    <ySubscriptXSize value="650"/>
+    <ySubscriptYSize value="600"/>
+    <ySubscriptXOffset value="0"/>
+    <ySubscriptYOffset value="75"/>
+    <ySuperscriptXSize value="650"/>
+    <ySuperscriptYSize value="600"/>
+    <ySuperscriptXOffset value="0"/>
+    <ySuperscriptYOffset value="350"/>
+    <yStrikeoutSize value="50"/>
+    <yStrikeoutPosition value="300"/>
+    <sFamilyClass value="0"/>
+    <panose>
+      <bFamilyType value="0"/>
+      <bSerifStyle value="0"/>
+      <bWeight value="5"/>
+      <bProportion value="0"/>
+      <bContrast value="0"/>
+      <bStrokeVariation value="0"/>
+      <bArmStyle value="0"/>
+      <bLetterForm value="0"/>
+      <bMidline value="0"/>
+      <bXHeight value="0"/>
+    </panose>
+    <ulUnicodeRange1 value="00000000 00000000 00000000 00000001"/>
+    <ulUnicodeRange2 value="00000000 00000000 00000000 00000000"/>
+    <ulUnicodeRange3 value="00000000 00000000 00000000 00000000"/>
+    <ulUnicodeRange4 value="00000000 00000000 00000000 00000000"/>
+    <achVendID value="UKWN"/>
+    <fsSelection value="00000000 01000000"/>
+    <usFirstCharIndex value="32"/>
+    <usLastCharIndex value="122"/>
+    <sTypoAscender value="800"/>
+    <sTypoDescender value="-200"/>
+    <sTypoLineGap value="200"/>
+    <usWinAscent value="1000"/>
+    <usWinDescent value="200"/>
+    <ulCodePageRange1 value="00000000 00000000 00000000 00000001"/>
+    <ulCodePageRange2 value="00000000 00000000 00000000 00000000"/>
+    <sxHeight value="500"/>
+    <sCapHeight value="700"/>
+    <usDefaultChar value="0"/>
+    <usBreakChar value="32"/>
+    <usMaxContext value="0"/>
+  </OS_2>
+
+  <hmtx>
+    <mtx name=".notdef" width="500" lsb="93"/>
+    <mtx name="1em" width="1000" lsb="93"/>
+    <mtx name="3em" width="3000" lsb="93"/>
+  </hmtx>
+
+  <cmap>
+    <tableVersion version="0"/>
+    <cmap_format_4 platformID="3" platEncID="10" language="0">
+      <map code="0x0061" name="3em" />
+      <map code="0x0062" name="1em" />
+      <map code="0x0063" name="1em" />
+      <map code="0x0064" name="1em" />
+      <map code="0x0065" name="1em" />
+    </cmap_format_4>
+  </cmap>
+
+  <loca>
+    <!-- The 'loca' table will be calculated by the compiler -->
+  </loca>
+
+  <glyf>
+    <TTGlyph name=".notdef" xMin="0" yMin="0" xMax="0" yMax="0" />
+    <TTGlyph name="1em" xMin="0" yMin="0" xMax="0" yMax="0" />
+    <TTGlyph name="3em" xMin="0" yMin="0" xMax="0" yMax="0" />
+  </glyf>
+
+  <name>
+    <namerecord nameID="0" platformID="3" platEncID="1" langID="0x409">
+      Copyright (C) 2017 The Android Open Source Project
+    </namerecord>
+    <namerecord nameID="1" platformID="3" platEncID="1" langID="0x409">
+      Sample Font
+    </namerecord>
+    <namerecord nameID="2" platformID="3" platEncID="1" langID="0x409">
+      Regular
+    </namerecord>
+    <namerecord nameID="4" platformID="3" platEncID="1" langID="0x409">
+      Sample Font
+    </namerecord>
+    <namerecord nameID="6" platformID="3" platEncID="1" langID="0x409">
+      SampleFont-Regular
+    </namerecord>
+    <namerecord nameID="13" platformID="3" platEncID="1" langID="0x409">
+      Licensed under the Apache License, Version 2.0 (the "License");
+      you may not use this file except in compliance with the License.
+      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.
+    </namerecord>
+    <namerecord nameID="14" platformID="3" platEncID="1" langID="0x409">
+      http://www.apache.org/licenses/LICENSE-2.0
+    </namerecord>
+  </name>
+
+  <post>
+    <formatType value="3.0"/>
+    <italicAngle value="0.0"/>
+    <underlinePosition value="-75"/>
+    <underlineThickness value="50"/>
+    <isFixedPitch value="0"/>
+    <minMemType42 value="0"/>
+    <maxMemType42 value="0"/>
+    <minMemType1 value="0"/>
+    <maxMemType1 value="0"/>
+  </post>
+
+</ttFont>
diff --git a/core/tests/coretests/assets/fonts/all2em.ttf b/core/tests/coretests/assets/fonts/all2em.ttf
new file mode 100644
index 0000000..482f755
--- /dev/null
+++ b/core/tests/coretests/assets/fonts/all2em.ttf
Binary files differ
diff --git a/core/tests/coretests/assets/fonts/all2em.ttx b/core/tests/coretests/assets/fonts/all2em.ttx
new file mode 100644
index 0000000..fe95ff0
--- /dev/null
+++ b/core/tests/coretests/assets/fonts/all2em.ttx
@@ -0,0 +1,184 @@
+<?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.
+-->
+<ttFont sfntVersion="\x00\x01\x00\x00" ttLibVersion="3.0">
+
+  <GlyphOrder>
+    <GlyphID id="0" name=".notdef"/>
+    <GlyphID id="1" name="2em"/>
+  </GlyphOrder>
+
+  <head>
+    <tableVersion value="1.0"/>
+    <fontRevision value="1.0"/>
+    <checkSumAdjustment value="0x640cdb2f"/>
+    <magicNumber value="0x5f0f3cf5"/>
+    <flags value="00000000 00000011"/>
+    <unitsPerEm value="1000"/>
+    <created value="Fri Mar 17 07:26:00 2017"/>
+    <macStyle value="00000000 00000000"/>
+    <lowestRecPPEM value="7"/>
+    <fontDirectionHint value="2"/>
+    <glyphDataFormat value="0"/>
+  </head>
+
+  <hhea>
+    <tableVersion value="1.0"/>
+    <ascent value="1000"/>
+    <descent value="-200"/>
+    <lineGap value="0"/>
+    <caretSlopeRise value="1"/>
+    <caretSlopeRun value="0"/>
+    <caretOffset value="0"/>
+    <reserved0 value="0"/>
+    <reserved1 value="0"/>
+    <reserved2 value="0"/>
+    <reserved3 value="0"/>
+    <metricDataFormat value="0"/>
+  </hhea>
+
+  <maxp>
+    <tableVersion value="0x10000"/>
+    <maxZones value="0"/>
+    <maxTwilightPoints value="0"/>
+    <maxStorage value="0"/>
+    <maxFunctionDefs value="0"/>
+    <maxInstructionDefs value="0"/>
+    <maxStackElements value="0"/>
+    <maxSizeOfInstructions value="0"/>
+    <maxComponentElements value="0"/>
+  </maxp>
+
+  <OS_2>
+    <!-- The fields 'usFirstCharIndex' and 'usLastCharIndex'
+         will be recalculated by the compiler -->
+    <version value="3"/>
+    <xAvgCharWidth value="594"/>
+    <usWeightClass value="400"/>
+    <usWidthClass value="5"/>
+    <fsType value="00000000 00001000"/>
+    <ySubscriptXSize value="650"/>
+    <ySubscriptYSize value="600"/>
+    <ySubscriptXOffset value="0"/>
+    <ySubscriptYOffset value="75"/>
+    <ySuperscriptXSize value="650"/>
+    <ySuperscriptYSize value="600"/>
+    <ySuperscriptXOffset value="0"/>
+    <ySuperscriptYOffset value="350"/>
+    <yStrikeoutSize value="50"/>
+    <yStrikeoutPosition value="300"/>
+    <sFamilyClass value="0"/>
+    <panose>
+      <bFamilyType value="0"/>
+      <bSerifStyle value="0"/>
+      <bWeight value="5"/>
+      <bProportion value="0"/>
+      <bContrast value="0"/>
+      <bStrokeVariation value="0"/>
+      <bArmStyle value="0"/>
+      <bLetterForm value="0"/>
+      <bMidline value="0"/>
+      <bXHeight value="0"/>
+    </panose>
+    <ulUnicodeRange1 value="00000000 00000000 00000000 00000001"/>
+    <ulUnicodeRange2 value="00000000 00000000 00000000 00000000"/>
+    <ulUnicodeRange3 value="00000000 00000000 00000000 00000000"/>
+    <ulUnicodeRange4 value="00000000 00000000 00000000 00000000"/>
+    <achVendID value="UKWN"/>
+    <fsSelection value="00000000 01000000"/>
+    <usFirstCharIndex value="32"/>
+    <usLastCharIndex value="122"/>
+    <sTypoAscender value="800"/>
+    <sTypoDescender value="-200"/>
+    <sTypoLineGap value="200"/>
+    <usWinAscent value="1000"/>
+    <usWinDescent value="200"/>
+    <ulCodePageRange1 value="00000000 00000000 00000000 00000001"/>
+    <ulCodePageRange2 value="00000000 00000000 00000000 00000000"/>
+    <sxHeight value="500"/>
+    <sCapHeight value="700"/>
+    <usDefaultChar value="0"/>
+    <usBreakChar value="32"/>
+    <usMaxContext value="0"/>
+  </OS_2>
+
+  <hmtx>
+    <mtx name=".notdef" width="500" lsb="93"/>
+    <mtx name="2em" width="1000" lsb="93"/>
+  </hmtx>
+
+  <cmap>
+    <tableVersion version="0"/>
+    <cmap_format_4 platformID="3" platEncID="10" language="0">
+      <map code="0x0061" name="2em" />
+      <map code="0x0062" name="2em" />
+      <map code="0x0063" name="2em" />
+      <map code="0x0064" name="2em" />
+      <map code="0x0065" name="2em" />
+    </cmap_format_4>
+  </cmap>
+
+  <loca>
+    <!-- The 'loca' table will be calculated by the compiler -->
+  </loca>
+
+  <glyf>
+    <TTGlyph name=".notdef" xMin="0" yMin="0" xMax="0" yMax="0" />
+    <TTGlyph name="2em" xMin="0" yMin="0" xMax="0" yMax="0" />
+  </glyf>
+
+  <name>
+    <namerecord nameID="0" platformID="3" platEncID="1" langID="0x409">
+      Copyright (C) 2017 The Android Open Source Project
+    </namerecord>
+    <namerecord nameID="1" platformID="3" platEncID="1" langID="0x409">
+      Sample Font
+    </namerecord>
+    <namerecord nameID="2" platformID="3" platEncID="1" langID="0x409">
+      Regular
+    </namerecord>
+    <namerecord nameID="4" platformID="3" platEncID="1" langID="0x409">
+      Sample Font
+    </namerecord>
+    <namerecord nameID="6" platformID="3" platEncID="1" langID="0x409">
+      SampleFont-Regular
+    </namerecord>
+    <namerecord nameID="13" platformID="3" platEncID="1" langID="0x409">
+      Licensed under the Apache License, Version 2.0 (the "License");
+      you may not use this file except in compliance with the License.
+      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.
+    </namerecord>
+    <namerecord nameID="14" platformID="3" platEncID="1" langID="0x409">
+      http://www.apache.org/licenses/LICENSE-2.0
+    </namerecord>
+  </name>
+
+  <post>
+    <formatType value="3.0"/>
+    <italicAngle value="0.0"/>
+    <underlinePosition value="-75"/>
+    <underlineThickness value="50"/>
+    <isFixedPitch value="0"/>
+    <minMemType42 value="0"/>
+    <maxMemType42 value="0"/>
+    <minMemType1 value="0"/>
+    <maxMemType1 value="0"/>
+  </post>
+
+</ttFont>
diff --git a/core/tests/coretests/assets/fonts/b3em.ttf b/core/tests/coretests/assets/fonts/b3em.ttf
new file mode 100644
index 0000000..63948a2
--- /dev/null
+++ b/core/tests/coretests/assets/fonts/b3em.ttf
Binary files differ
diff --git a/core/tests/coretests/assets/fonts/b3em.ttx b/core/tests/coretests/assets/fonts/b3em.ttx
new file mode 100644
index 0000000..b5a77ef
--- /dev/null
+++ b/core/tests/coretests/assets/fonts/b3em.ttx
@@ -0,0 +1,187 @@
+<?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.
+-->
+<ttFont sfntVersion="\x00\x01\x00\x00" ttLibVersion="3.0">
+
+  <GlyphOrder>
+    <GlyphID id="0" name=".notdef"/>
+    <GlyphID id="1" name="1em"/>
+    <GlyphID id="2" name="3em"/>
+  </GlyphOrder>
+
+  <head>
+    <tableVersion value="1.0"/>
+    <fontRevision value="1.0"/>
+    <checkSumAdjustment value="0x640cdb2f"/>
+    <magicNumber value="0x5f0f3cf5"/>
+    <flags value="00000000 00000011"/>
+    <unitsPerEm value="1000"/>
+    <created value="Fri Mar 17 07:26:00 2017"/>
+    <macStyle value="00000000 00000000"/>
+    <lowestRecPPEM value="7"/>
+    <fontDirectionHint value="2"/>
+    <glyphDataFormat value="0"/>
+  </head>
+
+  <hhea>
+    <tableVersion value="1.0"/>
+    <ascent value="1000"/>
+    <descent value="-200"/>
+    <lineGap value="0"/>
+    <caretSlopeRise value="1"/>
+    <caretSlopeRun value="0"/>
+    <caretOffset value="0"/>
+    <reserved0 value="0"/>
+    <reserved1 value="0"/>
+    <reserved2 value="0"/>
+    <reserved3 value="0"/>
+    <metricDataFormat value="0"/>
+  </hhea>
+
+  <maxp>
+    <tableVersion value="0x10000"/>
+    <maxZones value="0"/>
+    <maxTwilightPoints value="0"/>
+    <maxStorage value="0"/>
+    <maxFunctionDefs value="0"/>
+    <maxInstructionDefs value="0"/>
+    <maxStackElements value="0"/>
+    <maxSizeOfInstructions value="0"/>
+    <maxComponentElements value="0"/>
+  </maxp>
+
+  <OS_2>
+    <!-- The fields 'usFirstCharIndex' and 'usLastCharIndex'
+         will be recalculated by the compiler -->
+    <version value="3"/>
+    <xAvgCharWidth value="594"/>
+    <usWeightClass value="400"/>
+    <usWidthClass value="5"/>
+    <fsType value="00000000 00001000"/>
+    <ySubscriptXSize value="650"/>
+    <ySubscriptYSize value="600"/>
+    <ySubscriptXOffset value="0"/>
+    <ySubscriptYOffset value="75"/>
+    <ySuperscriptXSize value="650"/>
+    <ySuperscriptYSize value="600"/>
+    <ySuperscriptXOffset value="0"/>
+    <ySuperscriptYOffset value="350"/>
+    <yStrikeoutSize value="50"/>
+    <yStrikeoutPosition value="300"/>
+    <sFamilyClass value="0"/>
+    <panose>
+      <bFamilyType value="0"/>
+      <bSerifStyle value="0"/>
+      <bWeight value="5"/>
+      <bProportion value="0"/>
+      <bContrast value="0"/>
+      <bStrokeVariation value="0"/>
+      <bArmStyle value="0"/>
+      <bLetterForm value="0"/>
+      <bMidline value="0"/>
+      <bXHeight value="0"/>
+    </panose>
+    <ulUnicodeRange1 value="00000000 00000000 00000000 00000001"/>
+    <ulUnicodeRange2 value="00000000 00000000 00000000 00000000"/>
+    <ulUnicodeRange3 value="00000000 00000000 00000000 00000000"/>
+    <ulUnicodeRange4 value="00000000 00000000 00000000 00000000"/>
+    <achVendID value="UKWN"/>
+    <fsSelection value="00000000 01000000"/>
+    <usFirstCharIndex value="32"/>
+    <usLastCharIndex value="122"/>
+    <sTypoAscender value="800"/>
+    <sTypoDescender value="-200"/>
+    <sTypoLineGap value="200"/>
+    <usWinAscent value="1000"/>
+    <usWinDescent value="200"/>
+    <ulCodePageRange1 value="00000000 00000000 00000000 00000001"/>
+    <ulCodePageRange2 value="00000000 00000000 00000000 00000000"/>
+    <sxHeight value="500"/>
+    <sCapHeight value="700"/>
+    <usDefaultChar value="0"/>
+    <usBreakChar value="32"/>
+    <usMaxContext value="0"/>
+  </OS_2>
+
+  <hmtx>
+    <mtx name=".notdef" width="500" lsb="93"/>
+    <mtx name="1em" width="1000" lsb="93"/>
+    <mtx name="3em" width="3000" lsb="93"/>
+  </hmtx>
+
+  <cmap>
+    <tableVersion version="0"/>
+    <cmap_format_4 platformID="3" platEncID="10" language="0">
+      <map code="0x0061" name="1em" />
+      <map code="0x0062" name="3em" />
+      <map code="0x0063" name="1em" />
+      <map code="0x0064" name="1em" />
+      <map code="0x0065" name="1em" />
+    </cmap_format_4>
+  </cmap>
+
+  <loca>
+    <!-- The 'loca' table will be calculated by the compiler -->
+  </loca>
+
+  <glyf>
+    <TTGlyph name=".notdef" xMin="0" yMin="0" xMax="0" yMax="0" />
+    <TTGlyph name="1em" xMin="0" yMin="0" xMax="0" yMax="0" />
+    <TTGlyph name="3em" xMin="0" yMin="0" xMax="0" yMax="0" />
+  </glyf>
+
+  <name>
+    <namerecord nameID="0" platformID="3" platEncID="1" langID="0x409">
+      Copyright (C) 2017 The Android Open Source Project
+    </namerecord>
+    <namerecord nameID="1" platformID="3" platEncID="1" langID="0x409">
+      Sample Font
+    </namerecord>
+    <namerecord nameID="2" platformID="3" platEncID="1" langID="0x409">
+      Regular
+    </namerecord>
+    <namerecord nameID="4" platformID="3" platEncID="1" langID="0x409">
+      Sample Font
+    </namerecord>
+    <namerecord nameID="6" platformID="3" platEncID="1" langID="0x409">
+      SampleFont-Regular
+    </namerecord>
+    <namerecord nameID="13" platformID="3" platEncID="1" langID="0x409">
+      Licensed under the Apache License, Version 2.0 (the "License");
+      you may not use this file except in compliance with the License.
+      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.
+    </namerecord>
+    <namerecord nameID="14" platformID="3" platEncID="1" langID="0x409">
+      http://www.apache.org/licenses/LICENSE-2.0
+    </namerecord>
+  </name>
+
+  <post>
+    <formatType value="3.0"/>
+    <italicAngle value="0.0"/>
+    <underlinePosition value="-75"/>
+    <underlineThickness value="50"/>
+    <isFixedPitch value="0"/>
+    <minMemType42 value="0"/>
+    <maxMemType42 value="0"/>
+    <minMemType1 value="0"/>
+    <maxMemType1 value="0"/>
+  </post>
+
+</ttFont>
diff --git a/core/tests/coretests/assets/fonts/c3em.ttf b/core/tests/coretests/assets/fonts/c3em.ttf
new file mode 100644
index 0000000..badc3e2
--- /dev/null
+++ b/core/tests/coretests/assets/fonts/c3em.ttf
Binary files differ
diff --git a/core/tests/coretests/assets/fonts/c3em.ttx b/core/tests/coretests/assets/fonts/c3em.ttx
new file mode 100644
index 0000000..f5ed8e5
--- /dev/null
+++ b/core/tests/coretests/assets/fonts/c3em.ttx
@@ -0,0 +1,187 @@
+<?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.
+-->
+<ttFont sfntVersion="\x00\x01\x00\x00" ttLibVersion="3.0">
+
+  <GlyphOrder>
+    <GlyphID id="0" name=".notdef"/>
+    <GlyphID id="1" name="1em"/>
+    <GlyphID id="2" name="3em"/>
+  </GlyphOrder>
+
+  <head>
+    <tableVersion value="1.0"/>
+    <fontRevision value="1.0"/>
+    <checkSumAdjustment value="0x640cdb2f"/>
+    <magicNumber value="0x5f0f3cf5"/>
+    <flags value="00000000 00000011"/>
+    <unitsPerEm value="1000"/>
+    <created value="Fri Mar 17 07:26:00 2017"/>
+    <macStyle value="00000000 00000000"/>
+    <lowestRecPPEM value="7"/>
+    <fontDirectionHint value="2"/>
+    <glyphDataFormat value="0"/>
+  </head>
+
+  <hhea>
+    <tableVersion value="1.0"/>
+    <ascent value="1000"/>
+    <descent value="-200"/>
+    <lineGap value="0"/>
+    <caretSlopeRise value="1"/>
+    <caretSlopeRun value="0"/>
+    <caretOffset value="0"/>
+    <reserved0 value="0"/>
+    <reserved1 value="0"/>
+    <reserved2 value="0"/>
+    <reserved3 value="0"/>
+    <metricDataFormat value="0"/>
+  </hhea>
+
+  <maxp>
+    <tableVersion value="0x10000"/>
+    <maxZones value="0"/>
+    <maxTwilightPoints value="0"/>
+    <maxStorage value="0"/>
+    <maxFunctionDefs value="0"/>
+    <maxInstructionDefs value="0"/>
+    <maxStackElements value="0"/>
+    <maxSizeOfInstructions value="0"/>
+    <maxComponentElements value="0"/>
+  </maxp>
+
+  <OS_2>
+    <!-- The fields 'usFirstCharIndex' and 'usLastCharIndex'
+         will be recalculated by the compiler -->
+    <version value="3"/>
+    <xAvgCharWidth value="594"/>
+    <usWeightClass value="400"/>
+    <usWidthClass value="5"/>
+    <fsType value="00000000 00001000"/>
+    <ySubscriptXSize value="650"/>
+    <ySubscriptYSize value="600"/>
+    <ySubscriptXOffset value="0"/>
+    <ySubscriptYOffset value="75"/>
+    <ySuperscriptXSize value="650"/>
+    <ySuperscriptYSize value="600"/>
+    <ySuperscriptXOffset value="0"/>
+    <ySuperscriptYOffset value="350"/>
+    <yStrikeoutSize value="50"/>
+    <yStrikeoutPosition value="300"/>
+    <sFamilyClass value="0"/>
+    <panose>
+      <bFamilyType value="0"/>
+      <bSerifStyle value="0"/>
+      <bWeight value="5"/>
+      <bProportion value="0"/>
+      <bContrast value="0"/>
+      <bStrokeVariation value="0"/>
+      <bArmStyle value="0"/>
+      <bLetterForm value="0"/>
+      <bMidline value="0"/>
+      <bXHeight value="0"/>
+    </panose>
+    <ulUnicodeRange1 value="00000000 00000000 00000000 00000001"/>
+    <ulUnicodeRange2 value="00000000 00000000 00000000 00000000"/>
+    <ulUnicodeRange3 value="00000000 00000000 00000000 00000000"/>
+    <ulUnicodeRange4 value="00000000 00000000 00000000 00000000"/>
+    <achVendID value="UKWN"/>
+    <fsSelection value="00000000 01000000"/>
+    <usFirstCharIndex value="32"/>
+    <usLastCharIndex value="122"/>
+    <sTypoAscender value="800"/>
+    <sTypoDescender value="-200"/>
+    <sTypoLineGap value="200"/>
+    <usWinAscent value="1000"/>
+    <usWinDescent value="200"/>
+    <ulCodePageRange1 value="00000000 00000000 00000000 00000001"/>
+    <ulCodePageRange2 value="00000000 00000000 00000000 00000000"/>
+    <sxHeight value="500"/>
+    <sCapHeight value="700"/>
+    <usDefaultChar value="0"/>
+    <usBreakChar value="32"/>
+    <usMaxContext value="0"/>
+  </OS_2>
+
+  <hmtx>
+    <mtx name=".notdef" width="500" lsb="93"/>
+    <mtx name="1em" width="1000" lsb="93"/>
+    <mtx name="3em" width="3000" lsb="93"/>
+  </hmtx>
+
+  <cmap>
+    <tableVersion version="0"/>
+    <cmap_format_4 platformID="3" platEncID="10" language="0">
+      <map code="0x0061" name="1em" />
+      <map code="0x0062" name="1em" />
+      <map code="0x0063" name="3em" />
+      <map code="0x0064" name="1em" />
+      <map code="0x0065" name="1em" />
+    </cmap_format_4>
+  </cmap>
+
+  <loca>
+    <!-- The 'loca' table will be calculated by the compiler -->
+  </loca>
+
+  <glyf>
+    <TTGlyph name=".notdef" xMin="0" yMin="0" xMax="0" yMax="0" />
+    <TTGlyph name="1em" xMin="0" yMin="0" xMax="0" yMax="0" />
+    <TTGlyph name="3em" xMin="0" yMin="0" xMax="0" yMax="0" />
+  </glyf>
+
+  <name>
+    <namerecord nameID="0" platformID="3" platEncID="1" langID="0x409">
+      Copyright (C) 2017 The Android Open Source Project
+    </namerecord>
+    <namerecord nameID="1" platformID="3" platEncID="1" langID="0x409">
+      Sample Font
+    </namerecord>
+    <namerecord nameID="2" platformID="3" platEncID="1" langID="0x409">
+      Regular
+    </namerecord>
+    <namerecord nameID="4" platformID="3" platEncID="1" langID="0x409">
+      Sample Font
+    </namerecord>
+    <namerecord nameID="6" platformID="3" platEncID="1" langID="0x409">
+      SampleFont-Regular
+    </namerecord>
+    <namerecord nameID="13" platformID="3" platEncID="1" langID="0x409">
+      Licensed under the Apache License, Version 2.0 (the "License");
+      you may not use this file except in compliance with the License.
+      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.
+    </namerecord>
+    <namerecord nameID="14" platformID="3" platEncID="1" langID="0x409">
+      http://www.apache.org/licenses/LICENSE-2.0
+    </namerecord>
+  </name>
+
+  <post>
+    <formatType value="3.0"/>
+    <italicAngle value="0.0"/>
+    <underlinePosition value="-75"/>
+    <underlineThickness value="50"/>
+    <isFixedPitch value="0"/>
+    <minMemType42 value="0"/>
+    <maxMemType42 value="0"/>
+    <minMemType1 value="0"/>
+    <maxMemType1 value="0"/>
+  </post>
+
+</ttFont>
diff --git a/core/tests/coretests/assets/fonts/no_coverage.ttf b/core/tests/coretests/assets/fonts/no_coverage.ttf
new file mode 100644
index 0000000..c884881
--- /dev/null
+++ b/core/tests/coretests/assets/fonts/no_coverage.ttf
Binary files differ
diff --git a/core/tests/coretests/assets/fonts/no_coverage.ttx b/core/tests/coretests/assets/fonts/no_coverage.ttx
new file mode 100644
index 0000000..3be5f86
--- /dev/null
+++ b/core/tests/coretests/assets/fonts/no_coverage.ttx
@@ -0,0 +1,180 @@
+<?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.
+-->
+<ttFont sfntVersion="\x00\x01\x00\x00" ttLibVersion="3.0">
+
+  <GlyphOrder>
+    <GlyphID id="0" name=".notdef"/>
+    <GlyphID id="1" name="dummy"/>
+  </GlyphOrder>
+
+  <head>
+    <tableVersion value="1.0"/>
+    <fontRevision value="1.0"/>
+    <checkSumAdjustment value="0x640cdb2f"/>
+    <magicNumber value="0x5f0f3cf5"/>
+    <flags value="00000000 00000011"/>
+    <unitsPerEm value="1000"/>
+    <created value="Fri Mar 17 07:26:00 2017"/>
+    <macStyle value="00000000 00000000"/>
+    <lowestRecPPEM value="7"/>
+    <fontDirectionHint value="2"/>
+    <glyphDataFormat value="0"/>
+  </head>
+
+  <hhea>
+    <tableVersion value="1.0"/>
+    <ascent value="1000"/>
+    <descent value="-200"/>
+    <lineGap value="0"/>
+    <caretSlopeRise value="1"/>
+    <caretSlopeRun value="0"/>
+    <caretOffset value="0"/>
+    <reserved0 value="0"/>
+    <reserved1 value="0"/>
+    <reserved2 value="0"/>
+    <reserved3 value="0"/>
+    <metricDataFormat value="0"/>
+  </hhea>
+
+  <maxp>
+    <tableVersion value="0x10000"/>
+    <maxZones value="0"/>
+    <maxTwilightPoints value="0"/>
+    <maxStorage value="0"/>
+    <maxFunctionDefs value="0"/>
+    <maxInstructionDefs value="0"/>
+    <maxStackElements value="0"/>
+    <maxSizeOfInstructions value="0"/>
+    <maxComponentElements value="0"/>
+  </maxp>
+
+  <OS_2>
+    <!-- The fields 'usFirstCharIndex' and 'usLastCharIndex'
+         will be recalculated by the compiler -->
+    <version value="3"/>
+    <xAvgCharWidth value="594"/>
+    <usWeightClass value="400"/>
+    <usWidthClass value="5"/>
+    <fsType value="00000000 00001000"/>
+    <ySubscriptXSize value="650"/>
+    <ySubscriptYSize value="600"/>
+    <ySubscriptXOffset value="0"/>
+    <ySubscriptYOffset value="75"/>
+    <ySuperscriptXSize value="650"/>
+    <ySuperscriptYSize value="600"/>
+    <ySuperscriptXOffset value="0"/>
+    <ySuperscriptYOffset value="350"/>
+    <yStrikeoutSize value="50"/>
+    <yStrikeoutPosition value="300"/>
+    <sFamilyClass value="0"/>
+    <panose>
+      <bFamilyType value="0"/>
+      <bSerifStyle value="0"/>
+      <bWeight value="5"/>
+      <bProportion value="0"/>
+      <bContrast value="0"/>
+      <bStrokeVariation value="0"/>
+      <bArmStyle value="0"/>
+      <bLetterForm value="0"/>
+      <bMidline value="0"/>
+      <bXHeight value="0"/>
+    </panose>
+    <ulUnicodeRange1 value="00000000 00000000 00000000 00000001"/>
+    <ulUnicodeRange2 value="00000000 00000000 00000000 00000000"/>
+    <ulUnicodeRange3 value="00000000 00000000 00000000 00000000"/>
+    <ulUnicodeRange4 value="00000000 00000000 00000000 00000000"/>
+    <achVendID value="UKWN"/>
+    <fsSelection value="00000000 01000000"/>
+    <usFirstCharIndex value="32"/>
+    <usLastCharIndex value="122"/>
+    <sTypoAscender value="800"/>
+    <sTypoDescender value="-200"/>
+    <sTypoLineGap value="200"/>
+    <usWinAscent value="1000"/>
+    <usWinDescent value="200"/>
+    <ulCodePageRange1 value="00000000 00000000 00000000 00000001"/>
+    <ulCodePageRange2 value="00000000 00000000 00000000 00000000"/>
+    <sxHeight value="500"/>
+    <sCapHeight value="700"/>
+    <usDefaultChar value="0"/>
+    <usBreakChar value="32"/>
+    <usMaxContext value="0"/>
+  </OS_2>
+
+  <hmtx>
+    <mtx name=".notdef" width="500" lsb="93"/>
+    <mtx name="dummy" width="500" lsb="93"/>
+  </hmtx>
+
+  <cmap>
+    <tableVersion version="0"/>
+    <cmap_format_4 platformID="3" platEncID="10" language="0">
+      <map code="0xFFFD" name="dummy" />  <!-- dummy entry -->
+    </cmap_format_4>
+  </cmap>
+
+  <loca>
+    <!-- The 'loca' table will be calculated by the compiler -->
+  </loca>
+
+  <glyf>
+    <TTGlyph name=".notdef" xMin="0" yMin="0" xMax="0" yMax="0" />
+    <TTGlyph name="dummy" xMin="0" yMin="0" xMax="0" yMax="0" />
+  </glyf>
+
+  <name>
+    <namerecord nameID="0" platformID="3" platEncID="1" langID="0x409">
+      Copyright (C) 2017 The Android Open Source Project
+    </namerecord>
+    <namerecord nameID="1" platformID="3" platEncID="1" langID="0x409">
+      Sample Font
+    </namerecord>
+    <namerecord nameID="2" platformID="3" platEncID="1" langID="0x409">
+      Regular
+    </namerecord>
+    <namerecord nameID="4" platformID="3" platEncID="1" langID="0x409">
+      Sample Font
+    </namerecord>
+    <namerecord nameID="6" platformID="3" platEncID="1" langID="0x409">
+      SampleFont-Regular
+    </namerecord>
+    <namerecord nameID="13" platformID="3" platEncID="1" langID="0x409">
+      Licensed under the Apache License, Version 2.0 (the "License");
+      you may not use this file except in compliance with the License.
+      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.
+    </namerecord>
+    <namerecord nameID="14" platformID="3" platEncID="1" langID="0x409">
+      http://www.apache.org/licenses/LICENSE-2.0
+    </namerecord>
+  </name>
+
+  <post>
+    <formatType value="3.0"/>
+    <italicAngle value="0.0"/>
+    <underlinePosition value="-75"/>
+    <underlineThickness value="50"/>
+    <isFixedPitch value="0"/>
+    <minMemType42 value="0"/>
+    <maxMemType42 value="0"/>
+    <minMemType1 value="0"/>
+    <maxMemType1 value="0"/>
+  </post>
+
+</ttFont>
diff --git a/core/tests/coretests/src/android/app/DownloadManagerBaseTest.java b/core/tests/coretests/src/android/app/DownloadManagerBaseTest.java
index 42b06f5..68b9b00 100644
--- a/core/tests/coretests/src/android/app/DownloadManagerBaseTest.java
+++ b/core/tests/coretests/src/android/app/DownloadManagerBaseTest.java
@@ -29,13 +29,15 @@
 import android.net.wifi.WifiManager;
 import android.os.Environment;
 import android.os.ParcelFileDescriptor;
-import android.os.UserHandle;
 import android.os.ParcelFileDescriptor.AutoCloseInputStream;
 import android.os.SystemClock;
+import android.os.UserHandle;
 import android.provider.Settings;
 import android.test.InstrumentationTestCase;
 import android.util.Log;
 
+import libcore.io.Streams;
+
 import com.google.mockwebserver.MockResponse;
 import com.google.mockwebserver.MockWebServer;
 
@@ -54,8 +56,6 @@
 import java.util.Set;
 import java.util.concurrent.TimeoutException;
 
-import libcore.io.Streams;
-
 /**
  * Base class for Instrumented tests for the Download Manager.
  */
@@ -83,9 +83,6 @@
     protected static final int WAIT_FOR_DOWNLOAD_POLL_TIME = 1 * 1000;  // 1 second
     protected static final int MAX_WAIT_FOR_DOWNLOAD_TIME = 30 * 1000; // 30 seconds
 
-    protected static final int DOWNLOAD_TO_SYSTEM_CACHE = 1;
-    protected static final int DOWNLOAD_TO_DOWNLOAD_CACHE_DIR = 2;
-
     // Just a few popular file types used to return from a download
     protected enum DownloadFileType {
         PLAINTEXT,
@@ -923,13 +920,13 @@
      * @param body The body to return in the response from the server
      */
     protected long doStandardEnqueue(byte[] body) throws Exception {
-        return enqueueDownloadRequest(body, DOWNLOAD_TO_DOWNLOAD_CACHE_DIR);
+        return enqueueDownloadRequest(body);
     }
 
-    protected long enqueueDownloadRequest(byte[] body, int location) throws Exception {
+    protected long enqueueDownloadRequest(byte[] body) throws Exception {
         // Prepare the mock server with a standard response
         mServer.enqueue(buildResponse(HTTP_OK, body));
-        return doEnqueue(location);
+        return doEnqueue();
     }
 
     /**
@@ -938,13 +935,13 @@
      * @param body The body to return in the response from the server, contained in the file
      */
     protected long doStandardEnqueue(File body) throws Exception {
-        return enqueueDownloadRequest(body, DOWNLOAD_TO_DOWNLOAD_CACHE_DIR);
+        return enqueueDownloadRequest(body);
     }
 
-    protected long enqueueDownloadRequest(File body, int location) throws Exception {
+    protected long enqueueDownloadRequest(File body) throws Exception {
         // Prepare the mock server with a standard response
         mServer.enqueue(buildResponse(HTTP_OK, body));
-        return doEnqueue(location);
+        return doEnqueue();
     }
 
     /**
@@ -952,16 +949,12 @@
      * doing a standard enqueue request to the server.
      */
     protected long doCommonStandardEnqueue() throws Exception {
-        return doEnqueue(DOWNLOAD_TO_DOWNLOAD_CACHE_DIR);
+        return doEnqueue();
     }
 
-    private long doEnqueue(int location) throws Exception {
+    private long doEnqueue() throws Exception {
         Uri uri = getServerUri(DEFAULT_FILENAME);
         Request request = new Request(uri).setTitle(DEFAULT_FILENAME);
-        if (location == DOWNLOAD_TO_SYSTEM_CACHE) {
-            request.setDestinationToSystemCache();
-        }
-
         return mDownloadManager.enqueue(request);
     }
 
@@ -1026,8 +1019,8 @@
     /**
      * Helper that does the actual basic download verification.
      */
-    protected long doBasicDownload(byte[] blobData, int location) throws Exception {
-        long dlRequest = enqueueDownloadRequest(blobData, location);
+    protected long doBasicDownload(byte[] blobData) throws Exception {
+        long dlRequest = enqueueDownloadRequest(blobData);
 
         // wait for the download to complete
         waitForDownloadOrTimeout(dlRequest);
diff --git a/core/tests/coretests/src/android/app/DownloadManagerFunctionalTest.java b/core/tests/coretests/src/android/app/DownloadManagerFunctionalTest.java
index d1a5d28..c1d4be0 100644
--- a/core/tests/coretests/src/android/app/DownloadManagerFunctionalTest.java
+++ b/core/tests/coretests/src/android/app/DownloadManagerFunctionalTest.java
@@ -23,12 +23,13 @@
 import android.os.Environment;
 import android.os.ParcelFileDescriptor;
 import android.test.suitebuilder.annotation.LargeTest;
+
 import com.google.mockwebserver.MockResponse;
 
 import java.io.File;
-import java.util.concurrent.TimeoutException;
 import java.util.Iterator;
 import java.util.Set;
+import java.util.concurrent.TimeoutException;
 
 /**
  * Integration tests of the DownloadManager API.
@@ -95,11 +96,11 @@
      * Test a basic download of a binary file 500k in size.
      */
     @LargeTest
-    public void testBinaryDownloadToSystemCache() throws Exception {
+    public void testBinaryDownload() throws Exception {
         int fileSize = 1024;
         byte[] blobData = generateData(fileSize, DataType.BINARY);
 
-        long dlRequest = doBasicDownload(blobData, DOWNLOAD_TO_SYSTEM_CACHE);
+        long dlRequest = doBasicDownload(blobData);
         verifyDownload(dlRequest, blobData);
         mDownloadManager.remove(dlRequest);
     }
@@ -108,11 +109,11 @@
      * Tests the basic downloading of a text file 300000 bytes in size.
      */
     @LargeTest
-    public void testTextDownloadToSystemCache() throws Exception {
+    public void testTextDownload() throws Exception {
         int fileSize = 1024;
         byte[] blobData = generateData(fileSize, DataType.TEXT);
 
-        long dlRequest = doBasicDownload(blobData, DOWNLOAD_TO_SYSTEM_CACHE);
+        long dlRequest = doBasicDownload(blobData);
         verifyDownload(dlRequest, blobData);
         mDownloadManager.remove(dlRequest);
     }
@@ -318,7 +319,7 @@
         int fileSize = 1024;
         byte[] blobData = generateData(fileSize, DataType.BINARY);
 
-        long dlRequest = doBasicDownload(blobData, DOWNLOAD_TO_DOWNLOAD_CACHE_DIR);
+        long dlRequest = doBasicDownload(blobData);
         Cursor cursor = mDownloadManager.query(new Query().setFilterById(dlRequest));
         try {
             assertEquals("The count of downloads with this ID is not 1!", 1, cursor.getCount());
diff --git a/core/tests/coretests/src/android/app/DownloadManagerStressTest.java b/core/tests/coretests/src/android/app/DownloadManagerStressTest.java
index 9fa9131..39d9a8e 100644
--- a/core/tests/coretests/src/android/app/DownloadManagerStressTest.java
+++ b/core/tests/coretests/src/android/app/DownloadManagerStressTest.java
@@ -195,7 +195,7 @@
 
             // try to download 1MB file into /cache - and it should succeed
             byte[] blobData = generateData(DOWNLOAD_FILE_SIZE, DataType.TEXT);
-            long dlRequest = doBasicDownload(blobData, DOWNLOAD_TO_SYSTEM_CACHE);
+            long dlRequest = doBasicDownload(blobData);
             verifyAndCleanupSingleFileDownload(dlRequest, blobData);
         } finally {
             if (outFile != null) {
diff --git a/core/tests/coretests/src/android/app/timezone/DistroFormatVersionTest.java b/core/tests/coretests/src/android/app/timezone/DistroFormatVersionTest.java
index 9bbcd3d..70a0877 100644
--- a/core/tests/coretests/src/android/app/timezone/DistroFormatVersionTest.java
+++ b/core/tests/coretests/src/android/app/timezone/DistroFormatVersionTest.java
@@ -27,7 +27,6 @@
 /**
  * Tests for {@link DistroFormatVersion}.
  */
-// TODO(nfuller) Move to CTS once this class is part of the SystemApi. http://b/31008728
 public class DistroFormatVersionTest {
 
     @Test
diff --git a/core/tests/coretests/src/android/app/timezone/DistroRulesVersionTest.java b/core/tests/coretests/src/android/app/timezone/DistroRulesVersionTest.java
index 2fbc9a1..eecae46 100644
--- a/core/tests/coretests/src/android/app/timezone/DistroRulesVersionTest.java
+++ b/core/tests/coretests/src/android/app/timezone/DistroRulesVersionTest.java
@@ -27,7 +27,6 @@
 /**
  * Tests for {@link DistroRulesVersion}.
  */
-// TODO(nfuller) Move to CTS once this class is part of the SystemApi. http://b/31008728
 public class DistroRulesVersionTest {
 
     @Test
diff --git a/core/tests/coretests/src/android/app/timezone/RulesStateTest.java b/core/tests/coretests/src/android/app/timezone/RulesStateTest.java
index 7f4819b..99abe24 100644
--- a/core/tests/coretests/src/android/app/timezone/RulesStateTest.java
+++ b/core/tests/coretests/src/android/app/timezone/RulesStateTest.java
@@ -29,7 +29,6 @@
 /**
  * Tests for {@link RulesState}.
  */
-// TODO(nfuller) Move to CTS once this class is part of the SystemApi. http://b/31008728
 public class RulesStateTest {
 
     @Test
diff --git a/core/tests/coretests/src/android/app/timezone/RulesUpdaterContractTest.java b/core/tests/coretests/src/android/app/timezone/RulesUpdaterContractTest.java
index e7a839c..91f8ebc 100644
--- a/core/tests/coretests/src/android/app/timezone/RulesUpdaterContractTest.java
+++ b/core/tests/coretests/src/android/app/timezone/RulesUpdaterContractTest.java
@@ -33,7 +33,6 @@
 /**
  * Tests for {@link RulesUpdaterContract}.
  */
-// TODO(nfuller) Move to CTS once this class is part of the SystemApi. http://b/31008728
 public class RulesUpdaterContractTest {
 
     @Test
diff --git a/core/tests/coretests/src/android/content/pm/PackageBackwardCompatibilityTest.java b/core/tests/coretests/src/android/content/pm/PackageBackwardCompatibilityTest.java
new file mode 100644
index 0000000..4d2a047
--- /dev/null
+++ b/core/tests/coretests/src/android/content/pm/PackageBackwardCompatibilityTest.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.content.pm;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+
+import android.content.pm.PackageParser.Package;
+import android.os.Build;
+import android.support.test.filters.SmallTest;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+import java.util.ArrayList;
+import java.util.Collections;
+
+@SmallTest
+@RunWith(JUnit4.class)
+public class PackageBackwardCompatibilityTest {
+
+    private static final String ORG_APACHE_HTTP_LEGACY = "org.apache.http.legacy";
+
+    private static final String ANDROID_TEST_RUNNER = "android.test.runner";
+
+    private static final String ANDROID_TEST_MOCK = "android.test.mock";
+
+    private Package mPackage;
+
+    private static ArrayList<String> arrayList(String... strings) {
+        ArrayList<String> list = new ArrayList<>();
+        Collections.addAll(list, strings);
+        return list;
+    }
+
+    @Before
+    public void setUp() {
+        mPackage = new Package("org.package.name");
+        mPackage.applicationInfo.targetSdkVersion = Build.VERSION_CODES.CUR_DEVELOPMENT;
+    }
+
+    @Test
+    public void null_usesLibraries() {
+        PackageBackwardCompatibility.modifySharedLibraries(mPackage);
+        assertNull("usesLibraries not updated correctly", mPackage.usesLibraries);
+    }
+
+    @Test
+    public void null_usesOptionalLibraries() {
+        PackageBackwardCompatibility.modifySharedLibraries(mPackage);
+        assertNull("usesOptionalLibraries not updated correctly", mPackage.usesOptionalLibraries);
+    }
+
+    @Test
+    public void remove_org_apache_http_legacy_from_usesLibraries() {
+        mPackage.usesLibraries = arrayList(ORG_APACHE_HTTP_LEGACY);
+        PackageBackwardCompatibility.modifySharedLibraries(mPackage);
+        assertNull("usesLibraries not updated correctly", mPackage.usesLibraries);
+    }
+
+    @Test
+    public void remove_org_apache_http_legacy_from_usesOptionalLibraries() {
+        mPackage.usesOptionalLibraries = arrayList(ORG_APACHE_HTTP_LEGACY);
+        PackageBackwardCompatibility.modifySharedLibraries(mPackage);
+        assertNull("usesOptionalLibraries not updated correctly", mPackage.usesOptionalLibraries);
+    }
+
+    @Test
+    public void android_test_runner_in_usesLibraries() {
+        mPackage.usesLibraries = arrayList(ANDROID_TEST_RUNNER);
+        PackageBackwardCompatibility.modifySharedLibraries(mPackage);
+        assertEquals("usesLibraries not updated correctly",
+                arrayList(ANDROID_TEST_RUNNER, ANDROID_TEST_MOCK),
+                mPackage.usesLibraries);
+    }
+
+    @Test
+    public void android_test_runner_in_usesOptionalLibraries() {
+        mPackage.usesOptionalLibraries = arrayList(ANDROID_TEST_RUNNER);
+        PackageBackwardCompatibility.modifySharedLibraries(mPackage);
+        assertEquals("usesOptionalLibraries not updated correctly",
+                arrayList(ANDROID_TEST_RUNNER, ANDROID_TEST_MOCK),
+                mPackage.usesOptionalLibraries);
+    }
+
+    @Test
+    public void android_test_runner_in_usesLibraries_android_test_mock_in_usesOptionalLibraries() {
+        mPackage.usesLibraries = arrayList(ANDROID_TEST_RUNNER);
+        mPackage.usesOptionalLibraries = arrayList(ANDROID_TEST_MOCK);
+        PackageBackwardCompatibility.modifySharedLibraries(mPackage);
+        assertEquals("usesLibraries not updated correctly",
+                arrayList(ANDROID_TEST_RUNNER),
+                mPackage.usesLibraries);
+        assertEquals("usesOptionalLibraries not updated correctly",
+                arrayList(ANDROID_TEST_MOCK),
+                mPackage.usesOptionalLibraries);
+    }
+}
diff --git a/core/tests/coretests/src/android/content/pm/PackageHelperTests.java b/core/tests/coretests/src/android/content/pm/PackageHelperTests.java
index 5c497b4..55092fa 100644
--- a/core/tests/coretests/src/android/content/pm/PackageHelperTests.java
+++ b/core/tests/coretests/src/android/content/pm/PackageHelperTests.java
@@ -34,6 +34,7 @@
 import java.io.IOException;
 import java.util.ArrayList;
 import java.util.List;
+import java.util.UUID;
 
 import static android.net.TrafficStats.MB_IN_BYTES;
 import static android.os.storage.VolumeInfo.STATE_MOUNTED;
@@ -90,14 +91,20 @@
         File internalFile = new File(sInternalVolPath);
         File adoptedFile = new File(sAdoptedVolPath);
         File publicFile = new File(sPublicVolPath);
+        UUID internalUuid = UUID.randomUUID();
+        UUID adoptedUuid = UUID.randomUUID();
+        UUID publicUuid = UUID.randomUUID();
         Mockito.when(storageManager.getStorageBytesUntilLow(internalFile)).thenReturn(sInternalSize);
         Mockito.when(storageManager.getStorageBytesUntilLow(adoptedFile)).thenReturn(sAdoptedSize);
         Mockito.when(storageManager.getStorageBytesUntilLow(publicFile)).thenReturn(sPublicSize);
-        Mockito.when(storageManager.getAllocatableBytes(Mockito.eq(internalFile), Mockito.anyInt()))
+        Mockito.when(storageManager.getUuidForPath(Mockito.eq(internalFile))).thenReturn(internalUuid);
+        Mockito.when(storageManager.getUuidForPath(Mockito.eq(adoptedFile))).thenReturn(adoptedUuid);
+        Mockito.when(storageManager.getUuidForPath(Mockito.eq(publicFile))).thenReturn(publicUuid);
+        Mockito.when(storageManager.getAllocatableBytes(Mockito.eq(internalUuid), Mockito.anyInt()))
                 .thenReturn(sInternalSize);
-        Mockito.when(storageManager.getAllocatableBytes(Mockito.eq(adoptedFile), Mockito.anyInt()))
+        Mockito.when(storageManager.getAllocatableBytes(Mockito.eq(adoptedUuid), Mockito.anyInt()))
                 .thenReturn(sAdoptedSize);
-        Mockito.when(storageManager.getAllocatableBytes(Mockito.eq(publicFile), Mockito.anyInt()))
+        Mockito.when(storageManager.getAllocatableBytes(Mockito.eq(publicUuid), Mockito.anyInt()))
                 .thenReturn(sPublicSize);
         return storageManager;
     }
diff --git a/core/tests/coretests/src/android/graphics/TypefaceSystemFallbackTest.java b/core/tests/coretests/src/android/graphics/TypefaceSystemFallbackTest.java
new file mode 100644
index 0000000..ca4f7d4
--- /dev/null
+++ b/core/tests/coretests/src/android/graphics/TypefaceSystemFallbackTest.java
@@ -0,0 +1,469 @@
+/*
+ * 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.graphics;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+import android.content.Context;
+import android.content.res.AssetManager;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+import android.util.ArrayMap;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.charset.Charset;
+import java.nio.file.Files;
+import java.nio.file.StandardCopyOption;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class TypefaceSystemFallbackTest {
+    private static final String SYSTEM_FONT_DIR = "/system/fonts/";
+    private static final String SYSTEM_FONTS_XML = "/system/etc/fonts.xml";
+
+    private static final String[] TEST_FONT_FILES = {
+        "a3em.ttf",  // Supports "a","b","c". The width of "a" is 3em,  others are 1em.
+        "b3em.ttf",  // Supports "a","b","c". The width of "b" is 3em,  others are 1em.
+        "c3em.ttf",  // Supports "a","b","c". The width of "c" is 3em,  others are 1em.
+        "all2em.ttf",  // Supports "a,","b","c". All of them have the same width of 2em.
+        "no_coverage.ttf",  // This font doesn't support any characters.
+    };
+    private static final String TEST_FONTS_XML;
+    private static final String TEST_FONT_DIR;
+
+    private static final float GLYPH_1EM_WIDTH;
+    private static final float GLYPH_2EM_WIDTH;
+    private static final float GLYPH_3EM_WIDTH;
+
+    static {
+        final Context targetCtx = InstrumentationRegistry.getInstrumentation().getTargetContext();
+        final File cacheDir = new File(targetCtx.getCacheDir(), "TypefaceSystemFallbackTest");
+        if (!cacheDir.isDirectory()) {
+            cacheDir.mkdirs();
+        }
+        TEST_FONT_DIR = cacheDir.getAbsolutePath() + "/";
+        TEST_FONTS_XML = new File(cacheDir, "fonts.xml").getAbsolutePath();
+
+        final AssetManager am =
+                InstrumentationRegistry.getInstrumentation().getContext().getAssets();
+        final Paint paint = new Paint();
+        paint.setTypeface(new Typeface.Builder(am, "fonts/a3em.ttf").build());
+        GLYPH_3EM_WIDTH = paint.measureText("a");
+        GLYPH_1EM_WIDTH = paint.measureText("b");
+
+        paint.setTypeface(new Typeface.Builder(am, "fonts/all2em.ttf").build());
+        GLYPH_2EM_WIDTH = paint.measureText("a");
+    }
+
+    @Before
+    public void setUp() {
+        final AssetManager am =
+                InstrumentationRegistry.getInstrumentation().getContext().getAssets();
+        for (final String fontFile : TEST_FONT_FILES) {
+            final String sourceInAsset = "fonts/" + fontFile;
+            final File outInCache = new File(TEST_FONT_DIR, fontFile);
+            try (InputStream is = am.open(sourceInAsset)) {
+                Files.copy(is, outInCache.toPath(), StandardCopyOption.REPLACE_EXISTING);
+            } catch (IOException e) {
+                throw new RuntimeException(e);
+            }
+        }
+    }
+
+    @After
+    public void tearDown() {
+        for (final String fontFile : TEST_FONT_FILES) {
+            final File outInCache = new File(TEST_FONT_DIR, fontFile);
+            outInCache.delete();
+        }
+    }
+
+    private static void buildSystemFallback(String xml,
+            ArrayMap<String, Typeface> fontMap, ArrayMap<String, FontFamily[]> fallbackMap) {
+        try (FileOutputStream fos = new FileOutputStream(TEST_FONTS_XML)) {
+            fos.write(xml.getBytes(Charset.forName("UTF-8")));
+        } catch (IOException e) {
+            throw new RuntimeException(e);
+        }
+        Typeface.buildSystemFallback(TEST_FONTS_XML, TEST_FONT_DIR, fontMap, fallbackMap);
+    }
+
+    @Test
+    public void testBuildSystemFallback() {
+        final ArrayMap<String, Typeface> fontMap = new ArrayMap<>();
+        final ArrayMap<String, FontFamily[]> fallbackMap = new ArrayMap<>();
+
+        Typeface.buildSystemFallback(SYSTEM_FONTS_XML, SYSTEM_FONT_DIR, fontMap, fallbackMap);
+
+        assertFalse(fontMap.isEmpty());
+        assertFalse(fallbackMap.isEmpty());
+    }
+
+    @Test
+    public void testBuildSystemFallback_NonExistentFontShouldBeIgnored() {
+        final String xml = "<?xml version='1.0' encoding='UTF-8'?>"
+                + "<familyset version='22'>"
+                + "  <family name='sans-serif'>"
+                + "    <font weight='400' style='normal'>a3em.ttf</font>"
+                + "    <font weight='400' style='normal'>NoSuchFont.ttf</font>"
+                + "  </family>"
+                + "  <family name='NoSuchFont'>"
+                + "    <font weight='400' style='normal'>NoSuchFont.ttf</font>"
+                + "  </family>"
+                + "  <family>"
+                + "    <font weight='400' style='normal'>NoSuchFont.ttf</font>"
+                + "  </family>"
+                + "</familyset>";
+        final ArrayMap<String, Typeface> fontMap = new ArrayMap<>();
+        final ArrayMap<String, FontFamily[]> fallbackMap = new ArrayMap<>();
+
+        buildSystemFallback(xml, fontMap, fallbackMap);
+
+        assertEquals(1, fontMap.size());
+        assertTrue(fontMap.containsKey("sans-serif"));
+        assertEquals(1, fallbackMap.size());
+        assertTrue(fallbackMap.containsKey("sans-serif"));
+    }
+
+    @Test
+    public void testBuildSystemFallback_NamedFamily() {
+        final String xml = "<?xml version='1.0' encoding='UTF-8'?>"
+                + "<familyset version='22'>"
+                + "  <family name='sans-serif'>"
+                + "    <font weight='400' style='normal'>a3em.ttf</font>"
+                + "  </family>"
+                + "  <family name='test'>"
+                + "    <font weight='400' style='normal'>b3em.ttf</font>"
+                + "  </family>"
+                + "  <family name='test2'>"
+                + "    <font weight='400' style='normal'>c3em.ttf</font>"
+                + "  </family>"
+                + "  <family>"
+                + "    <font weight='400' style='normal'>all2em.ttf</font>"
+                + "  </family>"
+                + "</familyset>";
+        final ArrayMap<String, Typeface> fontMap = new ArrayMap<>();
+        final ArrayMap<String, FontFamily[]> fallbackMap = new ArrayMap<>();
+
+        buildSystemFallback(xml, fontMap, fallbackMap);
+
+        final Paint paint = new Paint();
+
+        final Typeface sansSerifTypeface = fontMap.get("sans-serif");
+        assertNotNull(sansSerifTypeface);
+        paint.setTypeface(sansSerifTypeface);
+        assertEquals(GLYPH_3EM_WIDTH, paint.measureText("a"), 0.0f);
+        assertEquals(GLYPH_1EM_WIDTH, paint.measureText("b"), 0.0f);
+        assertEquals(GLYPH_1EM_WIDTH, paint.measureText("c"), 0.0f);
+
+        final Typeface testTypeface = fontMap.get("test");
+        assertNotNull(testTypeface);
+        paint.setTypeface(testTypeface);
+        assertEquals(GLYPH_1EM_WIDTH, paint.measureText("a"), 0.0f);
+        assertEquals(GLYPH_3EM_WIDTH, paint.measureText("b"), 0.0f);
+        assertEquals(GLYPH_1EM_WIDTH, paint.measureText("c"), 0.0f);
+
+        final Typeface test2Typeface = fontMap.get("test2");
+        assertNotNull(test2Typeface);
+        paint.setTypeface(test2Typeface);
+        assertEquals(GLYPH_1EM_WIDTH, paint.measureText("a"), 0.0f);
+        assertEquals(GLYPH_1EM_WIDTH, paint.measureText("b"), 0.0f);
+        assertEquals(GLYPH_3EM_WIDTH, paint.measureText("c"), 0.0f);
+    }
+
+    @Test
+    public void testBuildSystemFallback_defaultFallback() {
+        final String xml = "<?xml version='1.0' encoding='UTF-8'?>"
+                + "<familyset version='22'>"
+                + "  <family name='sans-serif'>"
+                + "    <font weight='400' style='normal'>no_coverage.ttf</font>"
+                + "  </family>"
+                + "  <family name='test'>"
+                + "    <font weight='400' style='normal'>no_coverage.ttf</font>"
+                + "  </family>"
+                + "  <family>"
+                + "    <font weight='400' style='normal'>a3em.ttf</font>"
+                + "  </family>"
+                + "  <family>"
+                + "    <font weight='400' style='normal'>all2em.ttf</font>"
+                + "  </family>"
+                + "</familyset>";
+        final ArrayMap<String, Typeface> fontMap = new ArrayMap<>();
+        final ArrayMap<String, FontFamily[]> fallbackMap = new ArrayMap<>();
+
+        buildSystemFallback(xml, fontMap, fallbackMap);
+
+        final Paint paint = new Paint();
+
+        final Typeface sansSerifTypeface = fontMap.get("sans-serif");
+        assertNotNull(sansSerifTypeface);
+        paint.setTypeface(sansSerifTypeface);
+        assertEquals(GLYPH_3EM_WIDTH, paint.measureText("a"), 0.0f);
+        assertEquals(GLYPH_1EM_WIDTH, paint.measureText("b"), 0.0f);
+        assertEquals(GLYPH_1EM_WIDTH, paint.measureText("c"), 0.0f);
+
+        final Typeface testTypeface = fontMap.get("test");
+        assertNotNull(testTypeface);
+        paint.setTypeface(testTypeface);
+        assertEquals(GLYPH_3EM_WIDTH, paint.measureText("a"), 0.0f);
+        assertEquals(GLYPH_1EM_WIDTH, paint.measureText("b"), 0.0f);
+        assertEquals(GLYPH_1EM_WIDTH, paint.measureText("c"), 0.0f);
+    }
+
+    @Test
+    public void testBuildSystemFallback_namedFallbackFamily() {
+        final String xml = "<?xml version='1.0' encoding='UTF-8'?>"
+                + "<familyset version='22'>"
+                + "  <family name='sans-serif'>"
+                + "    <font weight='400' style='normal'>no_coverage.ttf</font>"
+                + "  </family>"
+                + "  <family name='test'>"
+                + "    <font weight='400' style='normal'>no_coverage.ttf</font>"
+                + "  </family>"
+                + "  <family name='test2'>"
+                + "    <font weight='400' style='normal'>no_coverage.ttf</font>"
+                + "  </family>"
+                + "  <family>"
+                + "    <font weight='400' style='normal' fallbackFor='test'>a3em.ttf</font>"
+                + "  </family>"
+                + "  <family>"
+                + "    <font weight='400' style='normal' fallbackFor='test2'>b3em.ttf</font>"
+                + "  </family>"
+                + "  <family>"
+                + "    <font weight='400' style='normal'>all2em.ttf</font>"
+                + "  </family>"
+                + "</familyset>";
+        final ArrayMap<String, Typeface> fontMap = new ArrayMap<>();
+        final ArrayMap<String, FontFamily[]> fallbackMap = new ArrayMap<>();
+
+        buildSystemFallback(xml, fontMap, fallbackMap);
+
+        final Paint paint = new Paint();
+
+        final Typeface sansSerifTypeface = fontMap.get("sans-serif");
+        assertNotNull(sansSerifTypeface);
+        paint.setTypeface(sansSerifTypeface);
+        assertEquals(GLYPH_2EM_WIDTH, paint.measureText("a"), 0.0f);
+        assertEquals(GLYPH_2EM_WIDTH, paint.measureText("b"), 0.0f);
+        assertEquals(GLYPH_2EM_WIDTH, paint.measureText("c"), 0.0f);
+
+        final Typeface testTypeface = fontMap.get("test");
+        assertNotNull(testTypeface);
+        paint.setTypeface(testTypeface);
+        assertEquals(GLYPH_3EM_WIDTH, paint.measureText("a"), 0.0f);
+        assertEquals(GLYPH_1EM_WIDTH, paint.measureText("b"), 0.0f);
+        assertEquals(GLYPH_1EM_WIDTH, paint.measureText("c"), 0.0f);
+
+        final Typeface test2Typeface = fontMap.get("test2");
+        assertNotNull(test2Typeface);
+        paint.setTypeface(test2Typeface);
+        assertEquals(GLYPH_1EM_WIDTH, paint.measureText("a"), 0.0f);
+        assertEquals(GLYPH_3EM_WIDTH, paint.measureText("b"), 0.0f);
+        assertEquals(GLYPH_1EM_WIDTH, paint.measureText("c"), 0.0f);
+    }
+
+    @Test
+    public void testBuildSystemFallback_namedFallbackFamily2() {
+        final String xml = "<?xml version='1.0' encoding='UTF-8'?>"
+                + "<familyset version='22'>"
+                + "  <family name='sans-serif'>"
+                + "    <font weight='400' style='normal'>no_coverage.ttf</font>"
+                + "  </family>"
+                + "  <family name='test'>"
+                + "    <font weight='400' style='normal'>no_coverage.ttf</font>"
+                + "  </family>"
+                + "  <family name='test2'>"
+                + "    <font weight='400' style='normal'>no_coverage.ttf</font>"
+                + "  </family>"
+                + "  <family>"
+                + "    <font weight='400' style='normal' fallbackFor='test'>a3em.ttf</font>"
+                + "    <font weight='400' style='normal'>b3em.ttf</font>"
+                + "  </family>"
+                + "  <family>"
+                + "    <font weight='400' style='normal'>all2em.ttf</font>"
+                + "  </family>"
+                + "</familyset>";
+        final ArrayMap<String, Typeface> fontMap = new ArrayMap<>();
+        final ArrayMap<String, FontFamily[]> fallbackMap = new ArrayMap<>();
+
+        buildSystemFallback(xml, fontMap, fallbackMap);
+
+        final Paint paint = new Paint();
+
+        final Typeface sansSerifTypeface = fontMap.get("sans-serif");
+        assertNotNull(sansSerifTypeface);
+        paint.setTypeface(sansSerifTypeface);
+        assertEquals(GLYPH_1EM_WIDTH, paint.measureText("a"), 0.0f);
+        assertEquals(GLYPH_3EM_WIDTH, paint.measureText("b"), 0.0f);
+        assertEquals(GLYPH_1EM_WIDTH, paint.measureText("c"), 0.0f);
+
+        final Typeface testTypeface = fontMap.get("test");
+        assertNotNull(testTypeface);
+        paint.setTypeface(testTypeface);
+        assertEquals(GLYPH_3EM_WIDTH, paint.measureText("a"), 0.0f);
+        assertEquals(GLYPH_1EM_WIDTH, paint.measureText("b"), 0.0f);
+        assertEquals(GLYPH_1EM_WIDTH, paint.measureText("c"), 0.0f);
+
+        final Typeface test2Typeface = fontMap.get("test2");
+        assertNotNull(test2Typeface);
+        paint.setTypeface(test2Typeface);
+        assertEquals(GLYPH_1EM_WIDTH, paint.measureText("a"), 0.0f);
+        assertEquals(GLYPH_3EM_WIDTH, paint.measureText("b"), 0.0f);
+        assertEquals(GLYPH_1EM_WIDTH, paint.measureText("c"), 0.0f);
+    }
+
+    @Test
+    public void testBuildSystemFallback_ImplicitSansSerifFallback() {
+        final String xml = "<?xml version='1.0' encoding='UTF-8'?>"
+                + "<familyset version='22'>"
+                + "  <family name='sans-serif'>"
+                + "    <font weight='400' style='normal'>a3em.ttf</font>"
+                + "  </family>"
+                + "  <family name='test'>"
+                + "    <font weight='400' style='normal'>no_coverage.ttf</font>"
+                + "  </family>"
+                + "  <family name='test2'>"
+                + "    <font weight='400' style='normal'>no_coverage.ttf</font>"
+                + "  </family>"
+                + "  <family>"
+                + "    <font weight='400' style='normal'>all2em.ttf</font>"
+                + "  </family>"
+                + "</familyset>";
+        final ArrayMap<String, Typeface> fontMap = new ArrayMap<>();
+        final ArrayMap<String, FontFamily[]> fallbackMap = new ArrayMap<>();
+
+        buildSystemFallback(xml, fontMap, fallbackMap);
+
+        final Paint paint = new Paint();
+
+        final Typeface testTypeface = fontMap.get("test");
+        assertNotNull(testTypeface);
+        paint.setTypeface(testTypeface);
+        assertEquals(GLYPH_3EM_WIDTH, paint.measureText("a"), 0.0f);
+        assertEquals(GLYPH_1EM_WIDTH, paint.measureText("b"), 0.0f);
+        assertEquals(GLYPH_1EM_WIDTH, paint.measureText("c"), 0.0f);
+
+        final Typeface test2Typeface = fontMap.get("test2");
+        assertNotNull(test2Typeface);
+        paint.setTypeface(test2Typeface);
+        assertEquals(GLYPH_3EM_WIDTH, paint.measureText("a"), 0.0f);
+        assertEquals(GLYPH_1EM_WIDTH, paint.measureText("b"), 0.0f);
+        assertEquals(GLYPH_1EM_WIDTH, paint.measureText("c"), 0.0f);
+    }
+
+    @Test
+    public void testBuildSystemFallback_ElegantFallback() {
+        final String xml = "<?xml version='1.0' encoding='UTF-8'?>"
+                + "<familyset version='22'>"
+                + "  <family name='sans-serif'>"
+                + "    <font weight='400' style='normal'>no_coverage.ttf</font>"
+                + "  </family>"
+                + "  <family name='serif'>"
+                + "    <font weight='400' style='normal'>no_coverage.ttf</font>"
+                + "  </family>"
+                + "  <family variant='elegant'>"
+                + "    <font weight='400' style='normal'>a3em.ttf</font>"
+                + "  </family>"
+                + "  <family variant='compact'>"
+                + "    <font weight='400' style='normal'>b3em.ttf</font>"
+                + "  </family>"
+                + "</familyset>";
+        final ArrayMap<String, Typeface> fontMap = new ArrayMap<>();
+        final ArrayMap<String, FontFamily[]> fallbackMap = new ArrayMap<>();
+
+        buildSystemFallback(xml, fontMap, fallbackMap);
+
+        final Paint paint = new Paint();
+
+        final Typeface testTypeface = fontMap.get("serif");
+        assertNotNull(testTypeface);
+        paint.setTypeface(testTypeface);
+        paint.setElegantTextHeight(true);
+        assertEquals(GLYPH_3EM_WIDTH, paint.measureText("a"), 0.0f);
+        assertEquals(GLYPH_1EM_WIDTH, paint.measureText("b"), 0.0f);
+        assertEquals(GLYPH_1EM_WIDTH, paint.measureText("c"), 0.0f);
+
+        paint.setElegantTextHeight(false);
+        assertEquals(GLYPH_1EM_WIDTH, paint.measureText("a"), 0.0f);
+        assertEquals(GLYPH_3EM_WIDTH, paint.measureText("b"), 0.0f);
+        assertEquals(GLYPH_1EM_WIDTH, paint.measureText("c"), 0.0f);
+    }
+
+    @Test
+    public void testBuildSystemFallback_ElegantFallback_customFallback() {
+        final String xml = "<?xml version='1.0' encoding='UTF-8'?>"
+                + "<familyset version='22'>"
+                + "  <family name='sans-serif'>"
+                + "    <font weight='400' style='normal'>no_coverage.ttf</font>"
+                + "  </family>"
+                + "  <family name='serif'>"
+                + "    <font weight='400' style='normal'>no_coverage.ttf</font>"
+                + "  </family>"
+                + "  <family variant='elegant'>"
+                + "    <font weight='400' style='normal'>a3em.ttf</font>"
+                + "    <font weight='400' style='normal' fallbackFor='serif'>b3em.ttf</font>"
+                + "  </family>"
+                + "  <family variant='compact'>"
+                + "    <font weight='400' style='normal'>c3em.ttf</font>"
+                + "  </family>"
+                + "</familyset>";
+        final ArrayMap<String, Typeface> fontMap = new ArrayMap<>();
+        final ArrayMap<String, FontFamily[]> fallbackMap = new ArrayMap<>();
+
+        buildSystemFallback(xml, fontMap, fallbackMap);
+
+        final Paint paint = new Paint();
+
+        Typeface testTypeface = fontMap.get("serif");
+        assertNotNull(testTypeface);
+        paint.setTypeface(testTypeface);
+        paint.setElegantTextHeight(true);
+        assertEquals(GLYPH_1EM_WIDTH, paint.measureText("a"), 0.0f);
+        assertEquals(GLYPH_3EM_WIDTH, paint.measureText("b"), 0.0f);
+        assertEquals(GLYPH_1EM_WIDTH, paint.measureText("c"), 0.0f);
+
+        paint.setElegantTextHeight(false);
+        assertEquals(GLYPH_1EM_WIDTH, paint.measureText("a"), 0.0f);
+        assertEquals(GLYPH_1EM_WIDTH, paint.measureText("b"), 0.0f);
+        assertEquals(GLYPH_3EM_WIDTH, paint.measureText("c"), 0.0f);
+
+        testTypeface = fontMap.get("sans-serif");
+        assertNotNull(testTypeface);
+        paint.setTypeface(testTypeface);
+        paint.setElegantTextHeight(true);
+        assertEquals(GLYPH_3EM_WIDTH, paint.measureText("a"), 0.0f);
+        assertEquals(GLYPH_1EM_WIDTH, paint.measureText("b"), 0.0f);
+        assertEquals(GLYPH_1EM_WIDTH, paint.measureText("c"), 0.0f);
+
+        paint.setElegantTextHeight(false);
+        assertEquals(GLYPH_1EM_WIDTH, paint.measureText("a"), 0.0f);
+        assertEquals(GLYPH_1EM_WIDTH, paint.measureText("b"), 0.0f);
+        assertEquals(GLYPH_3EM_WIDTH, paint.measureText("c"), 0.0f);
+    }
+}
diff --git a/core/tests/coretests/src/android/provider/SettingsBackupTest.java b/core/tests/coretests/src/android/provider/SettingsBackupTest.java
index 5af2396..949fd16 100644
--- a/core/tests/coretests/src/android/provider/SettingsBackupTest.java
+++ b/core/tests/coretests/src/android/provider/SettingsBackupTest.java
@@ -158,6 +158,7 @@
                     Settings.Global.DEBUG_VIEW_ATTRIBUTES,
                     Settings.Global.DEFAULT_DNS_SERVER,
                     Settings.Global.DEFAULT_INSTALL_LOCATION,
+                    Settings.Global.DEFAULT_RESTRICT_BACKGROUND_DATA,
                     Settings.Global.DESK_DOCK_SOUND,
                     Settings.Global.DESK_UNDOCK_SOUND,
                     Settings.Global.DEVELOPMENT_ENABLE_FREEFORM_WINDOWS_SUPPORT,
@@ -436,6 +437,7 @@
                  Settings.Secure.DISABLED_SYSTEM_INPUT_METHODS,
                  Settings.Secure.DISPLAY_DENSITY_FORCED,
                  Settings.Secure.DOZE_ALWAYS_ON,
+                 Settings.Secure.DOZE_PULSE_ON_LONG_PRESS,
                  Settings.Secure.EMERGENCY_ASSISTANCE_APPLICATION,
                  Settings.Secure.ENABLED_NOTIFICATION_ASSISTANT,
                  Settings.Secure.ENABLED_NOTIFICATION_LISTENERS,
diff --git a/core/tests/coretests/src/android/widget/TextViewActivityTest.java b/core/tests/coretests/src/android/widget/TextViewActivityTest.java
index d69b1e4..56c72d2 100644
--- a/core/tests/coretests/src/android/widget/TextViewActivityTest.java
+++ b/core/tests/coretests/src/android/widget/TextViewActivityTest.java
@@ -786,7 +786,7 @@
 
     @Test
     public void testAssistItemIsAtIndexZero() throws Throwable {
-        mActivity.getSystemService(TextClassificationManager.class).setTextClassifier(null);
+        useSystemDefaultTextClassifier();
         final TextView textView = mActivity.findViewById(R.id.textview);
         mActivityRule.runOnUiThread(() -> textView.setCustomSelectionActionModeCallback(
                 new ActionMode.Callback() {
@@ -822,6 +822,23 @@
     }
 
     @Test
+    public void testNoAssistItemForPasswordField() throws Throwable {
+        useSystemDefaultTextClassifier();
+        final TextView textView = mActivity.findViewById(R.id.textview);
+        mActivityRule.runOnUiThread(() -> {
+            textView.setInputType(
+                    InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_PASSWORD);
+        });
+        mInstrumentation.waitForIdleSync();
+        final String password = "afigbo@android.com";
+
+        onView(withId(R.id.textview)).perform(replaceText(password));
+        onView(withId(R.id.textview)).perform(longPressOnTextAtIndex(password.indexOf('@')));
+        sleepForFloatingToolbarPopup();
+        assertFloatingToolbarDoesNotContainItem(android.R.id.textAssist);
+    }
+
+    @Test
     public void testPastePlainText_menuAction() {
         initializeClipboardWithText(TextStyle.STYLED);
 
@@ -848,6 +865,10 @@
                 mActivity.getString(com.android.internal.R.string.paste_as_plain_text));
     }
 
+    private void useSystemDefaultTextClassifier() {
+        mActivity.getSystemService(TextClassificationManager.class).setTextClassifier(null);
+    }
+
     private void initializeClipboardWithText(TextStyle textStyle) {
         final ClipData clip;
         switch (textStyle) {
diff --git a/core/tests/coretests/src/android/widget/espresso/FloatingToolbarEspressoUtils.java b/core/tests/coretests/src/android/widget/espresso/FloatingToolbarEspressoUtils.java
index 5206c9b..3825e3f 100644
--- a/core/tests/coretests/src/android/widget/espresso/FloatingToolbarEspressoUtils.java
+++ b/core/tests/coretests/src/android/widget/espresso/FloatingToolbarEspressoUtils.java
@@ -23,29 +23,31 @@
 import static android.support.test.espresso.matcher.ViewMatchers.hasDescendant;
 import static android.support.test.espresso.matcher.ViewMatchers.isDisplayed;
 import static android.support.test.espresso.matcher.ViewMatchers.isRoot;
-import static android.support.test.espresso.matcher.ViewMatchers.withTagValue;
 import static android.support.test.espresso.matcher.ViewMatchers.withId;
+import static android.support.test.espresso.matcher.ViewMatchers.withTagValue;
 import static android.support.test.espresso.matcher.ViewMatchers.withText;
+
 import static org.hamcrest.Matchers.allOf;
 import static org.hamcrest.Matchers.is;
 
-import android.view.MenuItem;
-import android.view.ViewGroup;
-import java.util.ArrayList;
-import java.util.List;
-import org.hamcrest.Description;
-import org.hamcrest.Matcher;
-import org.hamcrest.TypeSafeMatcher;
-
 import android.support.test.espresso.NoMatchingRootException;
 import android.support.test.espresso.NoMatchingViewException;
 import android.support.test.espresso.UiController;
 import android.support.test.espresso.ViewAction;
 import android.support.test.espresso.ViewInteraction;
+import android.view.MenuItem;
 import android.view.View;
+import android.view.ViewGroup;
 
 import com.android.internal.widget.FloatingToolbar;
 
+import org.hamcrest.Description;
+import org.hamcrest.Matcher;
+import org.hamcrest.TypeSafeMatcher;
+
+import java.util.ArrayList;
+import java.util.List;
+
 /**
  * Espresso utility methods for the floating toolbar.
  */
@@ -177,6 +179,39 @@
     }
 
     /**
+     * Asserts that the floating toolbar does not contain a menu item with the specified id.
+     *
+     * @param menuItemId id of the menu item
+     * @throws AssertionError if the assertion fails
+     */
+    public static void assertFloatingToolbarDoesNotContainItem(final int menuItemId) {
+        onFloatingToolBar().check(matches(new TypeSafeMatcher<View>() {
+            @Override
+            public boolean matchesSafely(View view) {
+                return !hasMenuItemWithSpecifiedId(view);
+            }
+
+            @Override
+            public void describeTo(Description description) {}
+
+            private boolean hasMenuItemWithSpecifiedId(View view) {
+                if (view.getTag() instanceof MenuItem
+                        && ((MenuItem) view.getTag()).getItemId() == menuItemId) {
+                    return true;
+                } else if (view instanceof ViewGroup) {
+                    ViewGroup viewGroup = (ViewGroup) view;
+                    for (int i = 0; i < viewGroup.getChildCount(); i++) {
+                        if (hasMenuItemWithSpecifiedId(viewGroup.getChildAt(i))) {
+                            return true;
+                        }
+                    }
+                }
+                return false;
+            }
+        }));
+    }
+
+    /**
      * Click specified item on the floating tool bar.
      *
      * @param itemLabel label of the item.
diff --git a/core/tests/coretests/src/com/android/internal/os/BatteryStatsNoteTest.java b/core/tests/coretests/src/com/android/internal/os/BatteryStatsNoteTest.java
index 4e8ab31..fa3d34a 100644
--- a/core/tests/coretests/src/com/android/internal/os/BatteryStatsNoteTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/BatteryStatsNoteTest.java
@@ -25,8 +25,22 @@
 
 import junit.framework.TestCase;
 
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
 /**
  * Test various BatteryStatsImpl noteStart methods.
+ *
+ * Build/Install/Run: bit FrameworksCoreTests:com.android.internal.os.BatteryStatsNoteTest
+ *
+ * Alternatively,
+ * Build: m FrameworksCoreTests
+ * Install: adb install -r \
+ *      ${ANDROID_PRODUCT_OUT}/data/app/FrameworksCoreTests/FrameworksCoreTests.apk
+ * Run: adb shell am instrument -e class com.android.internal.os.BatteryStatsNoteTest -w \
+ *      com.android.frameworks.coretests/android.support.test.runner.AndroidJUnitRunner
  */
 public class BatteryStatsNoteTest extends TestCase{
     private static final int UID = 10500;
@@ -86,4 +100,95 @@
         assertEquals(220_000, actualTime);
         assertEquals(120_000, bgTime);
     }
+
+
+    /** Test BatteryStatsImpl.noteUidProcessStateLocked. */
+    @SmallTest
+    public void testNoteUidProcessStateLocked() throws Exception {
+        final MockClocks clocks = new MockClocks();
+        MockBatteryStatsImpl bi = new MockBatteryStatsImpl(clocks);
+
+        // map of ActivityManager process states and how long to simulate run time in each state
+        Map<Integer, Integer> stateRuntimeMap = new HashMap<Integer, Integer>();
+        stateRuntimeMap.put(ActivityManager.PROCESS_STATE_TOP, 1111);
+        stateRuntimeMap.put(ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE, 1234);
+        stateRuntimeMap.put(ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE, 2468);
+        stateRuntimeMap.put(ActivityManager.PROCESS_STATE_TOP_SLEEPING, 7531);
+        stateRuntimeMap.put(ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND, 4455);
+        stateRuntimeMap.put(ActivityManager.PROCESS_STATE_TRANSIENT_BACKGROUND, 1337);
+        stateRuntimeMap.put(ActivityManager.PROCESS_STATE_BACKUP, 90210);
+        stateRuntimeMap.put(ActivityManager.PROCESS_STATE_HEAVY_WEIGHT, 911);
+        stateRuntimeMap.put(ActivityManager.PROCESS_STATE_SERVICE, 404);
+        stateRuntimeMap.put(ActivityManager.PROCESS_STATE_RECEIVER, 31459);
+        stateRuntimeMap.put(ActivityManager.PROCESS_STATE_HOME, 1123);
+        stateRuntimeMap.put(ActivityManager.PROCESS_STATE_LAST_ACTIVITY, 5813);
+        stateRuntimeMap.put(ActivityManager.PROCESS_STATE_CACHED_ACTIVITY, 867);
+        stateRuntimeMap.put(ActivityManager.PROCESS_STATE_CACHED_ACTIVITY_CLIENT, 5309);
+        stateRuntimeMap.put(ActivityManager.PROCESS_STATE_CACHED_EMPTY, 42);
+
+        bi.updateTimeBasesLocked(true, false, 0, 0);
+
+        for (Map.Entry<Integer, Integer> entry : stateRuntimeMap.entrySet()) {
+            bi.noteUidProcessStateLocked(UID, entry.getKey());
+            clocks.realtime += entry.getValue();
+            clocks.uptime = clocks.realtime;
+        }
+
+        long actualRunTimeUs;
+        long expectedRunTimeMs;
+        long elapsedTimeUs = clocks.realtime * 1000;
+        BatteryStats.Uid uid = bi.getUidStats().get(UID);
+
+        // compare runtime of process states to the Uid process states they map to
+        actualRunTimeUs = uid.getProcessStateTime(BatteryStats.Uid.PROCESS_STATE_TOP, elapsedTimeUs,
+                STATS_SINCE_CHARGED);
+        expectedRunTimeMs = stateRuntimeMap.get(ActivityManager.PROCESS_STATE_TOP);
+        assertEquals(expectedRunTimeMs * 1000, actualRunTimeUs);
+
+
+        actualRunTimeUs = uid.getProcessStateTime(BatteryStats.Uid.PROCESS_STATE_FOREGROUND_SERVICE,
+                elapsedTimeUs, STATS_SINCE_CHARGED);
+        expectedRunTimeMs = stateRuntimeMap.get(
+                ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE)
+                + stateRuntimeMap.get(ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE);
+        assertEquals(expectedRunTimeMs * 1000, actualRunTimeUs);
+
+
+        actualRunTimeUs = uid.getProcessStateTime(BatteryStats.Uid.PROCESS_STATE_TOP_SLEEPING,
+                elapsedTimeUs, STATS_SINCE_CHARGED);
+        expectedRunTimeMs = stateRuntimeMap.get(ActivityManager.PROCESS_STATE_TOP_SLEEPING);
+        assertEquals(expectedRunTimeMs * 1000, actualRunTimeUs);
+
+
+        actualRunTimeUs = uid.getProcessStateTime(BatteryStats.Uid.PROCESS_STATE_FOREGROUND,
+                elapsedTimeUs, STATS_SINCE_CHARGED);
+        expectedRunTimeMs = stateRuntimeMap.get(ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND);
+        assertEquals(expectedRunTimeMs * 1000, actualRunTimeUs);
+
+
+        actualRunTimeUs = uid.getProcessStateTime(BatteryStats.Uid.PROCESS_STATE_BACKGROUND,
+                elapsedTimeUs, STATS_SINCE_CHARGED);
+        expectedRunTimeMs = stateRuntimeMap.get(ActivityManager.PROCESS_STATE_TRANSIENT_BACKGROUND)
+                + stateRuntimeMap.get(ActivityManager.PROCESS_STATE_BACKUP)
+                + stateRuntimeMap.get(ActivityManager.PROCESS_STATE_HEAVY_WEIGHT)
+                + stateRuntimeMap.get(ActivityManager.PROCESS_STATE_SERVICE)
+                + stateRuntimeMap.get(ActivityManager.PROCESS_STATE_RECEIVER);
+        assertEquals(expectedRunTimeMs * 1000, actualRunTimeUs);
+
+
+        actualRunTimeUs = uid.getProcessStateTime(BatteryStats.Uid.PROCESS_STATE_CACHED,
+                elapsedTimeUs, STATS_SINCE_CHARGED);
+        expectedRunTimeMs = stateRuntimeMap.get(ActivityManager.PROCESS_STATE_HOME)
+                + stateRuntimeMap.get(ActivityManager.PROCESS_STATE_LAST_ACTIVITY)
+                + stateRuntimeMap.get(ActivityManager.PROCESS_STATE_CACHED_ACTIVITY)
+                + stateRuntimeMap.get(ActivityManager.PROCESS_STATE_CACHED_ACTIVITY_CLIENT)
+                + stateRuntimeMap.get(ActivityManager.PROCESS_STATE_CACHED_EMPTY);
+        assertEquals(expectedRunTimeMs * 1000, actualRunTimeUs);
+
+        // Special check for foreground service timer
+        actualRunTimeUs = uid.getForegroundServiceTimer().getTotalTimeLocked(elapsedTimeUs,
+                STATS_SINCE_CHARGED);
+        expectedRunTimeMs = stateRuntimeMap.get(ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE);
+        assertEquals(expectedRunTimeMs * 1000, actualRunTimeUs);
+    }
 }
diff --git a/core/tests/coretests/src/com/android/internal/os/LongSamplingCounterArrayTest.java b/core/tests/coretests/src/com/android/internal/os/LongSamplingCounterArrayTest.java
index 4a23f40..27aec56 100644
--- a/core/tests/coretests/src/com/android/internal/os/LongSamplingCounterArrayTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/LongSamplingCounterArrayTest.java
@@ -114,7 +114,7 @@
     public void testOnTimeStarted() {
         initializeCounterArrayWithDefaultValues();
         mCounterArray.onTimeStarted(0, 0, 0);
-        assertArrayEquals(PLUGGED_COUNTS, mCounterArray.mCounts, "Unexpected counts");
+        assertArrayEquals(COUNTS, mCounterArray.mCounts, "Unexpected counts");
         assertArrayEquals(LOADED_COUNTS, mCounterArray.mLoadedCounts, "Unexpected loadedCounts");
         assertArrayEquals(PLUGGED_COUNTS, mCounterArray.mPluggedCounts, "Unexpected pluggedCounts");
         assertArrayEquals(PLUGGED_COUNTS, mCounterArray.mUnpluggedCounts,
@@ -150,6 +150,7 @@
     @Test
     public void testAddCountLocked() {
         final long[] deltas = {123, 234, 345, 456};
+        when(mTimeBase.isRunning()).thenReturn(true);
         mCounterArray.addCountLocked(deltas);
         assertArrayEquals(deltas, mCounterArray.mCounts, "Unexpected counts");
         assertArrayEquals(null, mCounterArray.mLoadedCounts, "Unexpected loadedCounts");
diff --git a/core/tests/utiltests/AndroidManifest.xml b/core/tests/utiltests/AndroidManifest.xml
index da09894..8db81ca 100644
--- a/core/tests/utiltests/AndroidManifest.xml
+++ b/core/tests/utiltests/AndroidManifest.xml
@@ -30,7 +30,6 @@
 
     <uses-permission android:name="android.permission.MANAGE_NETWORK_POLICY" />
     <uses-permission android:name="android.permission.READ_NETWORK_USAGE_HISTORY" />
-    <uses-permission android:name="android.permission.MODIFY_NETWORK_ACCOUNTING" />
     <uses-permission android:name="android.permission.CONNECTIVITY_INTERNAL" />
     <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
     <uses-permission android:name="android.permission.MANAGE_USERS" />
@@ -40,6 +39,7 @@
     <uses-permission android:name="android.permission.INTERNET" />
     <uses-permission android:name="android.permission.CHANGE_NETWORK_STATE" />
     <uses-permission android:name="android.permission.PACKET_KEEPALIVE_OFFLOAD" />
+    <uses-permission android:name="android.permission.ACCESS_KEYGUARD_SECURE_STORAGE" />
 
     <application>
         <uses-library android:name="android.test.runner" />
diff --git a/core/tests/utiltests/src/com/android/internal/util/LockPatternUtilsTest.java b/core/tests/utiltests/src/com/android/internal/util/LockPatternUtilsTest.java
new file mode 100644
index 0000000..b18ee17
--- /dev/null
+++ b/core/tests/utiltests/src/com/android/internal/util/LockPatternUtilsTest.java
@@ -0,0 +1,100 @@
+/*
+ * 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.util;
+
+import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_MANAGED;
+import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.content.ContextWrapper;
+import android.content.pm.UserInfo;
+import android.os.UserManager;
+import android.provider.Settings;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import android.test.mock.MockContentResolver;
+import com.android.internal.util.test.FakeSettingsProvider;
+import com.android.internal.widget.ILockSettings;
+import com.android.internal.widget.LockPatternUtils;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mockito;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class LockPatternUtilsTest {
+
+    private static final int DEMO_USER_ID = 5;
+
+    private LockPatternUtils mLockPatternUtils;
+
+    private void configureTest(boolean isSecure, boolean isDemoUser, int deviceDemoMode)
+            throws Exception {
+        final Context context = spy(new ContextWrapper(InstrumentationRegistry.getTargetContext()));
+
+        final MockContentResolver cr = new MockContentResolver(context);
+        cr.addProvider(Settings.AUTHORITY, new FakeSettingsProvider());
+        when(context.getContentResolver()).thenReturn(cr);
+        Settings.Global.putInt(cr, Settings.Global.DEVICE_DEMO_MODE, deviceDemoMode);
+
+        final ILockSettings ils = Mockito.mock(ILockSettings.class);
+        when(ils.havePassword(DEMO_USER_ID)).thenReturn(isSecure);
+        when(ils.getLong("lockscreen.password_type", PASSWORD_QUALITY_UNSPECIFIED, DEMO_USER_ID))
+                .thenReturn((long) PASSWORD_QUALITY_MANAGED);
+        // TODO(b/63758238): stop spying the class under test
+        mLockPatternUtils = spy(new LockPatternUtils(context));
+        when(mLockPatternUtils.getLockSettings()).thenReturn(ils);
+
+        final UserInfo userInfo = Mockito.mock(UserInfo.class);
+        when(userInfo.isDemo()).thenReturn(isDemoUser);
+        final UserManager um = Mockito.mock(UserManager.class);
+        when(um.getUserInfo(DEMO_USER_ID)).thenReturn(userInfo);
+        when(context.getSystemService(Context.USER_SERVICE)).thenReturn(um);
+    }
+
+    @Test
+    public void isLockScreenDisabled_isDemoUser_true() throws Exception {
+        configureTest(false, true, 2);
+        assertTrue(mLockPatternUtils.isLockScreenDisabled(DEMO_USER_ID));
+    }
+
+    @Test
+    public void isLockScreenDisabled_isSecureAndDemoUser_false() throws Exception {
+        configureTest(true, true, 2);
+        assertFalse(mLockPatternUtils.isLockScreenDisabled(DEMO_USER_ID));
+    }
+
+    @Test
+    public void isLockScreenDisabled_isNotDemoUser_false() throws Exception {
+        configureTest(false, false, 2);
+        assertFalse(mLockPatternUtils.isLockScreenDisabled(DEMO_USER_ID));
+    }
+
+    @Test
+    public void isLockScreenDisabled_isNotInDemoMode_false() throws Exception {
+        configureTest(false, true, 0);
+        assertFalse(mLockPatternUtils.isLockScreenDisabled(DEMO_USER_ID));
+    }
+}
diff --git a/data/etc/platform.xml b/data/etc/platform.xml
index 85ea1ff..19007f9 100644
--- a/data/etc/platform.xml
+++ b/data/etc/platform.xml
@@ -93,7 +93,7 @@
     </permission>
 
     <!-- Group that can modify how network statistics are accounted -->
-    <permission name="android.permission.MODIFY_NETWORK_ACCOUNTING">
+    <permission name="android.permission.UPDATE_DEVICE_STATS">
         <group gid="net_bw_acct" />
     </permission>
 
diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml
index a79376c..e9e2e8a 100644
--- a/data/etc/privapp-permissions-platform.xml
+++ b/data/etc/privapp-permissions-platform.xml
@@ -186,7 +186,6 @@
         <permission name="android.permission.ACCESS_CACHE_FILESYSTEM"/>
         <permission name="android.permission.CLEAR_APP_CACHE"/>
         <permission name="android.permission.CONNECTIVITY_INTERNAL"/>
-        <permission name="android.permission.MODIFY_NETWORK_ACCOUNTING"/>
         <permission name="android.permission.UPDATE_APP_OPS_STATS"/>
         <permission name="android.permission.UPDATE_DEVICE_STATS"/>
     </privapp-permissions>
diff --git a/graphics/java/android/graphics/Bitmap.java b/graphics/java/android/graphics/Bitmap.java
index d586db4..57c7549 100644
--- a/graphics/java/android/graphics/Bitmap.java
+++ b/graphics/java/android/graphics/Bitmap.java
@@ -21,6 +21,7 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.Size;
+import android.content.res.ResourcesImpl;
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.os.StrictMode;
@@ -82,6 +83,12 @@
 
     private static volatile int sDefaultDensity = -1;
 
+    /** @hide Used only when ResourcesImpl.TRACE_FOR_DETAILED_PRELOAD is true. */
+    public static volatile int sPreloadTracingNumInstantiatedBitmaps;
+
+    /** @hide Used only when ResourcesImpl.TRACE_FOR_DETAILED_PRELOAD is true. */
+    public static volatile long sPreloadTracingTotalBitmapsSize;
+
     /**
      * For backwards compatibility, allows the app layer to change the default
      * density when running old apps.
@@ -128,6 +135,11 @@
         NativeAllocationRegistry registry = new NativeAllocationRegistry(
             Bitmap.class.getClassLoader(), nativeGetNativeFinalizer(), nativeSize);
         registry.registerNativeAllocation(this, nativeBitmap);
+
+        if (ResourcesImpl.TRACE_FOR_DETAILED_PRELOAD) {
+            sPreloadTracingNumInstantiatedBitmaps++;
+            sPreloadTracingTotalBitmapsSize += nativeSize;
+        }
     }
 
     /**
diff --git a/graphics/java/android/graphics/FontListParser.java b/graphics/java/android/graphics/FontListParser.java
index 7c07a30..80a9324 100644
--- a/graphics/java/android/graphics/FontListParser.java
+++ b/graphics/java/android/graphics/FontListParser.java
@@ -111,6 +111,7 @@
         String weightStr = parser.getAttributeValue(null, "weight");
         int weight = weightStr == null ? 400 : Integer.parseInt(weightStr);
         boolean isItalic = "italic".equals(parser.getAttributeValue(null, "style"));
+        String fallbackFor = parser.getAttributeValue(null, "fallbackFor");
         StringBuilder filename = new StringBuilder();
         while (parser.next() != XmlPullParser.END_TAG) {
             if (parser.getEventType() == XmlPullParser.TEXT) {
@@ -126,7 +127,7 @@
         }
         String sanitizedName = FILENAME_WHITESPACE_PATTERN.matcher(filename).replaceAll("");
         return new FontConfig.Font(sanitizedName, index,
-                axes.toArray(new FontVariationAxis[axes.size()]), weight, isItalic);
+                axes.toArray(new FontVariationAxis[axes.size()]), weight, isItalic, fallbackFor);
     }
 
     private static FontVariationAxis readAxis(XmlPullParser parser)
diff --git a/graphics/java/android/graphics/Paint.java b/graphics/java/android/graphics/Paint.java
index 1fd5697..aa9227c 100644
--- a/graphics/java/android/graphics/Paint.java
+++ b/graphics/java/android/graphics/Paint.java
@@ -852,6 +852,23 @@
     }
 
     /**
+     * Distance from top of the strike-through line to the baseline. Negative values mean above the
+     * baseline. This method returns where the strike-through line should be drawn independent of if
+     * the strikeThruText bit is set at the moment.
+     * @hide
+     */
+    public float getStrikeThruPosition() {
+        return nGetStrikeThruPosition(mNativePaint, mNativeTypeface);
+    }
+
+    /**
+     * @hide
+     */
+    public float getStrikeThruThickness() {
+        return nGetStrikeThruThickness(mNativePaint, mNativeTypeface);
+    }
+
+    /**
      * Helper for setFlags(), setting or clearing the STRIKE_THRU_TEXT_FLAG bit
      *
      * @param strikeThruText true to set the strikeThruText bit in the paint's
@@ -2997,5 +3014,9 @@
     @CriticalNative
     private static native float nGetUnderlineThickness(long paintPtr, long typefacePtr);
     @CriticalNative
+    private static native float nGetStrikeThruPosition(long paintPtr, long typefacePtr);
+    @CriticalNative
+    private static native float nGetStrikeThruThickness(long paintPtr, long typefacePtr);
+    @CriticalNative
     private static native void nSetTextSize(long paintPtr, float textSize);
 }
diff --git a/graphics/java/android/graphics/Typeface.java b/graphics/java/android/graphics/Typeface.java
index c4b56c3..1d8b583 100644
--- a/graphics/java/android/graphics/Typeface.java
+++ b/graphics/java/android/graphics/Typeface.java
@@ -38,6 +38,7 @@
 import android.provider.FontRequest;
 import android.provider.FontsContract;
 import android.text.FontConfig;
+import android.util.ArrayMap;
 import android.util.Base64;
 import android.util.Log;
 import android.util.LongSparseArray;
@@ -45,6 +46,7 @@
 import android.util.SparseArray;
 
 import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.Preconditions;
 
 import libcore.io.IoUtils;
@@ -105,12 +107,10 @@
     private static final LruCache<String, Typeface> sDynamicTypefaceCache = new LruCache<>(16);
 
     static Typeface sDefaultTypeface;
-    static Map<String, Typeface> sSystemFontMap;
-    static FontFamily[] sFallbackFonts;
+    static final Map<String, Typeface> sSystemFontMap;
+    static final Map<String, FontFamily[]> sSystemFallbackMap;
     private static final Object sLock = new Object();
 
-    static final String FONTS_CONFIG = "fonts.xml";
-
     /**
      * @hide
      */
@@ -129,6 +129,7 @@
     // Must be the same as the C++ constant in core/jni/android/graphics/FontFamily.cpp
     /** @hide */
     public static final int RESOLVE_BY_FONT_TABLE = -1;
+    private static final String DEFAULT_FAMILY = "sans-serif";
 
     // Style value for building typeface.
     private static final int STYLE_NORMAL = 0;
@@ -163,28 +164,27 @@
      */
     @Nullable
     public static Typeface createFromResources(AssetManager mgr, String path, int cookie) {
-        if (sFallbackFonts != null) {
-            synchronized (sDynamicTypefaceCache) {
-                final String key = Builder.createAssetUid(
-                        mgr, path, 0 /* ttcIndex */, null /* axes */,
-                        RESOLVE_BY_FONT_TABLE /* weight */, RESOLVE_BY_FONT_TABLE /* italic */);
-                Typeface typeface = sDynamicTypefaceCache.get(key);
-                if (typeface != null) return typeface;
+        synchronized (sDynamicTypefaceCache) {
+            final String key = Builder.createAssetUid(
+                    mgr, path, 0 /* ttcIndex */, null /* axes */,
+                    RESOLVE_BY_FONT_TABLE /* weight */, RESOLVE_BY_FONT_TABLE /* italic */,
+                    DEFAULT_FAMILY);
+            Typeface typeface = sDynamicTypefaceCache.get(key);
+            if (typeface != null) return typeface;
 
-                FontFamily fontFamily = new FontFamily();
-                // TODO: introduce ttc index and variation settings to resource type font.
-                if (fontFamily.addFontFromAssetManager(mgr, path, cookie, false /* isAsset */,
-                        0 /* ttcIndex */, RESOLVE_BY_FONT_TABLE /* weight */,
-                        RESOLVE_BY_FONT_TABLE /* italic */, null /* axes */)) {
-                    if (!fontFamily.freeze()) {
-                        return null;
-                    }
-                    FontFamily[] families = {fontFamily};
-                    typeface = createFromFamiliesWithDefault(families,
-                            RESOLVE_BY_FONT_TABLE, RESOLVE_BY_FONT_TABLE);
-                    sDynamicTypefaceCache.put(key, typeface);
-                    return typeface;
+            FontFamily fontFamily = new FontFamily();
+            // TODO: introduce ttc index and variation settings to resource type font.
+            if (fontFamily.addFontFromAssetManager(mgr, path, cookie, false /* isAsset */,
+                    0 /* ttcIndex */, RESOLVE_BY_FONT_TABLE /* weight */,
+                    RESOLVE_BY_FONT_TABLE /* italic */, null /* axes */)) {
+                if (!fontFamily.freeze()) {
+                    return null;
                 }
+                FontFamily[] families = {fontFamily};
+                typeface = createFromFamiliesWithDefault(families, DEFAULT_FAMILY,
+                        RESOLVE_BY_FONT_TABLE, RESOLVE_BY_FONT_TABLE);
+                sDynamicTypefaceCache.put(key, typeface);
+                return typeface;
             }
         }
         return null;
@@ -197,61 +197,57 @@
     @Nullable
     public static Typeface createFromResources(
             FamilyResourceEntry entry, AssetManager mgr, String path) {
-        if (sFallbackFonts != null) {
-            if (entry instanceof ProviderResourceEntry) {
-                final ProviderResourceEntry providerEntry = (ProviderResourceEntry) entry;
-                // Downloadable font
-                List<List<String>> givenCerts = providerEntry.getCerts();
-                List<List<byte[]>> certs = new ArrayList<>();
-                if (givenCerts != null) {
-                    for (int i = 0; i < givenCerts.size(); i++) {
-                        List<String> certSet = givenCerts.get(i);
-                        List<byte[]> byteArraySet = new ArrayList<>();
-                        for (int j = 0; j < certSet.size(); j++) {
-                            byteArraySet.add(Base64.decode(certSet.get(j), Base64.DEFAULT));
-                        }
-                        certs.add(byteArraySet);
+        if (entry instanceof ProviderResourceEntry) {
+            final ProviderResourceEntry providerEntry = (ProviderResourceEntry) entry;
+            // Downloadable font
+            List<List<String>> givenCerts = providerEntry.getCerts();
+            List<List<byte[]>> certs = new ArrayList<>();
+            if (givenCerts != null) {
+                for (int i = 0; i < givenCerts.size(); i++) {
+                    List<String> certSet = givenCerts.get(i);
+                    List<byte[]> byteArraySet = new ArrayList<>();
+                    for (int j = 0; j < certSet.size(); j++) {
+                        byteArraySet.add(Base64.decode(certSet.get(j), Base64.DEFAULT));
                     }
-                }
-                // Downloaded font and it wasn't cached, request it again and return a
-                // default font instead (nothing we can do now).
-                FontRequest request = new FontRequest(providerEntry.getAuthority(),
-                        providerEntry.getPackage(), providerEntry.getQuery(), certs);
-                Typeface typeface = FontsContract.getFontSync(request);
-                return typeface == null ? DEFAULT : typeface;
-            }
-
-            Typeface typeface = findFromCache(mgr, path);
-            if (typeface != null) return typeface;
-
-            // family is FontFamilyFilesResourceEntry
-            final FontFamilyFilesResourceEntry filesEntry =
-                    (FontFamilyFilesResourceEntry) entry;
-
-            FontFamily fontFamily = new FontFamily();
-            for (final FontFileResourceEntry fontFile : filesEntry.getEntries()) {
-                // TODO: Add ttc and variation font support. (b/37853920)
-                if (!fontFamily.addFontFromAssetManager(mgr, fontFile.getFileName(),
-                        0 /* resourceCookie */, false /* isAsset */, 0 /* ttcIndex */,
-                        fontFile.getWeight(), fontFile.getItalic(), null /* axes */)) {
-                    return null;
+                    certs.add(byteArraySet);
                 }
             }
-            if (!fontFamily.freeze()) {
+            // Downloaded font and it wasn't cached, request it again and return a
+            // default font instead (nothing we can do now).
+            FontRequest request = new FontRequest(providerEntry.getAuthority(),
+                    providerEntry.getPackage(), providerEntry.getQuery(), certs);
+            Typeface typeface = FontsContract.getFontSync(request);
+            return typeface == null ? DEFAULT : typeface;
+        }
+
+        Typeface typeface = findFromCache(mgr, path);
+        if (typeface != null) return typeface;
+
+        // family is FontFamilyFilesResourceEntry
+        final FontFamilyFilesResourceEntry filesEntry = (FontFamilyFilesResourceEntry) entry;
+
+        FontFamily fontFamily = new FontFamily();
+        for (final FontFileResourceEntry fontFile : filesEntry.getEntries()) {
+            // TODO: Add ttc and variation font support. (b/37853920)
+            if (!fontFamily.addFontFromAssetManager(mgr, fontFile.getFileName(),
+                    0 /* resourceCookie */, false /* isAsset */, 0 /* ttcIndex */,
+                    fontFile.getWeight(), fontFile.getItalic(), null /* axes */)) {
                 return null;
             }
-            FontFamily[] familyChain = { fontFamily };
-            typeface = createFromFamiliesWithDefault(familyChain,
-                    RESOLVE_BY_FONT_TABLE, RESOLVE_BY_FONT_TABLE);
-            synchronized (sDynamicTypefaceCache) {
-                final String key = Builder.createAssetUid(mgr, path, 0 /* ttcIndex */,
-                        null /* axes */, RESOLVE_BY_FONT_TABLE /* weight */,
-                        RESOLVE_BY_FONT_TABLE /* italic */);
-                sDynamicTypefaceCache.put(key, typeface);
-            }
-            return typeface;
         }
-        return null;
+        if (!fontFamily.freeze()) {
+            return null;
+        }
+        FontFamily[] familyChain = { fontFamily };
+        typeface = createFromFamiliesWithDefault(familyChain, DEFAULT_FAMILY,
+                RESOLVE_BY_FONT_TABLE, RESOLVE_BY_FONT_TABLE);
+        synchronized (sDynamicTypefaceCache) {
+            final String key = Builder.createAssetUid(mgr, path, 0 /* ttcIndex */,
+                    null /* axes */, RESOLVE_BY_FONT_TABLE /* weight */,
+                    RESOLVE_BY_FONT_TABLE /* italic */, DEFAULT_FAMILY);
+            sDynamicTypefaceCache.put(key, typeface);
+        }
+        return typeface;
     }
 
     /**
@@ -261,7 +257,8 @@
     public static Typeface findFromCache(AssetManager mgr, String path) {
         synchronized (sDynamicTypefaceCache) {
             final String key = Builder.createAssetUid(mgr, path, 0 /* ttcIndex */, null /* axes */,
-                    RESOLVE_BY_FONT_TABLE /* weight */, RESOLVE_BY_FONT_TABLE /* italic */);
+                    RESOLVE_BY_FONT_TABLE /* weight */, RESOLVE_BY_FONT_TABLE /* italic */,
+                    DEFAULT_FAMILY);
             Typeface typeface = sDynamicTypefaceCache.get(key);
             if (typeface != null) {
                 return typeface;
@@ -498,7 +495,7 @@
          * @return Unique id for a given AssetManager and asset path.
          */
         private static String createAssetUid(final AssetManager mgr, String path, int ttcIndex,
-                @Nullable FontVariationAxis[] axes, int weight, int italic) {
+                @Nullable FontVariationAxis[] axes, int weight, int italic, String fallback) {
             final SparseArray<String> pkgs = mgr.getAssignedPackageIdentifiers();
             final StringBuilder builder = new StringBuilder();
             final int size = pkgs.size();
@@ -513,7 +510,11 @@
             builder.append(Integer.toString(weight));
             builder.append("-");
             builder.append(Integer.toString(italic));
-            builder.append("-");
+            // Family name may contain hyphen. Use double hyphen for avoiding key conflicts before
+            // and after appending falblack name.
+            builder.append("--");
+            builder.append(fallback);
+            builder.append("--");
             if (axes != null) {
                 for (FontVariationAxis axis : axes) {
                     builder.append(axis.getTag());
@@ -593,13 +594,15 @@
                         return resolveFallbackTypeface();
                     }
                     FontFamily[] families = { fontFamily };
-                    return createFromFamiliesWithDefault(families, mWeight, mItalic);
+                    return createFromFamiliesWithDefault(families, mFallbackFamilyName, mWeight,
+                            mItalic);
                 } catch (IOException e) {
                     return resolveFallbackTypeface();
                 }
             } else if (mAssetManager != null) {  // Builder is created with asset manager.
                 final String key = createAssetUid(
-                        mAssetManager, mPath, mTtcIndex, mAxes, mWeight, mItalic);
+                        mAssetManager, mPath, mTtcIndex, mAxes, mWeight, mItalic,
+                        mFallbackFamilyName);
                 synchronized (sLock) {
                     Typeface typeface = sDynamicTypefaceCache.get(key);
                     if (typeface != null) return typeface;
@@ -613,7 +616,8 @@
                         return resolveFallbackTypeface();
                     }
                     FontFamily[] families = { fontFamily };
-                    typeface = createFromFamiliesWithDefault(families, mWeight, mItalic);
+                    typeface = createFromFamiliesWithDefault(families, mFallbackFamilyName,
+                            mWeight, mItalic);
                     sDynamicTypefaceCache.put(key, typeface);
                     return typeface;
                 }
@@ -627,7 +631,8 @@
                     return resolveFallbackTypeface();
                 }
                 FontFamily[] families = { fontFamily };
-                return createFromFamiliesWithDefault(families, mWeight, mItalic);
+                return createFromFamiliesWithDefault(families, mFallbackFamilyName, mWeight,
+                        mItalic);
             } else if (mFonts != null) {
                 final FontFamily fontFamily = new FontFamily();
                 boolean atLeastOneFont = false;
@@ -653,7 +658,8 @@
                 }
                 fontFamily.freeze();
                 FontFamily[] families = { fontFamily };
-                return createFromFamiliesWithDefault(families, mWeight, mItalic);
+                return createFromFamiliesWithDefault(families, mFallbackFamilyName, mWeight,
+                        mItalic);
             }
 
             // Must not reach here.
@@ -673,10 +679,7 @@
      * @return The best matching typeface.
      */
     public static Typeface create(String familyName, int style) {
-        if (sSystemFontMap != null) {
-            return create(sSystemFontMap.get(familyName), style);
-        }
-        return null;
+        return create(sSystemFontMap.get(familyName), style);
     }
 
     /**
@@ -751,34 +754,33 @@
         if (path == null) {
             throw new NullPointerException();  // for backward compatibility
         }
-        if (sFallbackFonts != null) {
-            synchronized (sLock) {
-                Typeface typeface = new Builder(mgr, path).build();
-                if (typeface != null) return typeface;
+        synchronized (sLock) {
+            Typeface typeface = new Builder(mgr, path).build();
+            if (typeface != null) return typeface;
 
-                final String key = Builder.createAssetUid(mgr, path, 0 /* ttcIndex */,
-                        null /* axes */, RESOLVE_BY_FONT_TABLE, RESOLVE_BY_FONT_TABLE);
-                typeface = sDynamicTypefaceCache.get(key);
-                if (typeface != null) return typeface;
+            final String key = Builder.createAssetUid(mgr, path, 0 /* ttcIndex */,
+                    null /* axes */, RESOLVE_BY_FONT_TABLE, RESOLVE_BY_FONT_TABLE,
+                    DEFAULT_FAMILY);
+            typeface = sDynamicTypefaceCache.get(key);
+            if (typeface != null) return typeface;
 
-                final FontFamily fontFamily = new FontFamily();
-                if (fontFamily.addFontFromAssetManager(mgr, path, 0, true /* isAsset */,
-                        0 /* ttc index */, RESOLVE_BY_FONT_TABLE, RESOLVE_BY_FONT_TABLE,
-                        null /* axes */)) {
-                    // Due to backward compatibility, even if the font is not supported by our font
-                    // stack, we need to place the empty font at the first place. The typeface with
-                    // empty font behaves different from default typeface especially in fallback
-                    // font selection.
-                    fontFamily.allowUnsupportedFont();
-                    fontFamily.freeze();
-                    final FontFamily[] families = { fontFamily };
-                    typeface = createFromFamiliesWithDefault(families, RESOLVE_BY_FONT_TABLE,
-                            RESOLVE_BY_FONT_TABLE);
-                    sDynamicTypefaceCache.put(key, typeface);
-                    return typeface;
-                } else {
-                    fontFamily.abortCreation();
-                }
+            final FontFamily fontFamily = new FontFamily();
+            if (fontFamily.addFontFromAssetManager(mgr, path, 0, true /* isAsset */,
+                    0 /* ttc index */, RESOLVE_BY_FONT_TABLE, RESOLVE_BY_FONT_TABLE,
+                    null /* axes */)) {
+                // Due to backward compatibility, even if the font is not supported by our font
+                // stack, we need to place the empty font at the first place. The typeface with
+                // empty font behaves different from default typeface especially in fallback
+                // font selection.
+                fontFamily.allowUnsupportedFont();
+                fontFamily.freeze();
+                final FontFamily[] families = { fontFamily };
+                typeface = createFromFamiliesWithDefault(families, DEFAULT_FAMILY,
+                        RESOLVE_BY_FONT_TABLE, RESOLVE_BY_FONT_TABLE);
+                sDynamicTypefaceCache.put(key, typeface);
+                return typeface;
+            } else {
+                fontFamily.abortCreation();
             }
         }
         throw new RuntimeException("Font asset not found " + path);
@@ -815,22 +817,20 @@
      * @return The new typeface.
      */
     public static Typeface createFromFile(@Nullable String path) {
-        if (sFallbackFonts != null) {
-            final FontFamily fontFamily = new FontFamily();
-            if (fontFamily.addFont(path, 0 /* ttcIndex */, null /* axes */,
-                      RESOLVE_BY_FONT_TABLE, RESOLVE_BY_FONT_TABLE)) {
-                // Due to backward compatibility, even if the font is not supported by our font
-                // stack, we need to place the empty font at the first place. The typeface with
-                // empty font behaves different from default typeface especially in fallback font
-                // selection.
-                fontFamily.allowUnsupportedFont();
-                fontFamily.freeze();
-                FontFamily[] families = { fontFamily };
-                return createFromFamiliesWithDefault(families, RESOLVE_BY_FONT_TABLE,
-                        RESOLVE_BY_FONT_TABLE);
-            } else {
-                fontFamily.abortCreation();
-            }
+        final FontFamily fontFamily = new FontFamily();
+        if (fontFamily.addFont(path, 0 /* ttcIndex */, null /* axes */,
+                  RESOLVE_BY_FONT_TABLE, RESOLVE_BY_FONT_TABLE)) {
+            // Due to backward compatibility, even if the font is not supported by our font
+            // stack, we need to place the empty font at the first place. The typeface with
+            // empty font behaves different from default typeface especially in fallback font
+            // selection.
+            fontFamily.allowUnsupportedFont();
+            fontFamily.freeze();
+            FontFamily[] families = { fontFamily };
+            return createFromFamiliesWithDefault(families, DEFAULT_FAMILY,
+                    RESOLVE_BY_FONT_TABLE, RESOLVE_BY_FONT_TABLE);
+        } else {
+            fontFamily.abortCreation();
         }
         throw new RuntimeException("Font not found " + path);
     }
@@ -852,6 +852,8 @@
     /**
      * Create a new typeface from an array of font families, including
      * also the font families in the fallback list.
+     * @param fallbackName the family name. If given families don't support characters, the
+     *               characters will be rendered with this family.
      * @param weight the weight for this family. {@link RESOLVE_BY_FONT_TABLE} can be used. In that
      *               case, the table information in the first family's font is used. If the first
      *               family has multiple fonts, the closest to the regular weight and upright font
@@ -863,13 +865,17 @@
      * @param families array of font families
      */
     private static Typeface createFromFamiliesWithDefault(FontFamily[] families,
-                int weight, int italic) {
-        long[] ptrArray = new long[families.length + sFallbackFonts.length];
+                String fallbackName, int weight, int italic) {
+        FontFamily[] fallback = sSystemFallbackMap.get(fallbackName);
+        if (fallback == null) {
+            fallback = sSystemFallbackMap.get(DEFAULT_FAMILY);
+        }
+        long[] ptrArray = new long[families.length + fallback.length];
         for (int i = 0; i < families.length; i++) {
             ptrArray[i] = families[i].mNativePtr;
         }
-        for (int i = 0; i < sFallbackFonts.length; i++) {
-            ptrArray[i + families.length] = sFallbackFonts[i].mNativePtr;
+        for (int i = 0; i < fallback.length; i++) {
+            ptrArray[i + families.length] = fallback[i].mNativePtr;
         }
         return new Typeface(nativeCreateFromArray(ptrArray, weight, italic));
     }
@@ -885,113 +891,189 @@
         mWeight = nativeGetWeight(ni);
     }
 
-    private static FontFamily makeFamilyFromParsed(FontConfig.Family family,
-            Map<String, ByteBuffer> bufferForPath) {
-        FontFamily fontFamily = new FontFamily(family.getLanguage(), family.getVariant());
-        for (FontConfig.Font font : family.getFonts()) {
-            String fullPathName = "/system/fonts/" + font.getFontName();
-            ByteBuffer fontBuffer = bufferForPath.get(fullPathName);
-            if (fontBuffer == null) {
-                try (FileInputStream file = new FileInputStream(fullPathName)) {
-                    FileChannel fileChannel = file.getChannel();
-                    long fontSize = fileChannel.size();
-                    fontBuffer = fileChannel.map(FileChannel.MapMode.READ_ONLY, 0, fontSize);
-                    bufferForPath.put(fullPathName, fontBuffer);
-                } catch (IOException e) {
-                    Log.e(TAG, "Error mapping font file " + fullPathName);
+    private static @Nullable ByteBuffer mmap(String fullPath) {
+        try (FileInputStream file = new FileInputStream(fullPath)) {
+            final FileChannel fileChannel = file.getChannel();
+            final long fontSize = fileChannel.size();
+            return fileChannel.map(FileChannel.MapMode.READ_ONLY, 0, fontSize);
+        } catch (IOException e) {
+            Log.e(TAG, "Error mapping font file " + fullPath);
+            return null;
+        }
+    }
+
+    private static @Nullable FontFamily createFontFamily(
+            String familyName, List<FontConfig.Font> fonts, String languageTag, int variant,
+            Map<String, ByteBuffer> cache, String fontDir) {
+        final FontFamily family = new FontFamily(languageTag, variant);
+        for (int i = 0; i < fonts.size(); i++) {
+            final FontConfig.Font font = fonts.get(i);
+            final String fullPath = fontDir + font.getFontName();
+            ByteBuffer buffer = cache.get(fullPath);
+            if (buffer == null) {
+                if (cache.containsKey(fullPath)) {
+                    continue;  // Already failed to mmap. Skip it.
+                }
+                buffer = mmap(fullPath);
+                cache.put(fullPath, buffer);
+                if (buffer == null) {
                     continue;
                 }
             }
-            if (!fontFamily.addFontFromBuffer(fontBuffer, font.getTtcIndex(), font.getAxes(),
+            if (!family.addFontFromBuffer(buffer, font.getTtcIndex(), font.getAxes(),
                     font.getWeight(), font.isItalic() ? STYLE_ITALIC : STYLE_NORMAL)) {
-                Log.e(TAG, "Error creating font " + fullPathName + "#" + font.getTtcIndex());
+                Log.e(TAG, "Error creating font " + fullPath + "#" + font.getTtcIndex());
             }
         }
-        if (!fontFamily.freeze()) {
-            // Treat as system error since reaching here means that a system pre-installed font
-            // can't be used by our font stack.
-            Log.e(TAG, "Unable to load Family: " + family.getName() + ":" + family.getLanguage());
+        if (!family.freeze()) {
+            Log.e(TAG, "Unable to load Family: " + familyName + " : " + languageTag);
             return null;
         }
-        return fontFamily;
+        return family;
     }
 
-    /*
-     * (non-Javadoc)
+    private static void pushFamilyToFallback(FontConfig.Family xmlFamily,
+            ArrayMap<String, ArrayList<FontFamily>> fallbackMap,
+            Map<String, ByteBuffer> cache,
+            String fontDir) {
+
+        final String languageTag = xmlFamily.getLanguage();
+        final int variant = xmlFamily.getVariant();
+
+        final ArrayList<FontConfig.Font> defaultFonts = new ArrayList<>();
+        final ArrayMap<String, ArrayList<FontConfig.Font>> specificFallbackFonts = new ArrayMap<>();
+
+        // Collect default fallback and specific fallback fonts.
+        for (final FontConfig.Font font : xmlFamily.getFonts()) {
+            final String fallbackName = font.getFallbackFor();
+            if (fallbackName == null) {
+                defaultFonts.add(font);
+            } else {
+                ArrayList<FontConfig.Font> fallback = specificFallbackFonts.get(fallbackName);
+                if (fallback == null) {
+                    fallback = new ArrayList<>();
+                    specificFallbackFonts.put(fallbackName, fallback);
+                }
+                fallback.add(font);
+            }
+        }
+
+        final FontFamily defaultFamily = defaultFonts.isEmpty() ? null : createFontFamily(
+                xmlFamily.getName(), defaultFonts, languageTag, variant, cache, fontDir);
+
+        // Insert family into fallback map.
+        for (int i = 0; i < fallbackMap.size(); i++) {
+            final ArrayList<FontConfig.Font> fallback =
+                    specificFallbackFonts.get(fallbackMap.keyAt(i));
+            if (fallback == null) {
+                if (defaultFamily != null) {
+                    fallbackMap.valueAt(i).add(defaultFamily);
+                }
+            } else {
+                final FontFamily family = createFontFamily(
+                        xmlFamily.getName(), fallback, languageTag, variant, cache, fontDir);
+                if (family != null) {
+                    fallbackMap.valueAt(i).add(family);
+                }
+            }
+        }
+    }
+
+    /**
+     * Build the system fallback from xml file.
      *
-     * This should only be called once, from the static class initializer block.
+     * @param xmlPath A full path string to the fonts.xml file.
+     * @param fontDir A full path string to the system font directory. This must end with
+     *                slash('/').
+     * @param fontMap An output system font map. Caller must pass empty map.
+     * @param fallbackMap An output system fallback map. Caller must pass empty map.
+     * @hide
      */
-    private static void init() {
-        // Load font config and initialize Minikin state
-        File systemFontConfigLocation = getSystemFontConfigLocation();
-        File configFilename = new File(systemFontConfigLocation, FONTS_CONFIG);
+    @VisibleForTesting
+    public static void buildSystemFallback(String xmlPath, String fontDir,
+            ArrayMap<String, Typeface> fontMap, ArrayMap<String, FontFamily[]> fallbackMap) {
         try {
-            FileInputStream fontsIn = new FileInputStream(configFilename);
-            FontConfig fontConfig = FontListParser.parse(fontsIn);
+            final FileInputStream fontsIn = new FileInputStream(xmlPath);
+            final FontConfig fontConfig = FontListParser.parse(fontsIn);
 
-            Map<String, ByteBuffer> bufferForPath = new HashMap<String, ByteBuffer>();
+            final HashMap<String, ByteBuffer> bufferCache = new HashMap<String, ByteBuffer>();
+            final FontConfig.Family[] xmlFamilies = fontConfig.getFamilies();
 
-            List<FontFamily> familyList = new ArrayList<FontFamily>();
-            // Note that the default typeface is always present in the fallback list;
-            // this is an enhancement from pre-Minikin behavior.
-            for (int i = 0; i < fontConfig.getFamilies().length; i++) {
-                FontConfig.Family f = fontConfig.getFamilies()[i];
-                if (i == 0 || f.getName() == null) {
-                    FontFamily family = makeFamilyFromParsed(f, bufferForPath);
-                    if (family != null) {
-                        familyList.add(family);
-                    }
+            final ArrayMap<String, ArrayList<FontFamily>> fallbackListMap = new ArrayMap<>();
+            // First traverse families which have a 'name' attribute to create fallback map.
+            for (final FontConfig.Family xmlFamily : xmlFamilies) {
+                final String familyName = xmlFamily.getName();
+                if (familyName == null) {
+                    continue;
+                }
+                final FontFamily family = createFontFamily(
+                        xmlFamily.getName(), Arrays.asList(xmlFamily.getFonts()),
+                        xmlFamily.getLanguage(), xmlFamily.getVariant(), bufferCache, fontDir);
+                if (family == null) {
+                    continue;
+                }
+                final ArrayList<FontFamily> fallback = new ArrayList<>();
+                fallback.add(family);
+                fallbackListMap.put(familyName, fallback);
+            }
+
+            // Then, add fallback fonts to the each fallback map.
+            for (int i = 0; i < xmlFamilies.length; i++) {
+                final FontConfig.Family xmlFamily = xmlFamilies[i];
+                // The first family (usually the sans-serif family) is always placed immediately
+                // after the primary family in the fallback.
+                if (i == 0 || xmlFamily.getName() == null) {
+                    pushFamilyToFallback(xmlFamily, fallbackListMap, bufferCache, fontDir);
                 }
             }
-            sFallbackFonts = familyList.toArray(new FontFamily[familyList.size()]);
-            setDefault(Typeface.createFromFamilies(sFallbackFonts));
 
-            Map<String, Typeface> systemFonts = new HashMap<String, Typeface>();
-            for (int i = 0; i < fontConfig.getFamilies().length; i++) {
-                Typeface typeface;
-                FontConfig.Family f = fontConfig.getFamilies()[i];
-                if (f.getName() != null) {
-                    if (i == 0) {
-                        // The first entry is the default typeface; no sense in
-                        // duplicating the corresponding FontFamily.
-                        typeface = sDefaultTypeface;
-                    } else {
-                        FontFamily fontFamily = makeFamilyFromParsed(f, bufferForPath);
-                        if (fontFamily == null) {
-                            continue;
-                        }
-                        FontFamily[] families = { fontFamily };
-                        typeface = Typeface.createFromFamiliesWithDefault(families,
-                                RESOLVE_BY_FONT_TABLE, RESOLVE_BY_FONT_TABLE);
-                    }
-                    systemFonts.put(f.getName(), typeface);
+            // Build the font map and fallback map.
+            for (int i = 0; i < fallbackListMap.size(); i++) {
+                final String fallbackName = fallbackListMap.keyAt(i);
+                final List<FontFamily> familyList = fallbackListMap.valueAt(i);
+                final FontFamily[] families = familyList.toArray(new FontFamily[familyList.size()]);
+
+                fallbackMap.put(fallbackName, families);
+                final long[] ptrArray = new long[families.length];
+                for (int j = 0; j < families.length; j++) {
+                    ptrArray[j] = families[j].mNativePtr;
                 }
+                fontMap.put(fallbackName, new Typeface(nativeCreateFromArray(
+                        ptrArray, RESOLVE_BY_FONT_TABLE, RESOLVE_BY_FONT_TABLE)));
             }
-            for (FontConfig.Alias alias : fontConfig.getAliases()) {
-                Typeface base = systemFonts.get(alias.getToName());
+
+            // Insert alias to font maps.
+            for (final FontConfig.Alias alias : fontConfig.getAliases()) {
+                Typeface base = fontMap.get(alias.getToName());
                 Typeface newFace = base;
                 int weight = alias.getWeight();
                 if (weight != 400) {
                     newFace = new Typeface(nativeCreateWeightAlias(base.native_instance, weight));
                 }
-                systemFonts.put(alias.getName(), newFace);
+                fontMap.put(alias.getName(), newFace);
             }
-            sSystemFontMap = systemFonts;
-
         } catch (RuntimeException e) {
             Log.w(TAG, "Didn't create default family (most likely, non-Minikin build)", e);
             // TODO: normal in non-Minikin case, remove or make error when Minikin-only
         } catch (FileNotFoundException e) {
-            Log.e(TAG, "Error opening " + configFilename, e);
+            Log.e(TAG, "Error opening " + xmlPath, e);
         } catch (IOException e) {
-            Log.e(TAG, "Error reading " + configFilename, e);
+            Log.e(TAG, "Error reading " + xmlPath, e);
         } catch (XmlPullParserException e) {
-            Log.e(TAG, "XML parse exception for " + configFilename, e);
+            Log.e(TAG, "XML parse exception for " + xmlPath, e);
         }
     }
 
     static {
-        init();
+        final ArrayMap<String, Typeface> systemFontMap = new ArrayMap<>();
+        final ArrayMap<String, FontFamily[]> systemFallbackMap = new ArrayMap<>();
+        buildSystemFallback("/system/etc/fonts.xml", "/system/fonts/", systemFontMap,
+                systemFallbackMap);
+        sSystemFontMap = Collections.unmodifiableMap(systemFontMap);
+        sSystemFallbackMap = Collections.unmodifiableMap(systemFallbackMap);
+
+        setDefault(sSystemFontMap.get(DEFAULT_FAMILY));
+
         // Set up defaults and typefaces exposed in public API
         DEFAULT         = create((String) null, 0);
         DEFAULT_BOLD    = create((String) null, Typeface.BOLD);
@@ -1008,10 +1090,6 @@
 
     }
 
-    private static File getSystemFontConfigLocation() {
-        return new File("/system/etc/");
-    }
-
     @Override
     protected void finalize() throws Throwable {
         try {
diff --git a/keystore/java/android/security/KeyStore.java b/keystore/java/android/security/KeyStore.java
index bfd1422..ccf9de0 100644
--- a/keystore/java/android/security/KeyStore.java
+++ b/keystore/java/android/security/KeyStore.java
@@ -341,12 +341,14 @@
         }
     }
 
-    public boolean grant(String key, int uid) {
+    public String grant(String key, int uid) {
         try {
-            return mBinder.grant(key, uid) == NO_ERROR;
+            String grantAlias =  mBinder.grant(key, uid);
+            if (grantAlias == "") return null;
+            return grantAlias;
         } catch (RemoteException e) {
             Log.w(TAG, "Cannot connect to keystore", e);
-            return false;
+            return null;
         }
     }
 
diff --git a/legacy-test/jarjar-rules.txt b/legacy-test/jarjar-rules.txt
index 9077e6f..fd8555c 100644
--- a/legacy-test/jarjar-rules.txt
+++ b/legacy-test/jarjar-rules.txt
@@ -1,2 +1,3 @@
 rule junit.** repackaged.junit.@1
 rule android.test.** repackaged.android.test.@1
+rule com.android.internal.util.** repackaged.com.android.internal.util.@1
diff --git a/libs/androidfw/ResourceTypes.cpp b/libs/androidfw/ResourceTypes.cpp
index 99ccc98..0782269 100644
--- a/libs/androidfw/ResourceTypes.cpp
+++ b/libs/androidfw/ResourceTypes.cpp
@@ -1901,6 +1901,8 @@
     if (diff != 0) return diff;
     diff = (int32_t)(screenSize - o.screenSize);
     if (diff != 0) return diff;
+    diff = (int32_t)(version - o.version);
+    if (diff != 0) return diff;
     diff = (int32_t)(screenLayout - o.screenLayout);
     if (diff != 0) return diff;
     diff = (int32_t)(screenLayout2 - o.screenLayout2);
@@ -1912,11 +1914,6 @@
     diff = (int32_t)(smallestScreenWidthDp - o.smallestScreenWidthDp);
     if (diff != 0) return diff;
     diff = (int32_t)(screenSizeDp - o.screenSizeDp);
-    if (diff != 0) return diff;
-
-    // Version MUST be last to ensure that a sorted list of configurations will always have the
-    // versions beside each other.
-    diff = (int32_t)(version - o.version);
     return (int)diff;
 }
 
diff --git a/libs/hwui/Android.bp b/libs/hwui/Android.bp
index 842e053..770a57a 100644
--- a/libs/hwui/Android.bp
+++ b/libs/hwui/Android.bp
@@ -36,6 +36,7 @@
         "external/skia/src/effects",
         "external/skia/src/image",
         "external/skia/src/utils",
+        "external/skia/src/gpu",
     ],
 
     product_variables: {
@@ -133,6 +134,7 @@
         "pipeline/skia/SkiaProfileRenderer.cpp",
         "pipeline/skia/SkiaRecordingCanvas.cpp",
         "pipeline/skia/SkiaVulkanPipeline.cpp",
+        "pipeline/skia/VectorDrawableAtlas.cpp",
         "renderstate/Blend.cpp",
         "renderstate/MeshState.cpp",
         "renderstate/OffscreenBufferPool.cpp",
@@ -340,6 +342,7 @@
         "tests/unit/TextureCacheTests.cpp",
 	"tests/unit/TypefaceTests.cpp",
         "tests/unit/VectorDrawableTests.cpp",
+        "tests/unit/VectorDrawableAtlasTests.cpp",
     ],
 }
 
diff --git a/libs/hwui/VectorDrawable.cpp b/libs/hwui/VectorDrawable.cpp
index f6b2912..8a1de02 100644
--- a/libs/hwui/VectorDrawable.cpp
+++ b/libs/hwui/VectorDrawable.cpp
@@ -491,34 +491,103 @@
     return *mCache.bitmap;
 }
 
-void Tree::updateCache(sk_sp<SkSurface> surface) {
-    if (surface.get()) {
-        mCache.surface = surface;
+void Tree::updateCache(sp<skiapipeline::VectorDrawableAtlas>& atlas, GrContext* context) {
+    SkRect dst;
+    sk_sp<SkSurface> surface = mCache.getSurface(&dst);
+    bool canReuseSurface = surface && dst.width() >= mProperties.getScaledWidth()
+            && dst.height() >= mProperties.getScaledHeight();
+    if (!canReuseSurface) {
+        int scaledWidth = SkScalarCeilToInt(mProperties.getScaledWidth());
+        int scaledHeight = SkScalarCeilToInt(mProperties.getScaledHeight());
+        auto atlasEntry = atlas->requestNewEntry(scaledWidth, scaledHeight, context);
+        if (INVALID_ATLAS_KEY != atlasEntry.key) {
+            dst = atlasEntry.rect;
+            surface = atlasEntry.surface;
+            mCache.setAtlas(atlas, atlasEntry.key);
+        } else {
+            //don't draw, if we failed to allocate an offscreen buffer
+            mCache.clear();
+            surface.reset();
+        }
     }
-    if (surface.get() || mCache.dirty) {
-        SkSurface* vdSurface = mCache.surface.get();
-        SkCanvas* canvas = vdSurface->getCanvas();
-        float scaleX = vdSurface->width() / mProperties.getViewportWidth();
-        float scaleY = vdSurface->height() / mProperties.getViewportHeight();
-        SkAutoCanvasRestore acr(canvas, true);
-        canvas->clear(SK_ColorTRANSPARENT);
-        canvas->scale(scaleX, scaleY);
-        mRootNode->draw(canvas, false);
+    if (!canReuseSurface || mCache.dirty) {
+        draw(surface.get(), dst);
         mCache.dirty = false;
-        canvas->flush();
     }
 }
 
+void Tree::draw(SkSurface* surface, const SkRect& dst) {
+    if (surface) {
+        SkCanvas* canvas = surface->getCanvas();
+        float scaleX = dst.width() / mProperties.getViewportWidth();
+        float scaleY = dst.height() / mProperties.getViewportHeight();
+        SkAutoCanvasRestore acr(canvas, true);
+        canvas->translate(dst.fLeft, dst.fTop);
+        canvas->clipRect(SkRect::MakeWH(dst.width(), dst.height()));
+        canvas->clear(SK_ColorTRANSPARENT);
+        canvas->scale(scaleX, scaleY);
+        mRootNode->draw(canvas, false);
+    }
+}
+
+void Tree::Cache::setAtlas(sp<skiapipeline::VectorDrawableAtlas> newAtlas,
+        skiapipeline::AtlasKey newAtlasKey) {
+    LOG_ALWAYS_FATAL_IF(newAtlasKey == INVALID_ATLAS_KEY);
+    clear();
+    mAtlas = newAtlas;
+    mAtlasKey = newAtlasKey;
+}
+
+sk_sp<SkSurface> Tree::Cache::getSurface(SkRect* bounds) {
+    sk_sp<SkSurface> surface;
+    sp<skiapipeline::VectorDrawableAtlas> atlas = mAtlas.promote();
+    if (atlas.get() && mAtlasKey != INVALID_ATLAS_KEY) {
+        auto atlasEntry = atlas->getEntry(mAtlasKey);
+        *bounds = atlasEntry.rect;
+        surface = atlasEntry.surface;
+        mAtlasKey = atlasEntry.key;
+    }
+
+    return surface;
+}
+
+void Tree::Cache::clear() {
+    sp<skiapipeline::VectorDrawableAtlas> lockAtlas = mAtlas.promote();
+    if (lockAtlas.get()) {
+        lockAtlas->releaseEntry(mAtlasKey);
+    }
+    mAtlas = nullptr;
+    mAtlasKey = INVALID_ATLAS_KEY;
+}
+
 void Tree::draw(SkCanvas* canvas) {
-   /*
-    * TODO address the following...
-    *
-    * 1) figure out how to set path's as volatile during animation
-    * 2) if mRoot->getPaint() != null either promote to layer (during
-    *    animation) or cache in SkSurface (for static content)
-    */
-    canvas->drawImageRect(mCache.surface->makeImageSnapshot().get(),
-        mutateProperties()->getBounds(), getPaint());
+    SkRect src;
+    sk_sp<SkSurface> vdSurface = mCache.getSurface(&src);
+    if (vdSurface) {
+        canvas->drawImageRect(vdSurface->makeImageSnapshot().get(), src,
+                mutateProperties()->getBounds(), getPaint());
+    } else {
+        // Handle the case when VectorDrawableAtlas has been destroyed, because of memory pressure.
+        // We render the VD into a temporary standalone buffer and mark the frame as dirty. Next
+        // frame will be cached into the atlas.
+        int scaledWidth = SkScalarCeilToInt(mProperties.getScaledWidth());
+        int scaledHeight = SkScalarCeilToInt(mProperties.getScaledHeight());
+        SkRect src = SkRect::MakeWH(scaledWidth, scaledHeight);
+#ifndef ANDROID_ENABLE_LINEAR_BLENDING
+        sk_sp<SkColorSpace> colorSpace = nullptr;
+#else
+        sk_sp<SkColorSpace> colorSpace = SkColorSpace::MakeSRGB();
+#endif
+        SkImageInfo info = SkImageInfo::MakeN32(scaledWidth, scaledHeight, kPremul_SkAlphaType,
+                colorSpace);
+        sk_sp<SkSurface> surface = SkSurface::MakeRenderTarget(canvas->getGrContext(),
+                SkBudgeted::kYes, info);
+        draw(surface.get(), src);
+        mCache.clear();
+        canvas->drawImageRect(surface->makeImageSnapshot().get(), mutateProperties()->getBounds(),
+                getPaint());
+        markDirty();
+    }
 }
 
 void Tree::updateBitmapCache(Bitmap& bitmap, bool useStagingData) {
diff --git a/libs/hwui/VectorDrawable.h b/libs/hwui/VectorDrawable.h
index 22cfe29..efbb695 100644
--- a/libs/hwui/VectorDrawable.h
+++ b/libs/hwui/VectorDrawable.h
@@ -19,6 +19,7 @@
 
 #include "hwui/Canvas.h"
 #include "hwui/Bitmap.h"
+#include "renderthread/CacheManager.h"
 #include "DisplayList.h"
 
 #include <SkBitmap.h>
@@ -687,35 +688,61 @@
     bool getPropertyChangeWillBeConsumed() const { return mWillBeConsumed; }
     void setPropertyChangeWillBeConsumed(bool willBeConsumed) { mWillBeConsumed = willBeConsumed; }
 
-    // Returns true if VD cache surface is big enough. This should always be called from RT and it
-    // works with Skia pipelines only.
-    bool canReuseSurface() {
-        SkSurface* surface = mCache.surface.get();
-        return surface && surface->width() >= mProperties.getScaledWidth()
-              && surface->height() >= mProperties.getScaledHeight();
-    }
-
-    // Draws VD cache into a canvas. This should always be called from RT and it works with Skia
-    // pipelines only.
+    /**
+     * Draws VD cache into a canvas. This should always be called from RT and it works with Skia
+     * pipelines only.
+     */
     void draw(SkCanvas* canvas);
 
-    // Draws VD into a GPU backed surface. If canReuseSurface returns false, then "surface" must
-    // contain a new surface. This should always be called from RT and it works with Skia pipelines
-    // only.
-    void updateCache(sk_sp<SkSurface> surface);
+    /**
+     * Draws VD into a GPU backed surface.
+     * This should always be called from RT and it works with Skia pipeline only.
+     */
+    void updateCache(sp<skiapipeline::VectorDrawableAtlas>& atlas, GrContext* context);
 
 private:
-    struct Cache {
+    class Cache {
+    public:
         sk_sp<Bitmap> bitmap; //used by HWUI pipeline and software
         //TODO: use surface instead of bitmap when drawing in software canvas
-        sk_sp<SkSurface> surface; //used only by Skia pipelines
         bool dirty = true;
+
+        // the rest of the code in Cache is used by Skia pipelines only
+
+        ~Cache() { clear(); }
+
+        /**
+         * Stores a weak pointer to the atlas and a key.
+         */
+        void setAtlas(sp<skiapipeline::VectorDrawableAtlas> atlas,
+                skiapipeline::AtlasKey newAtlasKey);
+
+        /**
+         * Gets a surface and bounds from the atlas.
+         *
+         * @return nullptr if the altas has been deleted.
+         */
+        sk_sp<SkSurface> getSurface(SkRect* bounds);
+
+        /**
+         * Releases atlas key from the atlas, which makes it available for reuse.
+         */
+        void clear();
+    private:
+        wp<skiapipeline::VectorDrawableAtlas> mAtlas;
+        skiapipeline::AtlasKey mAtlasKey = INVALID_ATLAS_KEY;
     };
 
     SkPaint* updatePaint(SkPaint* outPaint, TreeProperties* prop);
     bool allocateBitmapIfNeeded(Cache& cache, int width, int height);
     bool canReuseBitmap(Bitmap*, int width, int height);
     void updateBitmapCache(Bitmap& outCache, bool useStagingData);
+
+    /**
+     * Draws the root node into "surface" at a given "dst" position.
+     */
+    void draw(SkSurface* surface, const SkRect& dst);
+
     // Cap the bitmap size, such that it won't hurt the performance too much
     // and it won't crash due to a very large scale.
     // The drawable will look blurry above this size.
diff --git a/libs/hwui/font/CacheTexture.cpp b/libs/hwui/font/CacheTexture.cpp
index e2844ad..73beba9 100644
--- a/libs/hwui/font/CacheTexture.cpp
+++ b/libs/hwui/font/CacheTexture.cpp
@@ -91,6 +91,9 @@
     CacheBlock* prevBlock = blockToRemove->mPrev;
 
     if (prevBlock) {
+        // If this doesn't hold, we have a use-after-free below.
+        LOG_ALWAYS_FATAL_IF(head == blockToRemove,
+                "removeBlock: head should not have a previous block");
         prevBlock->mNext = nextBlock;
     } else {
         newHead = nextBlock;
diff --git a/libs/hwui/hwui/Bitmap.cpp b/libs/hwui/hwui/Bitmap.cpp
index 3a8914e..3361fa0 100644
--- a/libs/hwui/hwui/Bitmap.cpp
+++ b/libs/hwui/hwui/Bitmap.cpp
@@ -168,7 +168,7 @@
     // really hard to work with. Skia really, really wants immutable objects,
     // but with the nested-ref-count hackery going on that's just not
     // feasible without going insane trying to figure it out
-    this->android_only_reset(mInfo.width(), mInfo.height(), rowBytes, nullptr);
+    this->android_only_reset(mInfo.width(), mInfo.height(), rowBytes);
 }
 
 Bitmap::Bitmap(void* address, size_t size, const SkImageInfo& info, size_t rowBytes)
@@ -201,8 +201,7 @@
 
 Bitmap::Bitmap(GraphicBuffer* buffer, const SkImageInfo& info)
         : SkPixelRef(info.width(), info.height(), nullptr,
-                     bytesPerPixel(buffer->getPixelFormat()) * buffer->getStride(),
-                     nullptr)
+                     bytesPerPixel(buffer->getPixelFormat()) * buffer->getStride())
         , mInfo(validateAlpha(info))
         , mPixelStorageType(PixelStorageType::Hardware) {
     mPixelStorage.hardware.buffer = buffer;
diff --git a/libs/hwui/hwui/Canvas.cpp b/libs/hwui/hwui/Canvas.cpp
index 4507c50..e754daf 100644
--- a/libs/hwui/hwui/Canvas.cpp
+++ b/libs/hwui/hwui/Canvas.cpp
@@ -35,6 +35,13 @@
     return new uirenderer::RecordingCanvas(width, height);
 }
 
+static inline void drawStroke(SkScalar left, SkScalar right, SkScalar top, SkScalar thickness,
+        const SkPaint& paint, Canvas* canvas) {
+    const SkScalar strokeWidth = fmax(thickness, 1.0f);
+    const SkScalar bottom = top + strokeWidth;
+    canvas->drawRect(left, top, right, bottom, paint);
+}
+
 void Canvas::drawTextDecorations(float x, float y, float length, const SkPaint& paint) {
     uint32_t flags;
     SkDrawFilter* drawFilter = getDrawFilter();
@@ -46,7 +53,6 @@
         flags = paint.getFlags();
     }
     if (flags & (SkPaint::kUnderlineText_ReserveFlag | SkPaint::kStrikeThruText_ReserveFlag)) {
-
         const SkScalar left = x;
         const SkScalar right = x + length;
         if (flags & SkPaint::kUnderlineText_ReserveFlag) {
@@ -60,18 +66,15 @@
             if (!metrics.hasUnderlineThickness(&thickness)) {
                 thickness = paint.getTextSize() * Paint::kStdUnderline_Thickness;
             }
-            const float strokeWidth = fmax(thickness, 1.0f);
             const SkScalar top = y + position;
-            const SkScalar bottom = top + strokeWidth;
-            drawRect(left, top, right, bottom, paint);
+            drawStroke(left, right, top, thickness, paint, this);
         }
         if (flags & SkPaint::kStrikeThruText_ReserveFlag) {
             const float textSize = paint.getTextSize();
-            const float position = textSize * Paint::kStdStrikeThru_Offset;
-            const float strokeWidth = fmax(textSize * Paint::kStdUnderline_Thickness, 1.0f);
-            const SkScalar top = y + position - 0.5f * strokeWidth;
-            const SkScalar bottom = y + position + 0.5f * strokeWidth;
-            drawRect(left, top, right, bottom, paint);
+            const float position = textSize * Paint::kStdStrikeThru_Top;
+            const SkScalar thickness = textSize * Paint::kStdStrikeThru_Thickness;
+            const SkScalar top = y + position;
+            drawStroke(left, right, top, thickness, paint, this);
         }
     }
 }
diff --git a/libs/hwui/hwui/Paint.h b/libs/hwui/hwui/Paint.h
index 4305025..a5d83a0 100644
--- a/libs/hwui/hwui/Paint.h
+++ b/libs/hwui/hwui/Paint.h
@@ -37,6 +37,10 @@
     constexpr static float kStdUnderline_Top =
             kStdUnderline_Offset - 0.5f * kStdUnderline_Thickness;
 
+    constexpr static float kStdStrikeThru_Thickness = kStdUnderline_Thickness;
+    constexpr static float kStdStrikeThru_Top =
+            kStdStrikeThru_Offset - 0.5f * kStdStrikeThru_Thickness;
+
     Paint();
     Paint(const Paint& paint);
     Paint(const SkPaint& paint);  // NOLINT(implicit)
diff --git a/libs/hwui/hwui/Typeface.cpp b/libs/hwui/hwui/Typeface.cpp
index f66bb04..d96e376 100644
--- a/libs/hwui/hwui/Typeface.cpp
+++ b/libs/hwui/hwui/Typeface.cpp
@@ -68,7 +68,7 @@
 Typeface* gDefaultTypeface = NULL;
 
 Typeface* Typeface::resolveDefault(Typeface* src) {
-    LOG_ALWAYS_FATAL_IF(gDefaultTypeface == nullptr);
+    LOG_ALWAYS_FATAL_IF(src == nullptr && gDefaultTypeface == nullptr);
     return src == nullptr ? gDefaultTypeface : src;
 }
 
diff --git a/libs/hwui/pipeline/skia/SkiaPipeline.cpp b/libs/hwui/pipeline/skia/SkiaPipeline.cpp
index 6cd1a3e..03792e0 100644
--- a/libs/hwui/pipeline/skia/SkiaPipeline.cpp
+++ b/libs/hwui/pipeline/skia/SkiaPipeline.cpp
@@ -183,26 +183,16 @@
 };
 
 void SkiaPipeline::renderVectorDrawableCache() {
-    //render VectorDrawables into offscreen buffers
-    for (auto vd : mVectorDrawables) {
-        sk_sp<SkSurface> surface;
-        if (!vd->canReuseSurface()) {
-#ifndef ANDROID_ENABLE_LINEAR_BLENDING
-            sk_sp<SkColorSpace> colorSpace = nullptr;
-#else
-            sk_sp<SkColorSpace> colorSpace = SkColorSpace::MakeSRGB();
-#endif
-            int scaledWidth = SkScalarCeilToInt(vd->properties().getScaledWidth());
-            int scaledHeight = SkScalarCeilToInt(vd->properties().getScaledHeight());
-            SkImageInfo info = SkImageInfo::MakeN32(scaledWidth, scaledHeight,
-                    kPremul_SkAlphaType, colorSpace);
-            SkASSERT(mRenderThread.getGrContext() != nullptr);
-            surface = SkSurface::MakeRenderTarget(mRenderThread.getGrContext(), SkBudgeted::kYes,
-                    info);
+    if (!mVectorDrawables.empty()) {
+        sp<VectorDrawableAtlas> atlas = mRenderThread.cacheManager().acquireVectorDrawableAtlas();
+        auto grContext = mRenderThread.getGrContext();
+        atlas->prepareForDraw(grContext);
+        for (auto vd : mVectorDrawables) {
+            vd->updateCache(atlas, grContext);
         }
-        vd->updateCache(surface);
+        grContext->flush();
+        mVectorDrawables.clear();
     }
-    mVectorDrawables.clear();
 }
 
 void SkiaPipeline::renderFrame(const LayerUpdateQueue& layers, const SkRect& clip,
diff --git a/libs/hwui/pipeline/skia/VectorDrawableAtlas.cpp b/libs/hwui/pipeline/skia/VectorDrawableAtlas.cpp
new file mode 100644
index 0000000..437653a
--- /dev/null
+++ b/libs/hwui/pipeline/skia/VectorDrawableAtlas.cpp
@@ -0,0 +1,269 @@
+/*
+ * 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.
+ */
+
+#include "VectorDrawableAtlas.h"
+
+#include <GrRectanizer_pow2.h>
+#include <SkCanvas.h>
+#include <cmath>
+#include "utils/TraceUtils.h"
+#include "renderthread/RenderProxy.h"
+
+namespace android {
+namespace uirenderer {
+namespace skiapipeline {
+
+VectorDrawableAtlas::VectorDrawableAtlas(size_t surfaceArea, StorageMode storageMode)
+        : mWidth((int)std::sqrt(surfaceArea))
+        , mHeight((int)std::sqrt(surfaceArea))
+        , mStorageMode(storageMode) {
+}
+
+void VectorDrawableAtlas::prepareForDraw(GrContext* context) {
+    if (StorageMode::allowSharedSurface == mStorageMode) {
+        if (!mSurface) {
+            mSurface = createSurface(mWidth, mHeight, context);
+            mRectanizer = std::make_unique<GrRectanizerPow2>(mWidth, mHeight);
+            mPixelUsedByVDs = 0;
+            mPixelAllocated = 0;
+            mConsecutiveFailures = 0;
+            mFreeRects.clear();
+        } else {
+            if (isFragmented()) {
+                // Invoke repack outside renderFrame to avoid jank.
+                renderthread::RenderProxy::repackVectorDrawableAtlas();
+            }
+        }
+    }
+}
+
+#define MAX_CONSECUTIVE_FAILURES 5
+#define MAX_UNUSED_RATIO 2.0f
+
+bool VectorDrawableAtlas::isFragmented() {
+    return mConsecutiveFailures > MAX_CONSECUTIVE_FAILURES
+            && mPixelUsedByVDs*MAX_UNUSED_RATIO < mPixelAllocated;
+}
+
+void VectorDrawableAtlas::repackIfNeeded(GrContext* context) {
+    // We repackage when atlas failed to allocate space MAX_CONSECUTIVE_FAILURES consecutive
+    // times and the atlas allocated pixels are at least MAX_UNUSED_RATIO times higher than pixels
+    // used by atlas VDs.
+    if (isFragmented() && mSurface) {
+        repack(context);
+    }
+}
+
+// compare to CacheEntry objects based on VD area.
+bool VectorDrawableAtlas::compareCacheEntry(const CacheEntry& first, const CacheEntry& second)
+{
+    return first.VDrect.width()*first.VDrect.height() < second.VDrect.width()*second.VDrect.height();
+}
+
+void VectorDrawableAtlas::repack(GrContext* context) {
+    ATRACE_CALL();
+    sk_sp<SkSurface> newSurface;
+    SkCanvas* canvas = nullptr;
+    if (StorageMode::allowSharedSurface == mStorageMode) {
+        newSurface = createSurface(mWidth, mHeight, context);
+        if (!newSurface) {
+            return;
+        }
+        canvas = newSurface->getCanvas();
+        canvas->clear(SK_ColorTRANSPARENT);
+        mRectanizer = std::make_unique<GrRectanizerPow2>(mWidth, mHeight);
+    } else {
+        if (!mSurface) {
+            return; //nothing to repack
+        }
+        mRectanizer.reset();
+    }
+    mFreeRects.clear();
+    SkImage* sourceImageAtlas = nullptr;
+    if (mSurface) {
+        sourceImageAtlas = mSurface->makeImageSnapshot().get();
+    }
+
+    // Sort the list by VD size, which allows for the smallest VDs to get first in the atlas.
+    // Sorting is safe, because it does not affect iterator validity.
+    if (mRects.size() <= 100) {
+        mRects.sort(compareCacheEntry);
+    }
+
+    for (CacheEntry& entry : mRects) {
+        SkRect currentVDRect = entry.VDrect;
+        SkImage* sourceImage; //copy either from the atlas or from a standalone surface
+        if (entry.surface) {
+            if (!fitInAtlas(currentVDRect.width(), currentVDRect.height())) {
+                continue; //don't even try to repack huge VD
+            }
+            sourceImage = entry.surface->makeImageSnapshot().get();
+        } else {
+            sourceImage = sourceImageAtlas;
+        }
+        size_t VDRectArea = currentVDRect.width()*currentVDRect.height();
+        SkIPoint16 pos;
+        if (canvas && mRectanizer->addRect(currentVDRect.width(), currentVDRect.height(), &pos)) {
+            SkRect newRect = SkRect::MakeXYWH(pos.fX, pos.fY, currentVDRect.width(),
+                    currentVDRect.height());
+            canvas->drawImageRect(sourceImage, currentVDRect, newRect, nullptr);
+            entry.VDrect = newRect;
+            entry.rect = newRect;
+            if (entry.surface) {
+                // A rectangle moved from a standalone surface to the atlas.
+                entry.surface = nullptr;
+                mPixelUsedByVDs += VDRectArea;
+            }
+        } else {
+            // Repack failed for this item. If it is not already, store it in a standalone
+            // surface.
+            if (!entry.surface) {
+                // A rectangle moved from an atlas to a standalone surface.
+                mPixelUsedByVDs -= VDRectArea;
+                SkRect newRect = SkRect::MakeWH(currentVDRect.width(),
+                        currentVDRect.height());
+                entry.surface = createSurface(newRect.width(), newRect.height(), context);
+                auto tempCanvas = entry.surface->getCanvas();
+                tempCanvas->clear(SK_ColorTRANSPARENT);
+                tempCanvas->drawImageRect(sourceImageAtlas, currentVDRect, newRect, nullptr);
+                entry.VDrect = newRect;
+                entry.rect = newRect;
+            }
+        }
+    }
+    mPixelAllocated = mPixelUsedByVDs;
+    context->flush();
+    mSurface = newSurface;
+    mConsecutiveFailures = 0;
+}
+
+AtlasEntry VectorDrawableAtlas::requestNewEntry(int width, int height, GrContext* context) {
+    AtlasEntry result;
+    if (width <= 0 || height <= 0) {
+        return result;
+    }
+
+    if (mSurface) {
+        const size_t area = width*height;
+
+        // Use a rectanizer to allocate unused space from the atlas surface.
+        bool notTooBig = fitInAtlas(width, height);
+        SkIPoint16 pos;
+        if (notTooBig && mRectanizer->addRect(width, height, &pos)) {
+            mPixelUsedByVDs += area;
+            mPixelAllocated += area;
+            result.rect = SkRect::MakeXYWH(pos.fX, pos.fY, width, height);
+            result.surface = mSurface;
+            auto eraseIt = mRects.emplace(mRects.end(), result.rect, result.rect, nullptr);
+            CacheEntry* entry = &(*eraseIt);
+            entry->eraseIt = eraseIt;
+            result.key = reinterpret_cast<AtlasKey>(entry);
+            mConsecutiveFailures = 0;
+            return result;
+        }
+
+        // Try to reuse atlas memory from rectangles freed by "releaseEntry".
+        auto freeRectIt = mFreeRects.lower_bound(area);
+        while (freeRectIt != mFreeRects.end()) {
+            SkRect& freeRect = freeRectIt->second;
+            if (freeRect.width() >= width && freeRect.height() >= height) {
+                result.rect = SkRect::MakeXYWH(freeRect.fLeft, freeRect.fTop, width, height);
+                result.surface = mSurface;
+                auto eraseIt = mRects.emplace(mRects.end(), result.rect, freeRect, nullptr);
+                CacheEntry* entry = &(*eraseIt);
+                entry->eraseIt = eraseIt;
+                result.key = reinterpret_cast<AtlasKey>(entry);
+                mPixelUsedByVDs += area;
+                mFreeRects.erase(freeRectIt);
+                mConsecutiveFailures = 0;
+                return result;
+            }
+            freeRectIt++;
+        }
+
+        if (notTooBig && mConsecutiveFailures <= MAX_CONSECUTIVE_FAILURES) {
+            mConsecutiveFailures++;
+        }
+    }
+
+    // Allocate a surface for a rectangle that is too big or if atlas is full.
+    if (nullptr != context) {
+        result.rect = SkRect::MakeWH(width, height);
+        result.surface = createSurface(width, height, context);
+        auto eraseIt = mRects.emplace(mRects.end(), result.rect, result.rect, result.surface);
+        CacheEntry* entry = &(*eraseIt);
+        entry->eraseIt = eraseIt;
+        result.key = reinterpret_cast<AtlasKey>(entry);
+    }
+
+    return result;
+}
+
+AtlasEntry VectorDrawableAtlas::getEntry(AtlasKey atlasKey) {
+    AtlasEntry result;
+    if (INVALID_ATLAS_KEY != atlasKey) {
+        CacheEntry* entry = reinterpret_cast<CacheEntry*>(atlasKey);
+        result.rect = entry->VDrect;
+        result.surface = entry->surface;
+        if (!result.surface) {
+            result.surface = mSurface;
+        }
+        result.key = atlasKey;
+    }
+    return result;
+}
+
+void VectorDrawableAtlas::releaseEntry(AtlasKey atlasKey) {
+    if (INVALID_ATLAS_KEY != atlasKey) {
+        CacheEntry* entry = reinterpret_cast<CacheEntry*>(atlasKey);
+        if (!entry->surface) {
+            // Store freed atlas rectangles in "mFreeRects" and try to reuse them later, when atlas
+            // is full.
+            SkRect& removedRect = entry->rect;
+            size_t rectArea = removedRect.width()*removedRect.height();
+            mFreeRects.emplace(rectArea, removedRect);
+            SkRect& removedVDRect = entry->VDrect;
+            size_t VDRectArea = removedVDRect.width()*removedVDRect.height();
+            mPixelUsedByVDs -= VDRectArea;
+            mConsecutiveFailures = 0;
+        }
+        auto eraseIt = entry->eraseIt;
+        mRects.erase(eraseIt);
+    }
+}
+
+sk_sp<SkSurface> VectorDrawableAtlas::createSurface(int width, int height, GrContext* context) {
+#ifndef ANDROID_ENABLE_LINEAR_BLENDING
+    sk_sp<SkColorSpace> colorSpace = nullptr;
+#else
+    sk_sp<SkColorSpace> colorSpace = SkColorSpace::MakeSRGB();
+#endif
+    SkImageInfo info = SkImageInfo::MakeN32(width, height, kPremul_SkAlphaType, colorSpace);
+    return SkSurface::MakeRenderTarget(context, SkBudgeted::kYes, info);
+}
+
+void VectorDrawableAtlas::setStorageMode(StorageMode mode) {
+    mStorageMode = mode;
+    if (StorageMode::disallowSharedSurface == mStorageMode && mSurface) {
+        mSurface.reset();
+        mRectanizer.reset();
+        mFreeRects.clear();
+    }
+}
+
+} /* namespace skiapipeline */
+} /* namespace uirenderer */
+} /* namespace android */
diff --git a/libs/hwui/pipeline/skia/VectorDrawableAtlas.h b/libs/hwui/pipeline/skia/VectorDrawableAtlas.h
new file mode 100644
index 0000000..496c557
--- /dev/null
+++ b/libs/hwui/pipeline/skia/VectorDrawableAtlas.h
@@ -0,0 +1,198 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#include <map>
+#include <SkSurface.h>
+#include <utils/FatVector.h>
+#include <utils/RefBase.h>
+#include <list>
+
+class GrRectanizer;
+
+namespace android {
+namespace uirenderer {
+namespace skiapipeline {
+
+typedef uintptr_t AtlasKey;
+
+#define INVALID_ATLAS_KEY 0
+
+struct AtlasEntry {
+    sk_sp<SkSurface> surface;
+    SkRect rect;
+    AtlasKey key = INVALID_ATLAS_KEY;
+};
+
+/**
+ * VectorDrawableAtlas provides offscreen buffers used to draw VD and AnimatedVD.
+ * VectorDrawableAtlas can allocate a standalone surface or provide a subrect from a shared surface.
+ * VectorDrawableAtlas is owned by the CacheManager and weak pointers are kept by each
+ * VectorDrawable that is using it. VectorDrawableAtlas and its surface can be deleted at any time,
+ * except during a renderFrame call. VectorDrawable does not contain a pointer to atlas SkSurface
+ * nor any coordinates into the atlas, but instead holds a rectangle "id", which is resolved only
+ * when drawing. This design makes VectorDrawableAtlas free to move the data internally.
+ * At draw time a VectorDrawable may find, that its atlas has been deleted, which will make it
+ * draw in a standalone cache surface not part of an atlas. In this case VD won't use
+ * VectorDrawableAtlas until the next frame.
+ * VectorDrawableAtlas tries to fit VDs in the atlas SkSurface. If there is not enough space in
+ * the atlas, VectorDrawableAtlas creates a standalone surface for each VD.
+ * When a VectorDrawable is deleted, it invokes VectorDrawableAtlas::releaseEntry, which is keeping
+ * track of free spaces and allow to reuse the surface for another VD.
+ */
+ //TODO: Check if not using atlas for AnimatedVD is more efficient.
+ //TODO: For low memory situations, when there are no paint effects in VD, we may render without an
+ //TODO: offscreen surface.
+class VectorDrawableAtlas : public virtual RefBase {
+public:
+    enum class StorageMode {
+        allowSharedSurface,
+        disallowSharedSurface
+    };
+
+    VectorDrawableAtlas(size_t surfaceArea,
+            StorageMode storageMode = StorageMode::allowSharedSurface);
+
+    /**
+     * "prepareForDraw" may allocate a new surface if needed. It may schedule to repack the
+     * atlas at a later time.
+     */
+    void prepareForDraw(GrContext* context);
+
+    /**
+     * Repack the atlas if needed, by moving used rectangles into a new atlas surface.
+     * The goal of repacking is to fix a fragmented atlas.
+     */
+    void repackIfNeeded(GrContext* context);
+
+    /**
+     * Returns true if atlas is fragmented and repack is needed.
+     */
+    bool isFragmented();
+
+    /**
+     * "requestNewEntry" is called by VectorDrawable to allocate a new rectangle area from the atlas
+     * or create a standalone surface if atlas is full.
+     * On success it returns a non-negative unique id, which can be used later with "getEntry" and
+     * "releaseEntry".
+     */
+    AtlasEntry requestNewEntry(int width, int height, GrContext* context);
+
+    /**
+     * "getEntry" extracts coordinates and surface of a previously created rectangle.
+     * "atlasKey" is an unique id created by "requestNewEntry". Passing a non-existing "atlasKey" is
+     * causing an undefined behaviour.
+     * On success it returns a rectangle Id -> may be same or different from "atlasKey" if
+     * implementation decides to move the record internally.
+     */
+    AtlasEntry getEntry(AtlasKey atlasKey);
+
+    /**
+     * "releaseEntry" is invoked when a VectorDrawable is deleted. Passing a non-existing "atlasKey"
+     * is causing an undefined behaviour.
+     */
+    void releaseEntry(AtlasKey atlasKey);
+
+    void setStorageMode(StorageMode mode);
+
+private:
+    struct CacheEntry {
+        CacheEntry(const SkRect& newVDrect, const SkRect& newRect,
+                const sk_sp<SkSurface>& newSurface)
+                : VDrect(newVDrect)
+                , rect(newRect)
+                , surface(newSurface) { }
+
+        /**
+         * size and position of VectorDrawable into the atlas or in "this.surface"
+         */
+        SkRect VDrect;
+
+        /**
+         * rect allocated in atlas surface or "this.surface". It may be bigger than "VDrect"
+         */
+        SkRect rect;
+
+        /**
+         * this surface is used if atlas is full or VD is too big
+         */
+        sk_sp<SkSurface> surface;
+
+        /**
+         * iterator is used to delete self with a constant complexity (without traversing the list)
+         */
+        std::list<CacheEntry>::iterator eraseIt;
+    };
+
+    /**
+     * atlas surface shared by all VDs
+     */
+    sk_sp<SkSurface> mSurface;
+
+    std::unique_ptr<GrRectanizer> mRectanizer;
+    const int mWidth;
+    const int mHeight;
+
+    /**
+     * "mRects" keeps records only for rectangles used by VDs. List has nice properties: constant
+     * complexity to insert and erase and references are not invalidated by insert/erase.
+     */
+    std::list<CacheEntry> mRects;
+
+    /**
+     * Rectangles freed by "releaseEntry" are removed from "mRects" and added to "mFreeRects".
+     * "mFreeRects" is using for an index the rectangle area. There could be more than one free
+     * rectangle with the same area, which is the reason to use "multimap" instead of "map".
+     */
+    std::multimap<size_t, SkRect> mFreeRects;
+
+    /**
+     * area in atlas used by VectorDrawables (area in standalone surface not counted)
+     */
+    int mPixelUsedByVDs = 0;
+
+    /**
+     * area allocated in mRectanizer
+     */
+    int mPixelAllocated = 0;
+
+    /**
+     * Consecutive times we had to allocate standalone surfaces, because atlas was full.
+     */
+    int mConsecutiveFailures = 0;
+
+    /**
+     * mStorageMode allows using a shared surface to store small vector drawables.
+     * Using a shared surface can boost the performance by allowing GL ops to be batched, but may
+     * consume more memory.
+     */
+    StorageMode mStorageMode;
+
+    sk_sp<SkSurface> createSurface(int width, int height, GrContext* context);
+
+    inline bool fitInAtlas(int width, int height) {
+        return 2*width < mWidth && 2*height < mHeight;
+    }
+
+    void repack(GrContext* context);
+
+    static bool compareCacheEntry(const CacheEntry& first, const CacheEntry& second);
+};
+
+} /* namespace skiapipeline */
+} /* namespace uirenderer */
+} /* namespace android */
diff --git a/libs/hwui/renderthread/CacheManager.cpp b/libs/hwui/renderthread/CacheManager.cpp
index f0d6b38..55694d0 100644
--- a/libs/hwui/renderthread/CacheManager.cpp
+++ b/libs/hwui/renderthread/CacheManager.cpp
@@ -43,7 +43,8 @@
 
 CacheManager::CacheManager(const DisplayInfo& display)
         : mMaxSurfaceArea(display.w * display.h) {
-    mVectorDrawableAtlas.reset(new VectorDrawableAtlas);
+    mVectorDrawableAtlas = new skiapipeline::VectorDrawableAtlas(mMaxSurfaceArea/2,
+            skiapipeline::VectorDrawableAtlas::StorageMode::allowSharedSurface);
 }
 
 void CacheManager::reset(GrContext* context) {
@@ -61,7 +62,7 @@
 void CacheManager::destroy() {
     // cleanup any caches here as the GrContext is about to go away...
     mGrContext.reset(nullptr);
-    mVectorDrawableAtlas.reset(new VectorDrawableAtlas);
+    mVectorDrawableAtlas = new skiapipeline::VectorDrawableAtlas(mMaxSurfaceArea/2);
 }
 
 void CacheManager::updateContextCacheSizes() {
@@ -104,7 +105,7 @@
 
     switch (mode) {
         case TrimMemoryMode::Complete:
-            mVectorDrawableAtlas.reset(new VectorDrawableAtlas);
+            mVectorDrawableAtlas = new skiapipeline::VectorDrawableAtlas(mMaxSurfaceArea/2);
             mGrContext->freeGpuResources();
             break;
         case TrimMemoryMode::UiHidden:
@@ -121,24 +122,14 @@
     mGrContext->purgeResourcesNotUsedInMs(std::chrono::seconds(30));
 }
 
-VectorDrawableAtlas* CacheManager::acquireVectorDrawableAtlas() {
+sp<skiapipeline::VectorDrawableAtlas> CacheManager::acquireVectorDrawableAtlas() {
     LOG_ALWAYS_FATAL_IF(mVectorDrawableAtlas.get() == nullptr);
     LOG_ALWAYS_FATAL_IF(mGrContext == nullptr);
 
     /**
-     * TODO LIST:
-     *    1) compute the atlas based on the surfaceArea surface
-     *    2) identify a way to reuse cache entries
-     *    3) add ability to repack the cache?
-     *    4) define memory conditions where we clear the cache (e.g. surface->reset())
+     * TODO: define memory conditions where we clear the cache (e.g. surface->reset())
      */
-
-    return mVectorDrawableAtlas.release();
-}
-void CacheManager::releaseVectorDrawableAtlas(VectorDrawableAtlas* atlas) {
-    LOG_ALWAYS_FATAL_IF(mVectorDrawableAtlas.get() != nullptr);
-    mVectorDrawableAtlas.reset(atlas);
-    mVectorDrawableAtlas->isNewAtlas = false;
+    return mVectorDrawableAtlas;
 }
 
 void CacheManager::dumpMemoryUsage(String8& log, const RenderState* renderState) {
diff --git a/libs/hwui/renderthread/CacheManager.h b/libs/hwui/renderthread/CacheManager.h
index 43d58f2..90362f3 100644
--- a/libs/hwui/renderthread/CacheManager.h
+++ b/libs/hwui/renderthread/CacheManager.h
@@ -22,6 +22,7 @@
 #include <ui/DisplayInfo.h>
 #include <utils/String8.h>
 #include <vector>
+#include "pipeline/skia/VectorDrawableAtlas.h"
 
 namespace android {
 
@@ -36,11 +37,6 @@
 class IRenderPipeline;
 class RenderThread;
 
-struct VectorDrawableAtlas {
-    sk_sp<SkSurface> surface;
-    bool isNewAtlas = true;
-};
-
 class CacheManager {
 public:
     enum class TrimMemoryMode {
@@ -53,8 +49,7 @@
     void trimStaleResources();
     void dumpMemoryUsage(String8& log, const RenderState* renderState = nullptr);
 
-    VectorDrawableAtlas* acquireVectorDrawableAtlas();
-    void releaseVectorDrawableAtlas(VectorDrawableAtlas*);
+    sp<skiapipeline::VectorDrawableAtlas> acquireVectorDrawableAtlas();
 
     size_t getCacheSize() const { return mMaxResourceBytes; }
     size_t getBackgroundCacheSize() const { return mBackgroundResourceBytes; }
@@ -81,7 +76,7 @@
         size_t surfaceArea = 0;
     };
 
-    std::unique_ptr<VectorDrawableAtlas> mVectorDrawableAtlas;
+    sp<skiapipeline::VectorDrawableAtlas> mVectorDrawableAtlas;
 };
 
 } /* namespace renderthread */
diff --git a/libs/hwui/renderthread/RenderProxy.cpp b/libs/hwui/renderthread/RenderProxy.cpp
index 370cf52..7fe966d 100644
--- a/libs/hwui/renderthread/RenderProxy.cpp
+++ b/libs/hwui/renderthread/RenderProxy.cpp
@@ -21,6 +21,7 @@
 #include "Properties.h"
 #include "Readback.h"
 #include "Rect.h"
+#include "pipeline/skia/VectorDrawableAtlas.h"
 #include "renderthread/CanvasContext.h"
 #include "renderthread/EglManager.h"
 #include "renderthread/RenderTask.h"
@@ -718,6 +719,19 @@
     mRenderThread.queue(task);
 }
 
+CREATE_BRIDGE1(repackVectorDrawableAtlas, RenderThread* thread) {
+    args->thread->cacheManager().acquireVectorDrawableAtlas()->repackIfNeeded(
+            args->thread->getGrContext());
+    return nullptr;
+}
+
+void RenderProxy::repackVectorDrawableAtlas() {
+    RenderThread& thread = RenderThread::getInstance();
+    SETUP_TASK(repackVectorDrawableAtlas);
+    args->thread = &thread;
+    thread.queue(task);
+}
+
 void* RenderProxy::postAndWait(MethodInvokeRenderTask* task) {
     void* retval;
     task->setReturnPtr(&retval);
diff --git a/libs/hwui/renderthread/RenderProxy.h b/libs/hwui/renderthread/RenderProxy.h
index 31f0ce6..06eaebd 100644
--- a/libs/hwui/renderthread/RenderProxy.h
+++ b/libs/hwui/renderthread/RenderProxy.h
@@ -140,6 +140,9 @@
     static void onBitmapDestroyed(uint32_t pixelRefId);
 
     ANDROID_API static void disableVsync();
+
+    static void repackVectorDrawableAtlas();
+
 private:
     RenderThread& mRenderThread;
     CanvasContext* mContext;
diff --git a/libs/hwui/tests/unit/VectorDrawableAtlasTests.cpp b/libs/hwui/tests/unit/VectorDrawableAtlasTests.cpp
new file mode 100644
index 0000000..17f8176
--- /dev/null
+++ b/libs/hwui/tests/unit/VectorDrawableAtlasTests.cpp
@@ -0,0 +1,164 @@
+/*
+ * 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.
+ */
+
+#include <gtest/gtest.h>
+
+#include "tests/common/TestUtils.h"
+#include <GrRectanizer.h>
+#include "pipeline/skia/VectorDrawableAtlas.h"
+
+using namespace android;
+using namespace android::uirenderer;
+using namespace android::uirenderer::renderthread;
+using namespace android::uirenderer::skiapipeline;
+
+RENDERTHREAD_SKIA_PIPELINE_TEST(VectorDrawableAtlas, addGetRemove) {
+    VectorDrawableAtlas atlas(100*100);
+    atlas.prepareForDraw(renderThread.getGrContext());
+    //create 150 rects 10x10, which won't fit in the atlas (atlas can fit no more than 100 rects)
+    const int MAX_RECTS = 150;
+    AtlasEntry VDRects[MAX_RECTS];
+
+    sk_sp<SkSurface> atlasSurface;
+
+    //check we are able to allocate new rects
+    //check that rects in the atlas do not intersect
+    for (uint32_t i = 0; i < MAX_RECTS; i++) {
+        VDRects[i] = atlas.requestNewEntry(10, 10, renderThread.getGrContext());
+        if (0 == i) {
+            atlasSurface = VDRects[0].surface;
+        }
+        ASSERT_TRUE(VDRects[i].key != INVALID_ATLAS_KEY);
+        ASSERT_TRUE(VDRects[i].surface.get() != nullptr);
+        ASSERT_TRUE(VDRects[i].rect.width() == 10 && VDRects[i].rect.height() == 10);
+
+        //nothing in the atlas should intersect
+        if (atlasSurface.get() == VDRects[i].surface.get()) {
+            for (uint32_t j = 0; j < i; j++) {
+                if (atlasSurface.get() == VDRects[j].surface.get()) {
+                    ASSERT_FALSE(VDRects[i].rect.intersect(VDRects[j].rect));
+                }
+            }
+        }
+    }
+
+    //first 1/3 rects should all be in the same surface
+    for (uint32_t i = 1; i < MAX_RECTS/3; i++) {
+        ASSERT_NE(VDRects[i].key, VDRects[0].key);
+        ASSERT_EQ(VDRects[i].surface.get(), atlasSurface.get());
+    }
+
+    //first rect is using atlas and last is a standalone surface
+    ASSERT_NE(VDRects[0].surface.get(), VDRects[MAX_RECTS-1].surface.get());
+
+    //check getEntry returns the same surfaces that we had created
+    for (uint32_t i = 0; i < MAX_RECTS; i++) {
+        auto VDRect = atlas.getEntry(VDRects[i].key);
+        ASSERT_TRUE(VDRect.key != INVALID_ATLAS_KEY);
+        ASSERT_EQ(VDRects[i].key, VDRect.key);
+        ASSERT_EQ(VDRects[i].surface.get(), VDRect.surface.get());
+        ASSERT_EQ(VDRects[i].rect, VDRect.rect);
+        atlas.releaseEntry(VDRect.key);
+    }
+
+    //check that any new rects will be allocated in the atlas, even that rectanizer is full.
+    //rects in the atlas should not intersect.
+    for (uint32_t i = 0; i < MAX_RECTS/3; i++) {
+        VDRects[i] = atlas.requestNewEntry(10, 10, renderThread.getGrContext());
+        ASSERT_TRUE(VDRects[i].key != INVALID_ATLAS_KEY);
+        ASSERT_EQ(VDRects[i].surface.get(), atlasSurface.get());
+        ASSERT_TRUE(VDRects[i].rect.width() == 10 && VDRects[i].rect.height() == 10);
+        for (uint32_t j = 0; j < i; j++) {
+            ASSERT_FALSE(VDRects[i].rect.intersect(VDRects[j].rect));
+        }
+    }
+}
+
+
+RENDERTHREAD_SKIA_PIPELINE_TEST(VectorDrawableAtlas, disallowSharedSurface) {
+    VectorDrawableAtlas atlas(100*100);
+    //don't allow to use a shared surface
+    atlas.setStorageMode(VectorDrawableAtlas::StorageMode::disallowSharedSurface);
+    atlas.prepareForDraw(renderThread.getGrContext());
+    //create 150 rects 10x10, which won't fit in the atlas (atlas can fit no more than 100 rects)
+    const int MAX_RECTS = 150;
+    AtlasEntry VDRects[MAX_RECTS];
+
+    //check we are able to allocate new rects
+    //check that rects in the atlas use unique surfaces
+    for (uint32_t i = 0; i < MAX_RECTS; i++) {
+        VDRects[i] = atlas.requestNewEntry(10, 10, renderThread.getGrContext());
+        ASSERT_TRUE(VDRects[i].key != INVALID_ATLAS_KEY);
+        ASSERT_TRUE(VDRects[i].surface.get() != nullptr);
+        ASSERT_TRUE(VDRects[i].rect.width() == 10 && VDRects[i].rect.height() == 10);
+
+        //nothing in the atlas should use the same surface
+        for (uint32_t j = 0; j < i; j++) {
+            ASSERT_NE(VDRects[i].surface.get(), VDRects[j].surface.get());
+        }
+    }
+}
+
+RENDERTHREAD_SKIA_PIPELINE_TEST(VectorDrawableAtlas, repack) {
+    VectorDrawableAtlas atlas(100*100);
+    ASSERT_FALSE(atlas.isFragmented());
+    atlas.prepareForDraw(renderThread.getGrContext());
+    ASSERT_FALSE(atlas.isFragmented());
+    //create 150 rects 10x10, which won't fit in the atlas (atlas can fit no more than 100 rects)
+    const int MAX_RECTS = 150;
+    AtlasEntry VDRects[MAX_RECTS];
+
+    sk_sp<SkSurface> atlasSurface;
+
+    //fill the atlas with check we are able to allocate new rects
+    for (uint32_t i = 0; i < MAX_RECTS; i++) {
+        VDRects[i] = atlas.requestNewEntry(10, 10, renderThread.getGrContext());
+        if (0 == i) {
+            atlasSurface = VDRects[0].surface;
+        }
+        ASSERT_TRUE(VDRects[i].key != INVALID_ATLAS_KEY);
+    }
+
+    ASSERT_FALSE(atlas.isFragmented());
+
+    //first 1/3 rects should all be in the same surface
+    for (uint32_t i = 1; i < MAX_RECTS/3; i++) {
+        ASSERT_NE(VDRects[i].key, VDRects[0].key);
+        ASSERT_EQ(VDRects[i].surface.get(), atlasSurface.get());
+    }
+
+    //release all entries
+    for (uint32_t i = 0; i < MAX_RECTS; i++) {
+        auto VDRect = atlas.getEntry(VDRects[i].key);
+        ASSERT_TRUE(VDRect.key != INVALID_ATLAS_KEY);
+        atlas.releaseEntry(VDRect.key);
+    }
+
+    ASSERT_FALSE(atlas.isFragmented());
+
+    //allocate 4x4 rects, which will fragment the atlas badly, because each entry occupies a 10x10
+    //area
+    for (uint32_t i = 0; i < 4*MAX_RECTS; i++) {
+        AtlasEntry entry = atlas.requestNewEntry(4, 4, renderThread.getGrContext());
+        ASSERT_TRUE(entry.key != INVALID_ATLAS_KEY);
+    }
+
+    ASSERT_TRUE(atlas.isFragmented());
+
+    atlas.repackIfNeeded(renderThread.getGrContext());
+
+    ASSERT_FALSE(atlas.isFragmented());
+}
\ No newline at end of file
diff --git a/location/java/com/android/internal/location/gnssmetrics/GnssMetrics.java b/location/java/com/android/internal/location/gnssmetrics/GnssMetrics.java
index f5f1024..833376c 100644
--- a/location/java/com/android/internal/location/gnssmetrics/GnssMetrics.java
+++ b/location/java/com/android/internal/location/gnssmetrics/GnssMetrics.java
@@ -21,6 +21,8 @@
 import android.util.Base64;
 import android.util.TimeUtils;
 
+import java.util.Arrays;
+
 import com.android.internal.location.nano.GnssLogsProto.GnssLog;
 
 /**
@@ -40,6 +42,7 @@
     locationFailureStatistics = new Statistics();
     timeToFirstFixSecStatistics = new Statistics();
     positionAccuracyMeterStatistics = new Statistics();
+    topFourAverageCn0Statistics = new Statistics();
     reset();
   }
 
@@ -95,6 +98,27 @@
     return;
   }
 
+  /*
+  * Logs CN0 when at least 4 SVs are available
+  *
+  */
+  public void logCn0(float[] cn0s, int numSv) {
+    if (numSv < 4) {
+      return;
+    }
+    float[] cn0Array = Arrays.copyOf(cn0s, numSv);
+    Arrays.sort(cn0Array);
+    if (cn0Array[numSv - 4] > 0.0) {
+      double top4AvgCn0 = 0.0;
+      for (int i = numSv - 4; i < numSv; i++) {
+        top4AvgCn0 += (double) cn0Array[i];
+      }
+      top4AvgCn0 /= 4;
+      topFourAverageCn0Statistics.addItem(top4AvgCn0);
+    }
+    return;
+  }
+
   /**
    * Dumps GNSS metrics as a proto string
    * @return
@@ -117,6 +141,12 @@
       msg.standardDeviationPositionAccuracyMeters
           = (int) positionAccuracyMeterStatistics.getStandardDeviation();
     }
+    if (topFourAverageCn0Statistics.getCount() > 0) {
+      msg.numTopFourAverageCn0Processed = topFourAverageCn0Statistics.getCount();
+      msg.meanTopFourAverageCn0DbHz = topFourAverageCn0Statistics.getMean();
+      msg.standardDeviationTopFourAverageCn0DbHz
+          = topFourAverageCn0Statistics.getStandardDeviation();
+    }
     String s = Base64.encodeToString(GnssLog.toByteArray(msg), Base64.DEFAULT);
     reset();
     return s;
@@ -155,6 +185,14 @@
       s.append("  Position accuracy standard deviation (m): ").append(
           positionAccuracyMeterStatistics.getStandardDeviation()).append("\n");
     }
+    s.append("  Number of CN0 reports: ").append(
+        topFourAverageCn0Statistics.getCount()).append("\n");
+    if (topFourAverageCn0Statistics.getCount() > 0) {
+      s.append("  Top 4 Avg CN0 mean (dB-Hz): ").append(
+          topFourAverageCn0Statistics.getMean()).append("\n");
+      s.append("  Top 4 Avg CN0 standard deviation (dB-Hz): ").append(
+          topFourAverageCn0Statistics.getStandardDeviation()).append("\n");
+    }
     s.append("GNSS_KPI_END").append("\n");
     return s.toString();
   }
@@ -211,6 +249,9 @@
   /** Position accuracy statistics */
   private Statistics positionAccuracyMeterStatistics;
 
+  /** Top 4 average CN0 statistics */
+  private Statistics topFourAverageCn0Statistics;
+
   /**
    * Resets GNSS metrics
    */
@@ -221,6 +262,7 @@
     locationFailureStatistics.reset();
     timeToFirstFixSecStatistics.reset();
     positionAccuracyMeterStatistics.reset();
+    topFourAverageCn0Statistics.reset();
     return;
   }
 }
\ No newline at end of file
diff --git a/lowpan/Android.mk b/lowpan/Android.mk
deleted file mode 100644
index 0079958..0000000
--- a/lowpan/Android.mk
+++ /dev/null
@@ -1,33 +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.
-#
-
-LOCAL_PATH := $(call my-dir)
-
-ifneq (,$(findstring lowpan/java,$(FRAMEWORKS_BASE_SUBDIRS)))
-include $(CLEAR_VARS)
-LOCAL_MODULE := libandroid_net_lowpan
-LOCAL_MODULE_TAGS := optional
-LOCAL_SHARED_LIBRARIES += libbase
-LOCAL_SHARED_LIBRARIES += libbinder
-LOCAL_SHARED_LIBRARIES += libutils
-LOCAL_AIDL_INCLUDES += frameworks/native/aidl/binder
-LOCAL_AIDL_INCLUDES += frameworks/base/lowpan/java
-LOCAL_AIDL_INCLUDES += frameworks/base/core/java
-LOCAL_SRC_FILES += $(call all-Iaidl-files-under, java/android/net/lowpan)
-include $(BUILD_SHARED_LIBRARY)
-
-include $(call all-makefiles-under,$(LOCAL_PATH))
-endif
diff --git a/lowpan/java/android/net/lowpan/ILowpanInterface.aidl b/lowpan/java/android/net/lowpan/ILowpanInterface.aidl
index 329e9fa..603dc3c 100644
--- a/lowpan/java/android/net/lowpan/ILowpanInterface.aidl
+++ b/lowpan/java/android/net/lowpan/ILowpanInterface.aidl
@@ -16,103 +16,39 @@
 
 package android.net.lowpan;
 
+import android.net.IpPrefix;
+import android.net.lowpan.ILowpanEnergyScanCallback;
 import android.net.lowpan.ILowpanInterfaceListener;
 import android.net.lowpan.ILowpanNetScanCallback;
-import android.net.lowpan.ILowpanEnergyScanCallback;
-import android.os.PersistableBundle;
-import android.net.IpPrefix;
+import android.net.lowpan.LowpanBeaconInfo;
+import android.net.lowpan.LowpanChannelInfo;
+import android.net.lowpan.LowpanCredential;
+import android.net.lowpan.LowpanIdentity;
+import android.net.lowpan.LowpanProvision;
 
 /** {@hide} */
 interface ILowpanInterface {
 
-    //////////////////////////////////////////////////////////////////////////
-    // Permission String Constants
-
-    /* These are here for the sake of C++ interface implementations. */
+    // These are here for the sake of C++ interface implementations.
 
     const String PERM_ACCESS_LOWPAN_STATE    = "android.permission.ACCESS_LOWPAN_STATE";
     const String PERM_CHANGE_LOWPAN_STATE    = "android.permission.CHANGE_LOWPAN_STATE";
     const String PERM_READ_LOWPAN_CREDENTIAL = "android.permission.READ_LOWPAN_CREDENTIAL";
 
-    //////////////////////////////////////////////////////////////////////////
-    // Property Key Constants
-
-    /** Type: Boolean */
-    const String KEY_INTERFACE_ENABLED      = "android.net.lowpan.property.INTERFACE_ENABLED";
-
-    /** Type: Boolean */
-    const String KEY_INTERFACE_UP           = "android.net.lowpan.property.INTERFACE_UP";
-
-    /** Type: Boolean */
-    const String KEY_INTERFACE_COMMISSIONED = "android.net.lowpan.property.INTERFACE_COMMISSIONED";
-
-    /** Type: Boolean */
-    const String KEY_INTERFACE_CONNECTED    = "android.net.lowpan.property.INTERFACE_CONNECTED";
-
-    /** Type: String */
-    const String KEY_INTERFACE_STATE        = "android.net.lowpan.property.INTERFACE_STATE";
-
-    /** Type: String */
-    const String KEY_NETWORK_NAME             = "android.net.lowpan.property.NETWORK_NAME";
-
-    /** Type: Integer */
-    const String KEY_NETWORK_TYPE             = "android.net.lowpan.property.NETWORK_TYPE";
-
-    /** Type: Integer */
-    const String KEY_NETWORK_PANID            = "android.net.lowpan.property.NETWORK_PANID";
-
-    /** Type: byte[] */
-    const String KEY_NETWORK_XPANID           = "android.net.lowpan.property.NETWORK_XPANID";
-
-    /** Type: String */
-    const String KEY_NETWORK_ROLE             = "android.net.lowpan.property.NETWORK_ROLE";
-
-    /** Type: byte[] */
-    const String KEY_NETWORK_MASTER_KEY       = "android.net.lowpan.property.NETWORK_MASTER_KEY";
-
-    /** Type: Integer */
-    const String KEY_NETWORK_MASTER_KEY_INDEX
-        = "android.net.lowpan.property.NETWORK_MASTER_KEY_INDEX";
-
-    /** Type: int[] */
-    const String KEY_SUPPORTED_CHANNELS = "android.net.lowpan.property.SUPPORTED_CHANNELS";
-
-    /** Type: Integer */
-    const String KEY_CHANNEL            = "android.net.lowpan.property.CHANNEL";
-
-    /** Type: int[] */
+    /**
+     * Channel mask key.
+     * Used for setting a channel mask when starting a scan.
+     * Type: int[]
+     * */
     const String KEY_CHANNEL_MASK       = "android.net.lowpan.property.CHANNEL_MASK";
 
-    /** Type: Integer */
+    /**
+     * Max Transmit Power Key.
+     * Used for setting the maximum transmit power when starting a network scan.
+     * Type: Integer
+     * */
     const String KEY_MAX_TX_POWER       = "android.net.lowpan.property.MAX_TX_POWER";
 
-    /** Type: Integer */
-    const String KEY_RSSI               = "android.net.lowpan.property.RSSI";
-
-    /** Type: Integer */
-    const String KEY_LQI                = "android.net.lowpan.property.LQI";
-
-    /** Type: byte[] */
-    const String KEY_BEACON_ADDRESS     = "android.net.lowpan.property.BEACON_ORIGIN_ADDRESS";
-
-    /** Type: Boolean */
-    const String KEY_BEACON_CAN_ASSIST  = "android.net.lowpan.property.BEACON_CAN_ASSIST";
-
-    /** Type: String */
-    const String DRIVER_VERSION         = "android.net.lowpan.property.DRIVER_VERSION";
-
-    /** Type: String */
-    const String NCP_VERSION            = "android.net.lowpan.property.NCP_VERSION";
-
-    /** Type: byte[]
-     * @hide */
-    const String KEY_EXTENDED_ADDRESS = "android.net.lowpan.property.EXTENDED_ADDRESS";
-
-    /** Type: byte[]
-     * @hide */
-    const String KEY_MAC_ADDRESS      = "android.net.lowpan.property.MAC_ADDRESS";
-
-    //////////////////////////////////////////////////////////////////////////
     // Interface States
 
     const String STATE_OFFLINE = "offline";
@@ -121,58 +57,87 @@
     const String STATE_ATTACHED = "attached";
     const String STATE_FAULT = "fault";
 
-    //////////////////////////////////////////////////////////////////////////
     // Device Roles
 
     const String ROLE_END_DEVICE = "end-device";
     const String ROLE_ROUTER = "router";
     const String ROLE_SLEEPY_END_DEVICE = "sleepy-end-device";
     const String ROLE_SLEEPY_ROUTER = "sleepy-router";
-    const String ROLE_UNKNOWN = "unknown";
+    const String ROLE_LEADER = "leader";
+    const String ROLE_COORDINATOR = "coordinator";
+    const String ROLE_DETACHED = "detached";
 
-    //////////////////////////////////////////////////////////////////////////
+    const String NETWORK_TYPE_UNKNOWN = "unknown";
+
+    /**
+     * Network type for Thread 1.x networks.
+     *
+     * @see android.net.lowpan.LowpanIdentity#getType
+     * @see #getLowpanIdentity
+     */
+    const String NETWORK_TYPE_THREAD_V1 = "org.threadgroup.thread.v1";
+
     // Service-Specific Error Code Constants
 
     const int ERROR_UNSPECIFIED = 1;
     const int ERROR_INVALID_ARGUMENT = 2;
     const int ERROR_DISABLED = 3;
     const int ERROR_WRONG_STATE = 4;
-    const int ERROR_INVALID_TYPE = 5;
-    const int ERROR_INVALID_VALUE = 6;
-    const int ERROR_TIMEOUT = 7;
-    const int ERROR_IO_FAILURE = 8;
-    const int ERROR_BUSY = 9;
-    const int ERROR_ALREADY = 10;
-    const int ERROR_CANCELED = 11;
-    const int ERROR_CREDENTIAL_NEEDED = 12;
-    const int ERROR_FEATURE_NOT_SUPPORTED = 14;
-    const int ERROR_PROPERTY_NOT_FOUND = 16;
-    const int ERROR_JOIN_FAILED_UNKNOWN = 18;
-    const int ERROR_JOIN_FAILED_AT_SCAN = 19;
-    const int ERROR_JOIN_FAILED_AT_AUTH = 20;
-    const int ERROR_FORM_FAILED_AT_SCAN = 21;
-    const int ERROR_NCP_PROBLEM = 27;
-    const int ERROR_PERMISSION_DENIED = 28;
+    const int ERROR_TIMEOUT = 5;
+    const int ERROR_IO_FAILURE = 6;
+    const int ERROR_NCP_PROBLEM = 7;
+    const int ERROR_BUSY = 8;
+    const int ERROR_ALREADY = 9;
+    const int ERROR_CANCELED = 10;
+    const int ERROR_FEATURE_NOT_SUPPORTED = 11;
+    const int ERROR_JOIN_FAILED_UNKNOWN = 12;
+    const int ERROR_JOIN_FAILED_AT_SCAN = 13;
+    const int ERROR_JOIN_FAILED_AT_AUTH = 14;
+    const int ERROR_FORM_FAILED_AT_SCAN = 15;
 
-    //////////////////////////////////////////////////////////////////////////
     // Methods
 
     @utf8InCpp String getName();
 
-    void join(in Map parameters);
-    void form(in Map parameters);
+    @utf8InCpp String getNcpVersion();
+    @utf8InCpp String getDriverVersion();
+    LowpanChannelInfo[] getSupportedChannels();
+    @utf8InCpp String[] getSupportedNetworkTypes();
+    byte[] getMacAddress();
+
+    boolean isEnabled();
+    void setEnabled(boolean enabled);
+
+    boolean isUp();
+    boolean isCommissioned();
+    boolean isConnected();
+    @utf8InCpp String getState();
+
+    @utf8InCpp String getRole();
+    @utf8InCpp String getPartitionId();
+    byte[] getExtendedAddress();
+
+    LowpanIdentity getLowpanIdentity();
+    LowpanCredential getLowpanCredential();
+
+    @utf8InCpp String[] getLinkAddresses();
+    IpPrefix[] getLinkNetworks();
+
+    void join(in LowpanProvision provision);
+    void form(in LowpanProvision provision);
+    void attach(in LowpanProvision provision);
     void leave();
     void reset();
 
+    void startCommissioningSession(in LowpanBeaconInfo beaconInfo);
+    void closeCommissioningSession();
+    oneway void sendToCommissioner(in byte[] packet);
+
     void beginLowPower();
-    void pollForData();
+    oneway void pollForData();
 
     oneway void onHostWake();
 
-    @utf8InCpp String[] getPropertyKeys();
-    Map getProperties(in @utf8InCpp String[] keys);
-    void setProperties(in Map properties);
-
     void addListener(ILowpanInterfaceListener listener);
     oneway void removeListener(ILowpanInterfaceListener listener);
 
@@ -182,14 +147,9 @@
     void startEnergyScan(in Map properties, ILowpanEnergyScanCallback listener);
     oneway void stopEnergyScan();
 
-    String[] copyLinkAddresses();
-    IpPrefix[] copyLinkNetworks();
-
     void addOnMeshPrefix(in IpPrefix prefix, int flags);
     oneway void removeOnMeshPrefix(in IpPrefix prefix);
 
     void addExternalRoute(in IpPrefix prefix, int flags);
     oneway void removeExternalRoute(in IpPrefix prefix);
-
-    @utf8InCpp String getPropertyAsString(@utf8InCpp String key);
 }
diff --git a/lowpan/java/android/net/lowpan/ILowpanInterfaceListener.aidl b/lowpan/java/android/net/lowpan/ILowpanInterfaceListener.aidl
index 0ae01a1..5e4049a 100644
--- a/lowpan/java/android/net/lowpan/ILowpanInterfaceListener.aidl
+++ b/lowpan/java/android/net/lowpan/ILowpanInterfaceListener.aidl
@@ -17,14 +17,29 @@
 package android.net.lowpan;
 
 import android.net.IpPrefix;
+import android.net.lowpan.LowpanIdentity;
 
 /** {@hide} */
 interface ILowpanInterfaceListener {
-    oneway void onPropertiesChanged(in Map properties);
+    oneway void onEnabledChanged(boolean value);
 
-    oneway void onLinkNetworkAdded(in IpPrefix prefix);
-    oneway void onLinkNetworkRemoved(in IpPrefix prefix);
+    oneway void onConnectedChanged(boolean value);
 
-    oneway void onLinkAddressAdded(in String address);
-    oneway void onLinkAddressRemoved(in String address);
+    oneway void onUpChanged(boolean value);
+
+    oneway void onRoleChanged(@utf8InCpp String value);
+
+    oneway void onStateChanged(@utf8InCpp String value);
+
+    oneway void onLowpanIdentityChanged(in LowpanIdentity value);
+
+    oneway void onLinkNetworkAdded(in IpPrefix value);
+
+    oneway void onLinkNetworkRemoved(in IpPrefix value);
+
+    oneway void onLinkAddressAdded(@utf8InCpp String value);
+
+    oneway void onLinkAddressRemoved(@utf8InCpp String value);
+
+    oneway void onReceiveFromCommissioner(in byte[] packet);
 }
diff --git a/lowpan/java/android/net/lowpan/ILowpanNetScanCallback.aidl b/lowpan/java/android/net/lowpan/ILowpanNetScanCallback.aidl
index c20a6f8..9743fce 100644
--- a/lowpan/java/android/net/lowpan/ILowpanNetScanCallback.aidl
+++ b/lowpan/java/android/net/lowpan/ILowpanNetScanCallback.aidl
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2016 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.
@@ -16,8 +16,10 @@
 
 package android.net.lowpan;
 
+import android.net.lowpan.LowpanBeaconInfo;
+
 /** {@hide} */
 interface ILowpanNetScanCallback {
-    oneway void onNetScanBeacon(in Map parameters);
+    oneway void onNetScanBeacon(in LowpanBeaconInfo beacon);
     oneway void onNetScanFinished();
 }
diff --git a/lowpan/java/android/net/lowpan/InterfaceDisabledException.java b/lowpan/java/android/net/lowpan/InterfaceDisabledException.java
new file mode 100644
index 0000000..e917d45
--- /dev/null
+++ b/lowpan/java/android/net/lowpan/InterfaceDisabledException.java
@@ -0,0 +1,41 @@
+/*
+ * 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.net.lowpan;
+
+/**
+ * Exception indicating this operation requires the interface to be enabled.
+ *
+ * @see LowpanInterface
+ * @hide
+ */
+// @SystemApi
+public class InterfaceDisabledException extends LowpanException {
+
+    public InterfaceDisabledException() {}
+
+    public InterfaceDisabledException(String message) {
+        super(message);
+    }
+
+    public InterfaceDisabledException(String message, Throwable cause) {
+        super(message, cause);
+    }
+
+    protected InterfaceDisabledException(Exception cause) {
+        super(cause);
+    }
+}
diff --git a/lowpan/java/android/net/lowpan/JoinFailedAtAuthException.java b/lowpan/java/android/net/lowpan/JoinFailedAtAuthException.java
new file mode 100644
index 0000000..7aceb71
--- /dev/null
+++ b/lowpan/java/android/net/lowpan/JoinFailedAtAuthException.java
@@ -0,0 +1,41 @@
+/*
+ * 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.net.lowpan;
+
+/**
+ * Exception indicating the join operation was unable to find the given network.
+ *
+ * @see LowpanInterface
+ * @hide
+ */
+// @SystemApi
+public class JoinFailedAtAuthException extends JoinFailedException {
+
+    public JoinFailedAtAuthException() {}
+
+    public JoinFailedAtAuthException(String message) {
+        super(message);
+    }
+
+    public JoinFailedAtAuthException(String message, Throwable cause) {
+        super(message, cause);
+    }
+
+    public JoinFailedAtAuthException(Exception cause) {
+        super(cause);
+    }
+}
diff --git a/lowpan/java/android/net/lowpan/JoinFailedAtScanException.java b/lowpan/java/android/net/lowpan/JoinFailedAtScanException.java
new file mode 100644
index 0000000..a4346f98
--- /dev/null
+++ b/lowpan/java/android/net/lowpan/JoinFailedAtScanException.java
@@ -0,0 +1,41 @@
+/*
+ * 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.net.lowpan;
+
+/**
+ * Exception indicating the join operation was unable to find the given network.
+ *
+ * @see LowpanInterface
+ * @hide
+ */
+// @SystemApi
+public class JoinFailedAtScanException extends JoinFailedException {
+
+    public JoinFailedAtScanException() {}
+
+    public JoinFailedAtScanException(String message) {
+        super(message);
+    }
+
+    public JoinFailedAtScanException(String message, Throwable cause) {
+        super(message, cause);
+    }
+
+    public JoinFailedAtScanException(Exception cause) {
+        super(cause);
+    }
+}
diff --git a/lowpan/java/android/net/lowpan/JoinFailedException.java b/lowpan/java/android/net/lowpan/JoinFailedException.java
new file mode 100644
index 0000000..e51d382
--- /dev/null
+++ b/lowpan/java/android/net/lowpan/JoinFailedException.java
@@ -0,0 +1,41 @@
+/*
+ * 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.net.lowpan;
+
+/**
+ * Exception indicating the join operation has failed.
+ *
+ * @see LowpanInterface
+ * @hide
+ */
+// @SystemApi
+public class JoinFailedException extends LowpanException {
+
+    public JoinFailedException() {}
+
+    public JoinFailedException(String message) {
+        super(message);
+    }
+
+    public JoinFailedException(String message, Throwable cause) {
+        super(message, cause);
+    }
+
+    protected JoinFailedException(Exception cause) {
+        super(cause);
+    }
+}
diff --git a/lowpan/java/android/net/lowpan/LowpanBeaconInfo.aidl b/lowpan/java/android/net/lowpan/LowpanBeaconInfo.aidl
new file mode 100644
index 0000000..9464fea
--- /dev/null
+++ b/lowpan/java/android/net/lowpan/LowpanBeaconInfo.aidl
@@ -0,0 +1,19 @@
+/**
+ * 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.net.lowpan;
+
+parcelable LowpanBeaconInfo cpp_header "android/net/lowpan/LowpanBeaconInfo.h";
diff --git a/lowpan/java/android/net/lowpan/LowpanBeaconInfo.java b/lowpan/java/android/net/lowpan/LowpanBeaconInfo.java
index e18abd5..345a4c5 100644
--- a/lowpan/java/android/net/lowpan/LowpanBeaconInfo.java
+++ b/lowpan/java/android/net/lowpan/LowpanBeaconInfo.java
@@ -16,9 +16,12 @@
 
 package android.net.lowpan;
 
+import android.os.Parcel;
+import android.os.Parcelable;
 import com.android.internal.util.HexDump;
+import java.util.Arrays;
 import java.util.Collection;
-import java.util.Map;
+import java.util.Objects;
 import java.util.TreeSet;
 
 /**
@@ -27,70 +30,93 @@
  * @hide
  */
 // @SystemApi
-public class LowpanBeaconInfo extends LowpanIdentity {
+public class LowpanBeaconInfo implements Parcelable {
+    public static final int UNKNOWN_RSSI = Integer.MAX_VALUE;
+    public static final int UNKNOWN_LQI = 0;
 
-    private int mRssi = UNKNOWN;
-    private int mLqi = UNKNOWN;
+    private LowpanIdentity mIdentity;
+    private int mRssi = UNKNOWN_RSSI;
+    private int mLqi = UNKNOWN_LQI;
     private byte[] mBeaconAddress = null;
     private final TreeSet<Integer> mFlags = new TreeSet<>();
 
     public static final int FLAG_CAN_ASSIST = 1;
 
-    static class Builder extends LowpanIdentity.Builder {
-        private final LowpanBeaconInfo identity = new LowpanBeaconInfo();
+    /** @hide */
+    public static class Builder {
+        final LowpanIdentity.Builder mIdentityBuilder = new LowpanIdentity.Builder();
+        final LowpanBeaconInfo mBeaconInfo = new LowpanBeaconInfo();
+
+        public Builder setLowpanIdentity(LowpanIdentity x) {
+            mIdentityBuilder.setLowpanIdentity(x);
+            return this;
+        }
+
+        public Builder setName(String x) {
+            mIdentityBuilder.setName(x);
+            return this;
+        }
+
+        public Builder setXpanid(byte x[]) {
+            mIdentityBuilder.setXpanid(x);
+            return this;
+        }
+
+        public Builder setPanid(int x) {
+            mIdentityBuilder.setPanid(x);
+            return this;
+        }
+
+        public Builder setChannel(int x) {
+            mIdentityBuilder.setChannel(x);
+            return this;
+        }
+
+        public Builder setType(String x) {
+            mIdentityBuilder.setType(x);
+            return this;
+        }
 
         public Builder setRssi(int x) {
-            identity.mRssi = x;
+            mBeaconInfo.mRssi = x;
             return this;
         }
 
         public Builder setLqi(int x) {
-            identity.mLqi = x;
+            mBeaconInfo.mLqi = x;
             return this;
         }
 
         public Builder setBeaconAddress(byte x[]) {
-            identity.mBeaconAddress = x.clone();
+            mBeaconInfo.mBeaconAddress = (x != null ? x.clone() : null);
             return this;
         }
 
         public Builder setFlag(int x) {
-            identity.mFlags.add(x);
+            mBeaconInfo.mFlags.add(x);
             return this;
         }
 
         public Builder setFlags(Collection<Integer> x) {
-            identity.mFlags.addAll(x);
-            return this;
-        }
-
-        /** @hide */
-        Builder updateFromMap(Map map) {
-            if (map.containsKey(LowpanProperties.KEY_RSSI.getName())) {
-                setRssi(LowpanProperties.KEY_RSSI.getFromMap(map));
-            }
-            if (map.containsKey(LowpanProperties.KEY_LQI.getName())) {
-                setLqi(LowpanProperties.KEY_LQI.getFromMap(map));
-            }
-            if (map.containsKey(LowpanProperties.KEY_BEACON_ADDRESS.getName())) {
-                setBeaconAddress(LowpanProperties.KEY_BEACON_ADDRESS.getFromMap(map));
-            }
-            identity.mFlags.clear();
-            if (map.containsKey(LowpanProperties.KEY_BEACON_CAN_ASSIST.getName())
-                    && LowpanProperties.KEY_BEACON_CAN_ASSIST.getFromMap(map).booleanValue()) {
-                setFlag(FLAG_CAN_ASSIST);
-            }
-            super.updateFromMap(map);
+            mBeaconInfo.mFlags.addAll(x);
             return this;
         }
 
         public LowpanBeaconInfo build() {
-            return identity;
+            mBeaconInfo.mIdentity = mIdentityBuilder.build();
+            if (mBeaconInfo.mBeaconAddress == null) {
+                mBeaconInfo.mBeaconAddress = new byte[0];
+            }
+            return mBeaconInfo;
         }
     }
 
     private LowpanBeaconInfo() {}
 
+    public LowpanIdentity getLowpanIdentity() {
+        return mIdentity;
+    }
+
     public int getRssi() {
         return mRssi;
     }
@@ -112,26 +138,21 @@
     }
 
     @Override
-    void addToMap(Map<String, Object> parameters) {
-        super.addToMap(parameters);
-    }
-
-    @Override
     public String toString() {
         StringBuffer sb = new StringBuffer();
 
-        sb.append(super.toString());
+        sb.append(mIdentity.toString());
 
-        if (mRssi != UNKNOWN) {
-            sb.append(", RSSI: ").append(mRssi);
+        if (mRssi != UNKNOWN_RSSI) {
+            sb.append(", RSSI:").append(mRssi).append("dBm");
         }
 
-        if (mLqi != UNKNOWN) {
-            sb.append(", LQI: ").append(mLqi);
+        if (mLqi != UNKNOWN_LQI) {
+            sb.append(", LQI:").append(mLqi);
         }
 
-        if (mBeaconAddress != null) {
-            sb.append(", BeaconAddress: ").append(HexDump.toHexString(mBeaconAddress));
+        if (mBeaconAddress.length > 0) {
+            sb.append(", BeaconAddress:").append(HexDump.toHexString(mBeaconAddress));
         }
 
         for (Integer flag : mFlags) {
@@ -147,4 +168,67 @@
 
         return sb.toString();
     }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(mIdentity, mRssi, mLqi, Arrays.hashCode(mBeaconAddress), mFlags);
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (!(obj instanceof LowpanBeaconInfo)) {
+            return false;
+        }
+        LowpanBeaconInfo rhs = (LowpanBeaconInfo) obj;
+        return mIdentity.equals(rhs.mIdentity)
+                && Arrays.equals(mBeaconAddress, rhs.mBeaconAddress)
+                && mRssi == rhs.mRssi
+                && mLqi == rhs.mLqi
+                && mFlags.equals(rhs.mFlags);
+    }
+
+    /** Implement the Parcelable interface. */
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    /** Implement the Parcelable interface. */
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        mIdentity.writeToParcel(dest, flags);
+        dest.writeInt(mRssi);
+        dest.writeInt(mLqi);
+        dest.writeByteArray(mBeaconAddress);
+
+        dest.writeInt(mFlags.size());
+        for (Integer val : mFlags) {
+            dest.writeInt(val);
+        }
+    }
+
+    /** Implement the Parcelable interface. */
+    public static final Creator<LowpanBeaconInfo> CREATOR =
+            new Creator<LowpanBeaconInfo>() {
+                public LowpanBeaconInfo createFromParcel(Parcel in) {
+                    Builder builder = new Builder();
+
+                    builder.setLowpanIdentity(LowpanIdentity.CREATOR.createFromParcel(in));
+
+                    builder.setRssi(in.readInt());
+                    builder.setLqi(in.readInt());
+
+                    builder.setBeaconAddress(in.createByteArray());
+
+                    for (int i = in.readInt(); i > 0; i--) {
+                        builder.setFlag(in.readInt());
+                    }
+
+                    return builder.build();
+                }
+
+                public LowpanBeaconInfo[] newArray(int size) {
+                    return new LowpanBeaconInfo[size];
+                }
+            };
 }
diff --git a/lowpan/java/android/net/lowpan/LowpanChannelInfo.aidl b/lowpan/java/android/net/lowpan/LowpanChannelInfo.aidl
new file mode 100644
index 0000000..0676deb
--- /dev/null
+++ b/lowpan/java/android/net/lowpan/LowpanChannelInfo.aidl
@@ -0,0 +1,19 @@
+/**
+ * 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.net.lowpan;
+
+parcelable LowpanChannelInfo cpp_header "android/net/lowpan/LowpanChannelInfo.h";
diff --git a/lowpan/java/android/net/lowpan/LowpanChannelInfo.java b/lowpan/java/android/net/lowpan/LowpanChannelInfo.java
index 621affe..52b1c6d 100644
--- a/lowpan/java/android/net/lowpan/LowpanChannelInfo.java
+++ b/lowpan/java/android/net/lowpan/LowpanChannelInfo.java
@@ -16,22 +16,66 @@
 
 package android.net.lowpan;
 
-/** Provides detailed information about a given channel. */
+import android.os.Parcel;
+import android.os.Parcelable;
+import java.util.Objects;
+
+/**
+ * Provides detailed information about a given channel.
+ *
+ * @hide
+ */
 // @SystemApi
-public class LowpanChannelInfo {
+public class LowpanChannelInfo implements Parcelable {
 
     public static final int UNKNOWN_POWER = Integer.MAX_VALUE;
+    public static final float UNKNOWN_FREQUENCY = 0.0f;
+    public static final float UNKNOWN_BANDWIDTH = 0.0f;
 
-    // Instance Variables
-
-    private String mName = null;
     private int mIndex = 0;
-    private boolean mIsMaskedByRegulatoryDomain = false;
-    private float mSpectrumCenterFrequency = 0.0f;
-    private float mSpectrumBandwidth = 0.0f;
+    private String mName = null;
+    private float mSpectrumCenterFrequency = UNKNOWN_FREQUENCY;
+    private float mSpectrumBandwidth = UNKNOWN_BANDWIDTH;
     private int mMaxTransmitPower = UNKNOWN_POWER;
+    private boolean mIsMaskedByRegulatoryDomain = false;
 
-    // Public Getters and Setters
+    /** @hide */
+    public static LowpanChannelInfo getChannelInfoForIeee802154Page0(int index) {
+        LowpanChannelInfo info = new LowpanChannelInfo();
+
+        if (index < 0) {
+            info = null;
+
+        } else if (index == 0) {
+            info.mSpectrumCenterFrequency = 868300000.0f;
+            info.mSpectrumBandwidth = 600000.0f;
+
+        } else if (index < 11) {
+            info.mSpectrumCenterFrequency = 906000000.0f - (2000000.0f * 1) + 2000000.0f * (index);
+            info.mSpectrumBandwidth = 0; // Unknown
+
+        } else if (index < 26) {
+            info.mSpectrumCenterFrequency =
+                    2405000000.0f - (5000000.0f * 11) + 5000000.0f * (index);
+            info.mSpectrumBandwidth = 2000000.0f;
+
+        } else {
+            info = null;
+        }
+
+        info.mName = Integer.toString(index);
+
+        return info;
+    }
+
+    private LowpanChannelInfo() {}
+
+    private LowpanChannelInfo(int index, String name, float cf, float bw) {
+        mIndex = index;
+        mName = name;
+        mSpectrumCenterFrequency = cf;
+        mSpectrumBandwidth = bw;
+    }
 
     public String getName() {
         return mName;
@@ -63,22 +107,110 @@
 
         sb.append("Channel ").append(mIndex);
 
-        if (mName != null) {
+        if (mName != null && !mName.equals(Integer.toString(mIndex))) {
             sb.append(" (").append(mName).append(")");
         }
 
         if (mSpectrumCenterFrequency > 0.0f) {
-            sb.append(", SpectrumCenterFrequency: ").append(mSpectrumCenterFrequency).append("Hz");
+            if (mSpectrumCenterFrequency > 1000000000.0f) {
+                sb.append(", SpectrumCenterFrequency: ")
+                        .append(mSpectrumCenterFrequency / 1000000000.0f)
+                        .append("GHz");
+            } else if (mSpectrumCenterFrequency > 1000000.0f) {
+                sb.append(", SpectrumCenterFrequency: ")
+                        .append(mSpectrumCenterFrequency / 1000000.0f)
+                        .append("MHz");
+            } else {
+                sb.append(", SpectrumCenterFrequency: ")
+                        .append(mSpectrumCenterFrequency / 1000.0f)
+                        .append("kHz");
+            }
         }
 
         if (mSpectrumBandwidth > 0.0f) {
-            sb.append(", SpectrumBandwidth: ").append(mSpectrumBandwidth).append("Hz");
+            if (mSpectrumBandwidth > 1000000000.0f) {
+                sb.append(", SpectrumBandwidth: ")
+                        .append(mSpectrumBandwidth / 1000000000.0f)
+                        .append("GHz");
+            } else if (mSpectrumBandwidth > 1000000.0f) {
+                sb.append(", SpectrumBandwidth: ")
+                        .append(mSpectrumBandwidth / 1000000.0f)
+                        .append("MHz");
+            } else {
+                sb.append(", SpectrumBandwidth: ")
+                        .append(mSpectrumBandwidth / 1000.0f)
+                        .append("kHz");
+            }
         }
 
         if (mMaxTransmitPower != UNKNOWN_POWER) {
-            sb.append(", MaxTransmitPower: ").append(mMaxTransmitPower);
+            sb.append(", MaxTransmitPower: ").append(mMaxTransmitPower).append("dBm");
         }
 
         return sb.toString();
     }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (!(obj instanceof LowpanChannelInfo)) {
+            return false;
+        }
+        LowpanChannelInfo rhs = (LowpanChannelInfo) obj;
+        return Objects.equals(mName, rhs.mName)
+                && mIndex == rhs.mIndex
+                && mIsMaskedByRegulatoryDomain == rhs.mIsMaskedByRegulatoryDomain
+                && mSpectrumCenterFrequency == rhs.mSpectrumCenterFrequency
+                && mSpectrumBandwidth == rhs.mSpectrumBandwidth
+                && mMaxTransmitPower == rhs.mMaxTransmitPower;
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(
+                mName,
+                mIndex,
+                mIsMaskedByRegulatoryDomain,
+                mSpectrumCenterFrequency,
+                mSpectrumBandwidth,
+                mMaxTransmitPower);
+    }
+
+    /** Implement the Parcelable interface. */
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    /** Implement the Parcelable interface. */
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeInt(mIndex);
+        dest.writeString(mName);
+        dest.writeFloat(mSpectrumCenterFrequency);
+        dest.writeFloat(mSpectrumBandwidth);
+        dest.writeInt(mMaxTransmitPower);
+        dest.writeBoolean(mIsMaskedByRegulatoryDomain);
+    }
+
+    /** Implement the Parcelable interface. */
+    public static final Creator<LowpanChannelInfo> CREATOR =
+            new Creator<LowpanChannelInfo>() {
+
+                public LowpanChannelInfo createFromParcel(Parcel in) {
+                    LowpanChannelInfo info = new LowpanChannelInfo();
+
+                    info.mIndex = in.readInt();
+                    info.mName = in.readString();
+                    info.mSpectrumCenterFrequency = in.readFloat();
+                    info.mSpectrumBandwidth = in.readFloat();
+                    info.mMaxTransmitPower = in.readInt();
+                    info.mIsMaskedByRegulatoryDomain = in.readBoolean();
+
+                    return info;
+                }
+
+                public LowpanChannelInfo[] newArray(int size) {
+                    return new LowpanChannelInfo[size];
+                }
+            };
 }
diff --git a/lowpan/java/android/net/lowpan/LowpanCommissioningSession.java b/lowpan/java/android/net/lowpan/LowpanCommissioningSession.java
index 1da085d..8f75e8d 100644
--- a/lowpan/java/android/net/lowpan/LowpanCommissioningSession.java
+++ b/lowpan/java/android/net/lowpan/LowpanCommissioningSession.java
@@ -18,9 +18,11 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
-import android.net.Network;
+import android.net.IpPrefix;
+import android.os.DeadObjectException;
 import android.os.Handler;
-import java.net.InetSocketAddress;
+import android.os.Looper;
+import android.os.RemoteException;
 
 /**
  * Commissioning Session.
@@ -31,8 +33,15 @@
  * @hide
  */
 // @SystemApi
-public abstract class LowpanCommissioningSession {
-    public LowpanCommissioningSession() {}
+public class LowpanCommissioningSession {
+
+    private final ILowpanInterface mBinder;
+    private final LowpanBeaconInfo mBeaconInfo;
+    private final ILowpanInterfaceListener mInternalCallback = new InternalCallback();
+    private final Looper mLooper;
+    private Handler mHandler;
+    private Callback mCallback = null;
+    private volatile boolean mIsClosed = false;
 
     /**
      * Callback base class for {@link LowpanCommissioningSession}
@@ -40,34 +49,175 @@
      * @hide
      */
     // @SystemApi
-    public class Callback {
+    public abstract static class Callback {
         public void onReceiveFromCommissioner(@NonNull byte[] packet) {};
 
         public void onClosed() {};
     }
 
+    private class InternalCallback extends ILowpanInterfaceListener.Stub {
+        @Override
+        public void onStateChanged(String value) {
+            if (!mIsClosed) {
+                switch (value) {
+                    case ILowpanInterface.STATE_OFFLINE:
+                    case ILowpanInterface.STATE_FAULT:
+                        synchronized (LowpanCommissioningSession.this) {
+                            lockedCleanup();
+                        }
+                }
+            }
+        }
+
+        @Override
+        public void onReceiveFromCommissioner(byte[] packet) {
+            mHandler.post(
+                    () -> {
+                        synchronized (LowpanCommissioningSession.this) {
+                            if (!mIsClosed && (mCallback != null)) {
+                                mCallback.onReceiveFromCommissioner(packet);
+                            }
+                        }
+                    });
+        }
+
+        // We ignore all other callbacks.
+        @Override
+        public void onEnabledChanged(boolean value) {}
+
+        @Override
+        public void onConnectedChanged(boolean value) {}
+
+        @Override
+        public void onUpChanged(boolean value) {}
+
+        @Override
+        public void onRoleChanged(String value) {}
+
+        @Override
+        public void onLowpanIdentityChanged(LowpanIdentity value) {}
+
+        @Override
+        public void onLinkNetworkAdded(IpPrefix value) {}
+
+        @Override
+        public void onLinkNetworkRemoved(IpPrefix value) {}
+
+        @Override
+        public void onLinkAddressAdded(String value) {}
+
+        @Override
+        public void onLinkAddressRemoved(String value) {}
+    }
+
+    LowpanCommissioningSession(
+            ILowpanInterface binder, LowpanBeaconInfo beaconInfo, Looper looper) {
+        mBinder = binder;
+        mBeaconInfo = beaconInfo;
+        mLooper = looper;
+
+        if (mLooper != null) {
+            mHandler = new Handler(mLooper);
+        } else {
+            mHandler = new Handler();
+        }
+
+        try {
+            mBinder.addListener(mInternalCallback);
+
+        } catch (RemoteException x) {
+            throw x.rethrowAsRuntimeException();
+        }
+    }
+
+    private void lockedCleanup() {
+        // Note: this method is only called from synchronized contexts.
+
+        if (!mIsClosed) {
+            try {
+                mBinder.removeListener(mInternalCallback);
+
+            } catch (DeadObjectException x) {
+                /* We don't care if we receive a DOE at this point.
+                 * DOE is as good as success as far as we are concerned.
+                 */
+
+            } catch (RemoteException x) {
+                throw x.rethrowAsRuntimeException();
+            }
+
+            if (mCallback != null) {
+                mHandler.post(() -> mCallback.onClosed());
+            }
+        }
+
+        mCallback = null;
+        mIsClosed = true;
+    }
+
     /** TODO: doc */
     @NonNull
-    public abstract LowpanBeaconInfo getBeaconInfo();
+    public LowpanBeaconInfo getBeaconInfo() {
+        return mBeaconInfo;
+    }
 
     /** TODO: doc */
-    public abstract void sendToCommissioner(@NonNull byte[] packet);
+    public void sendToCommissioner(@NonNull byte[] packet) {
+        if (!mIsClosed) {
+            try {
+                mBinder.sendToCommissioner(packet);
+
+            } catch (DeadObjectException x) {
+                /* This method is a best-effort delivery.
+                 * We don't care if we receive a DOE at this point.
+                 */
+
+            } catch (RemoteException x) {
+                throw x.rethrowAsRuntimeException();
+            }
+        }
+    }
 
     /** TODO: doc */
-    public abstract void setCallback(@Nullable Callback cb, @Nullable Handler handler);
+    public synchronized void setCallback(@Nullable Callback cb, @Nullable Handler handler) {
+        if (!mIsClosed) {
+            /* This class can be created with or without a default looper.
+             * Also, this method can be called with or without a specific
+             * handler. If a handler is specified, it is to always be used.
+             * Otherwise, if there was a Looper specified when this object
+             * was created, we create a new handle based on that looper.
+             * Otherwise we just create a default handler object. Since we
+             * don't really know how the previous handler was created, we
+             * end up always replacing it here. This isn't a huge problem
+             * because this method should be called infrequently.
+             */
+            if (handler != null) {
+                mHandler = handler;
+            } else if (mLooper != null) {
+                mHandler = new Handler(mLooper);
+            } else {
+                mHandler = new Handler();
+            }
+            mCallback = cb;
+        }
+    }
 
     /** TODO: doc */
-    public abstract void close();
+    public synchronized void close() {
+        if (!mIsClosed) {
+            try {
+                mBinder.closeCommissioningSession();
 
-    /**
-     * This method is largely for Nest Weave, as an alternative to {@link #sendToCommissioner()}
-     * and @{link Callback#onReceiveFromCommissioner()}.
-     *
-     * <p>When used with the Network instance obtained from getNetwork(), the caller can use the
-     * given InetSocketAddress to communicate with the commissioner using a UDP (or, under certain
-     * circumstances, TCP) socket.
-     */
-    public abstract @Nullable InetSocketAddress getInetSocketAddress();
+                lockedCleanup();
 
-    public abstract @Nullable Network getNetwork();
+            } catch (DeadObjectException x) {
+                /* We don't care if we receive a DOE at this point.
+                 * DOE is as good as success as far as we are concerned.
+                 */
+
+            } catch (RemoteException x) {
+                throw x.rethrowAsRuntimeException();
+            }
+        }
+    }
 }
diff --git a/lowpan/java/android/net/lowpan/LowpanCredential.aidl b/lowpan/java/android/net/lowpan/LowpanCredential.aidl
new file mode 100644
index 0000000..af0c2d6
--- /dev/null
+++ b/lowpan/java/android/net/lowpan/LowpanCredential.aidl
@@ -0,0 +1,19 @@
+/**
+ * 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.net.lowpan;
+
+parcelable LowpanCredential cpp_header "android/net/lowpan/LowpanCredential.h";
diff --git a/lowpan/java/android/net/lowpan/LowpanCredential.java b/lowpan/java/android/net/lowpan/LowpanCredential.java
index ca86021..e9126f9 100644
--- a/lowpan/java/android/net/lowpan/LowpanCredential.java
+++ b/lowpan/java/android/net/lowpan/LowpanCredential.java
@@ -16,7 +16,11 @@
 
 package android.net.lowpan;
 
-import java.util.Map;
+import android.os.Parcel;
+import android.os.Parcelable;
+import com.android.internal.util.HexDump;
+import java.util.Arrays;
+import java.util.Objects;
 
 /**
  * Describes a credential for a LoWPAN network.
@@ -24,7 +28,7 @@
  * @hide
  */
 // @SystemApi
-public class LowpanCredential {
+public class LowpanCredential implements Parcelable {
 
     public static final int UNSPECIFIED_KEY_INDEX = 0;
 
@@ -49,18 +53,18 @@
         return new LowpanCredential(masterKey, keyIndex);
     }
 
-    public void setMasterKey(byte[] masterKey) {
+    void setMasterKey(byte[] masterKey) {
         if (masterKey != null) {
             masterKey = masterKey.clone();
         }
         mMasterKey = masterKey;
     }
 
-    public void setMasterKeyIndex(int keyIndex) {
+    void setMasterKeyIndex(int keyIndex) {
         mMasterKeyIndex = keyIndex;
     }
 
-    public void setMasterKey(byte[] masterKey, int keyIndex) {
+    void setMasterKey(byte[] masterKey, int keyIndex) {
         setMasterKey(masterKey);
         setMasterKeyIndex(keyIndex);
     }
@@ -80,12 +84,89 @@
         return mMasterKey != null;
     }
 
-    void addToMap(Map<String, Object> parameters) throws LowpanException {
+    public String toSensitiveString() {
+        StringBuffer sb = new StringBuffer();
+
+        sb.append("<LowpanCredential");
+
         if (isMasterKey()) {
-            LowpanProperties.KEY_NETWORK_MASTER_KEY.putInMap(parameters, getMasterKey());
-            LowpanProperties.KEY_NETWORK_MASTER_KEY_INDEX.putInMap(parameters, getMasterKeyIndex());
+            sb.append(" MasterKey:").append(HexDump.toHexString(mMasterKey));
+            if (mMasterKeyIndex != UNSPECIFIED_KEY_INDEX) {
+                sb.append(", Index:").append(mMasterKeyIndex);
+            }
         } else {
-            throw new LowpanException("Unsupported Network Credential");
+            sb.append(" empty");
         }
+
+        sb.append(">");
+
+        return sb.toString();
     }
+
+    @Override
+    public String toString() {
+        StringBuffer sb = new StringBuffer();
+
+        sb.append("<LowpanCredential");
+
+        if (isMasterKey()) {
+            // We don't print out the contents of the key here,
+            // we only do that in toSensitiveString.
+            sb.append(" MasterKey");
+            if (mMasterKeyIndex != UNSPECIFIED_KEY_INDEX) {
+                sb.append(", Index:").append(mMasterKeyIndex);
+            }
+        } else {
+            sb.append(" empty");
+        }
+
+        sb.append(">");
+
+        return sb.toString();
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (!(obj instanceof LowpanCredential)) {
+            return false;
+        }
+        LowpanCredential rhs = (LowpanCredential) obj;
+        return Arrays.equals(mMasterKey, rhs.mMasterKey) && mMasterKeyIndex == rhs.mMasterKeyIndex;
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(Arrays.hashCode(mMasterKey), mMasterKeyIndex);
+    }
+
+    /** Implement the Parcelable interface. */
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    /** Implement the Parcelable interface. */
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeByteArray(mMasterKey);
+        dest.writeInt(mMasterKeyIndex);
+    }
+
+    /** Implement the Parcelable interface. */
+    public static final Creator<LowpanCredential> CREATOR =
+            new Creator<LowpanCredential>() {
+
+                public LowpanCredential createFromParcel(Parcel in) {
+                    LowpanCredential credential = new LowpanCredential();
+
+                    credential.mMasterKey = in.createByteArray();
+                    credential.mMasterKeyIndex = in.readInt();
+
+                    return credential;
+                }
+
+                public LowpanCredential[] newArray(int size) {
+                    return new LowpanCredential[size];
+                }
+            };
 }
diff --git a/lowpan/java/android/net/lowpan/LowpanEnergyScanResult.java b/lowpan/java/android/net/lowpan/LowpanEnergyScanResult.java
index 91ed19c..da87752 100644
--- a/lowpan/java/android/net/lowpan/LowpanEnergyScanResult.java
+++ b/lowpan/java/android/net/lowpan/LowpanEnergyScanResult.java
@@ -28,7 +28,7 @@
     private int mChannel = UNKNOWN;
     private int mMaxRssi = UNKNOWN;
 
-    public LowpanEnergyScanResult() {}
+    LowpanEnergyScanResult() {}
 
     public int getChannel() {
         return mChannel;
@@ -38,11 +38,11 @@
         return mMaxRssi;
     }
 
-    public void setChannel(int x) {
+    void setChannel(int x) {
         mChannel = x;
     }
 
-    public void setMaxRssi(int x) {
+    void setMaxRssi(int x) {
         mMaxRssi = x;
     }
 
diff --git a/lowpan/java/android/net/lowpan/LowpanException.java b/lowpan/java/android/net/lowpan/LowpanException.java
index 5a1f729..5dfce48 100644
--- a/lowpan/java/android/net/lowpan/LowpanException.java
+++ b/lowpan/java/android/net/lowpan/LowpanException.java
@@ -28,245 +28,65 @@
  */
 // @SystemApi
 public class LowpanException extends AndroidException {
-    // Make the eclipse warning about serializable exceptions go away
-    private static final long serialVersionUID = 0x31863cbe562b0e11l; // randomly generated
-
-    public static final int LOWPAN_ERROR = 1;
-    public static final int LOWPAN_CREDENTIAL_NEEDED = 2;
-    public static final int LOWPAN_DEAD = 3;
-    public static final int LOWPAN_DISABLED = 4;
-    public static final int LOWPAN_WRONG_STATE = 5;
-    public static final int LOWPAN_BUSY = 7;
-    public static final int LOWPAN_NCP_PROBLEM = 8;
-    public static final int LOWPAN_ALREADY = 9;
-    public static final int LOWPAN_CANCELED = 10;
-    public static final int LOWPAN_FEATURE_NOT_SUPPORTED = 12;
-    public static final int LOWPAN_PROPERTY_NOT_FOUND = 13;
-    public static final int LOWPAN_JOIN_FAILED_UNKNOWN = 14;
-    public static final int LOWPAN_JOIN_FAILED_AT_SCAN = 15;
-    public static final int LOWPAN_JOIN_FAILED_AT_AUTH = 16;
-    public static final int LOWPAN_FORM_FAILED_AT_SCAN = 17;
-
-    public static LowpanException rethrowAsLowpanException(ServiceSpecificException e)
-            throws LowpanException {
-        int reason;
-        switch (e.errorCode) {
-            case ILowpanInterface.ERROR_INVALID_ARGUMENT:
-            case ILowpanInterface.ERROR_INVALID_TYPE:
-            case ILowpanInterface.ERROR_INVALID_VALUE:
-                throw new IllegalArgumentException(e.getMessage(), e);
-
-            case ILowpanInterface.ERROR_PERMISSION_DENIED:
-                throw new SecurityException(e.getMessage(), e);
-
-            case ILowpanInterface.ERROR_DISABLED:
-                reason = LowpanException.LOWPAN_DISABLED;
-                break;
-
-            case ILowpanInterface.ERROR_WRONG_STATE:
-                reason = LowpanException.LOWPAN_WRONG_STATE;
-                break;
-
-            case ILowpanInterface.ERROR_BUSY:
-                reason = LowpanException.LOWPAN_BUSY;
-                break;
-
-            case ILowpanInterface.ERROR_ALREADY:
-                reason = LowpanException.LOWPAN_ALREADY;
-                break;
-
-            case ILowpanInterface.ERROR_CANCELED:
-                reason = LowpanException.LOWPAN_CANCELED;
-                break;
-
-            case ILowpanInterface.ERROR_CREDENTIAL_NEEDED:
-                reason = LowpanException.LOWPAN_CREDENTIAL_NEEDED;
-                break;
-
-            case ILowpanInterface.ERROR_FEATURE_NOT_SUPPORTED:
-                reason = LowpanException.LOWPAN_FEATURE_NOT_SUPPORTED;
-                break;
-
-            case ILowpanInterface.ERROR_PROPERTY_NOT_FOUND:
-                reason = LowpanException.LOWPAN_PROPERTY_NOT_FOUND;
-                break;
-
-            case ILowpanInterface.ERROR_JOIN_FAILED_UNKNOWN:
-                reason = LowpanException.LOWPAN_JOIN_FAILED_UNKNOWN;
-                break;
-
-            case ILowpanInterface.ERROR_JOIN_FAILED_AT_SCAN:
-                reason = LowpanException.LOWPAN_JOIN_FAILED_AT_SCAN;
-                break;
-
-            case ILowpanInterface.ERROR_JOIN_FAILED_AT_AUTH:
-                reason = LowpanException.LOWPAN_JOIN_FAILED_AT_AUTH;
-                break;
-
-            case ILowpanInterface.ERROR_FORM_FAILED_AT_SCAN:
-                reason = LowpanException.LOWPAN_FORM_FAILED_AT_SCAN;
-                break;
-
-            case ILowpanInterface.ERROR_TIMEOUT:
-            case ILowpanInterface.ERROR_NCP_PROBLEM:
-                reason = LowpanException.LOWPAN_NCP_PROBLEM;
-                break;
-            case ILowpanInterface.ERROR_UNSPECIFIED:
-            default:
-                reason = LOWPAN_ERROR;
-                break;
-        }
-        throw new LowpanException(reason, e.getMessage(), e);
-    }
-
-    private final int mReason;
-
-    public final int getReason() {
-        return mReason;
-    }
-
-    public LowpanException(int problem) {
-        super(getDefaultMessage(problem));
-        mReason = problem;
-    }
+    public LowpanException() {}
 
     public LowpanException(String message) {
-        super(getCombinedMessage(LOWPAN_ERROR, message));
-        mReason = LOWPAN_ERROR;
+        super(message);
     }
 
-    public LowpanException(int problem, String message, Throwable cause) {
-        super(getCombinedMessage(problem, message), cause);
-        mReason = problem;
+    public LowpanException(String message, Throwable cause) {
+        super(message, cause);
     }
 
-    public LowpanException(int problem, Throwable cause) {
-        super(getDefaultMessage(problem), cause);
-        mReason = problem;
+    public LowpanException(Exception cause) {
+        super(cause);
     }
 
-    /** @hide */
-    public static String getDefaultMessage(int problem) {
-        String problemString;
+    /* This method returns LowpanException so that the caller
+     * can add "throw" before the invocation of this method.
+     * This might seem superfluous, but it is actually to
+     * help provide a hint to the java compiler that this
+     * function will not return.
+     */
+    static LowpanException rethrowFromServiceSpecificException(ServiceSpecificException e)
+            throws LowpanException {
+        switch (e.errorCode) {
+            case ILowpanInterface.ERROR_DISABLED:
+                throw new InterfaceDisabledException(e);
 
-        // TODO: Does this need localization?
+            case ILowpanInterface.ERROR_WRONG_STATE:
+                throw new WrongStateException(e);
 
-        switch (problem) {
-            case LOWPAN_DEAD:
-                problemString = "LoWPAN interface is no longer alive";
-                break;
-            case LOWPAN_DISABLED:
-                problemString = "LoWPAN interface is disabled";
-                break;
-            case LOWPAN_WRONG_STATE:
-                problemString = "LoWPAN interface in wrong state to perfom requested action";
-                break;
-            case LOWPAN_BUSY:
-                problemString =
-                        "LoWPAN interface was unable to perform the requestion action because it was busy";
-                break;
-            case LOWPAN_NCP_PROBLEM:
-                problemString =
-                        "The Network Co-Processor associated with this interface has experienced a problem";
-                break;
-            case LOWPAN_ALREADY:
-                problemString = "The LoWPAN interface is already in the given state";
-                break;
-            case LOWPAN_CANCELED:
-                problemString = "This operation was canceled";
-                break;
-            case LOWPAN_CREDENTIAL_NEEDED:
-                problemString = "Additional credentials are required to complete this operation";
-                break;
-            case LOWPAN_FEATURE_NOT_SUPPORTED:
-                problemString =
-                        "A dependent feature required to perform the given action is not currently supported";
-                break;
-            case LOWPAN_PROPERTY_NOT_FOUND:
-                problemString = "The given property was not found";
-                break;
-            case LOWPAN_JOIN_FAILED_UNKNOWN:
-                problemString = "The join operation failed for an unspecified reason";
-                break;
-            case LOWPAN_JOIN_FAILED_AT_SCAN:
-                problemString =
-                        "The join operation failed because it could not communicate with any peers";
-                break;
-            case LOWPAN_JOIN_FAILED_AT_AUTH:
-                problemString =
-                        "The join operation failed because the credentials were not accepted by any peers";
-                break;
-            case LOWPAN_FORM_FAILED_AT_SCAN:
-                problemString = "Network form failed";
-                break;
-            case LOWPAN_ERROR:
+            case ILowpanInterface.ERROR_CANCELED:
+                throw new OperationCanceledException(e);
+
+            case ILowpanInterface.ERROR_JOIN_FAILED_UNKNOWN:
+                throw new JoinFailedException(e);
+
+            case ILowpanInterface.ERROR_JOIN_FAILED_AT_SCAN:
+                throw new JoinFailedAtScanException(e);
+
+            case ILowpanInterface.ERROR_JOIN_FAILED_AT_AUTH:
+                throw new JoinFailedAtAuthException(e);
+
+            case ILowpanInterface.ERROR_FORM_FAILED_AT_SCAN:
+                throw new NetworkAlreadyExistsException(e);
+
+            case ILowpanInterface.ERROR_FEATURE_NOT_SUPPORTED:
+                throw new LowpanException(
+                        e.getMessage() != null ? e.getMessage() : "Feature not supported", e);
+
+            case ILowpanInterface.ERROR_NCP_PROBLEM:
+                throw new LowpanRuntimeException(
+                        e.getMessage() != null ? e.getMessage() : "NCP problem", e);
+
+            case ILowpanInterface.ERROR_INVALID_ARGUMENT:
+                throw new LowpanRuntimeException(
+                        e.getMessage() != null ? e.getMessage() : "Invalid argument", e);
+
+            case ILowpanInterface.ERROR_UNSPECIFIED:
             default:
-                problemString = "The requested LoWPAN operation failed";
-                break;
+                throw new LowpanRuntimeException(e);
         }
-
-        return problemString;
-    }
-
-    private static String getCombinedMessage(int problem, String message) {
-        String problemString = getProblemString(problem);
-        return String.format("%s (%d): %s", problemString, problem, message);
-    }
-
-    private static String getProblemString(int problem) {
-        String problemString;
-
-        switch (problem) {
-            case LOWPAN_ERROR:
-                problemString = "LOWPAN_ERROR";
-                break;
-            case LOWPAN_DEAD:
-                problemString = "LOWPAN_DEAD";
-                break;
-            case LOWPAN_DISABLED:
-                problemString = "LOWPAN_DISABLED";
-                break;
-            case LOWPAN_WRONG_STATE:
-                problemString = "LOWPAN_WRONG_STATE";
-                break;
-            case LOWPAN_BUSY:
-                problemString = "LOWPAN_BUSY";
-                break;
-            case LOWPAN_NCP_PROBLEM:
-                problemString = "LOWPAN_NCP_PROBLEM";
-                break;
-            case LOWPAN_ALREADY:
-                problemString = "LOWPAN_ALREADY";
-                break;
-            case LOWPAN_CANCELED:
-                problemString = "LOWPAN_CANCELED";
-                break;
-            case LOWPAN_CREDENTIAL_NEEDED:
-                problemString = "LOWPAN_CREDENTIAL_NEEDED";
-                break;
-            case LOWPAN_FEATURE_NOT_SUPPORTED:
-                problemString = "LOWPAN_FEATURE_NOT_SUPPORTED";
-                break;
-            case LOWPAN_PROPERTY_NOT_FOUND:
-                problemString = "LOWPAN_PROPERTY_NOT_FOUND";
-                break;
-            case LOWPAN_JOIN_FAILED_UNKNOWN:
-                problemString = "LOWPAN_JOIN_FAILED_UNKNOWN";
-                break;
-            case LOWPAN_JOIN_FAILED_AT_SCAN:
-                problemString = "LOWPAN_JOIN_FAILED_AT_SCAN";
-                break;
-            case LOWPAN_JOIN_FAILED_AT_AUTH:
-                problemString = "LOWPAN_JOIN_FAILED_AT_AUTH";
-                break;
-            case LOWPAN_FORM_FAILED_AT_SCAN:
-                problemString = "LOWPAN_FORM_FAILED_AT_SCAN";
-                break;
-            default:
-                problemString = "LOWPAN_ERROR_CODE_" + problem;
-                break;
-        }
-
-        return problemString;
     }
 }
diff --git a/lowpan/java/android/net/lowpan/LowpanIdentity.aidl b/lowpan/java/android/net/lowpan/LowpanIdentity.aidl
new file mode 100644
index 0000000..fcef98f
--- /dev/null
+++ b/lowpan/java/android/net/lowpan/LowpanIdentity.aidl
@@ -0,0 +1,19 @@
+/**
+ * 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.net.lowpan;
+
+parcelable LowpanIdentity cpp_header "android/net/lowpan/LowpanIdentity.h";
diff --git a/lowpan/java/android/net/lowpan/LowpanIdentity.java b/lowpan/java/android/net/lowpan/LowpanIdentity.java
index 2d36f7f..6cb1f98 100644
--- a/lowpan/java/android/net/lowpan/LowpanIdentity.java
+++ b/lowpan/java/android/net/lowpan/LowpanIdentity.java
@@ -16,8 +16,16 @@
 
 package android.net.lowpan;
 
+import android.annotation.NonNull;
+import android.icu.text.StringPrep;
+import android.icu.text.StringPrepParseException;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.util.Log;
 import com.android.internal.util.HexDump;
-import java.util.Map;
+import java.nio.charset.StandardCharsets;
+import java.util.Arrays;
+import java.util.Objects;
 
 /**
  * Describes an instance of a LoWPAN network.
@@ -25,76 +33,100 @@
  * @hide
  */
 // @SystemApi
-public class LowpanIdentity {
+public class LowpanIdentity implements Parcelable {
+    private static final String TAG = LowpanIdentity.class.getSimpleName();
 
     // Constants
-
-    /** @hide */
-    public static final int TYPE_ZIGBEE = 1;
-
-    /** @hide */
-    public static final int TYPE_ZIGBEE_IP = 2;
-
-    /** @hide */
-    public static final int TYPE_THREAD = 3;
-
-    public static final int UNKNOWN = Integer.MAX_VALUE;
-
+    public static final int UNSPECIFIED_CHANNEL = -1;
+    public static final int UNSPECIFIED_PANID = 0xFFFFFFFF;
     // Builder
 
     /** @hide */
     // @SystemApi
     public static class Builder {
-        private final LowpanIdentity identity = new LowpanIdentity();
+        private static final StringPrep stringPrep =
+                StringPrep.getInstance(StringPrep.RFC3920_RESOURCEPREP);
 
-        public Builder setName(String x) {
-            identity.mName = x;
+        final LowpanIdentity mIdentity = new LowpanIdentity();
+
+        private static String escape(@NonNull byte[] bytes) {
+            StringBuffer sb = new StringBuffer();
+            for (byte b : bytes) {
+                if (b >= 32 && b <= 126) {
+                    sb.append((char) b);
+                } else {
+                    sb.append(String.format("\\0x%02x", b & 0xFF));
+                }
+            }
+            return sb.toString();
+        }
+
+        public Builder setLowpanIdentity(@NonNull LowpanIdentity x) {
+            Objects.requireNonNull(x);
+            setRawName(x.getRawName());
+            setXpanid(x.getXpanid());
+            setPanid(x.getPanid());
+            setChannel(x.getChannel());
+            setType(x.getType());
+            return this;
+        }
+
+        public Builder setName(@NonNull String name) {
+            Objects.requireNonNull(name);
+            try {
+                mIdentity.mName = stringPrep.prepare(name, StringPrep.DEFAULT);
+                mIdentity.mRawName = mIdentity.mName.getBytes(StandardCharsets.UTF_8);
+                mIdentity.mIsNameValid = true;
+            } catch (StringPrepParseException x) {
+                Log.w(TAG, x.toString());
+                setRawName(name.getBytes(StandardCharsets.UTF_8));
+            }
+            return this;
+        }
+
+        public Builder setRawName(@NonNull byte[] name) {
+            Objects.requireNonNull(name);
+            mIdentity.mRawName = name.clone();
+            mIdentity.mName = new String(name, StandardCharsets.UTF_8);
+            try {
+                String nameCheck = stringPrep.prepare(mIdentity.mName, StringPrep.DEFAULT);
+                mIdentity.mIsNameValid =
+                        Arrays.equals(nameCheck.getBytes(StandardCharsets.UTF_8), name);
+            } catch (StringPrepParseException x) {
+                Log.w(TAG, x.toString());
+                mIdentity.mIsNameValid = false;
+            }
+
+            // Non-normal names must be rendered differently to avoid confusion.
+            if (!mIdentity.mIsNameValid) {
+                mIdentity.mName = "«" + escape(name) + "»";
+            }
+
             return this;
         }
 
         public Builder setXpanid(byte x[]) {
-            identity.mXpanid = (x != null ? x.clone() : null);
+            mIdentity.mXpanid = (x != null ? x.clone() : null);
             return this;
         }
 
         public Builder setPanid(int x) {
-            identity.mPanid = x;
+            mIdentity.mPanid = x;
             return this;
         }
 
-        /** @hide */
-        public Builder setType(int x) {
-            identity.mType = x;
+        public Builder setType(@NonNull String x) {
+            mIdentity.mType = x;
             return this;
         }
 
         public Builder setChannel(int x) {
-            identity.mChannel = x;
-            return this;
-        }
-
-        /** @hide */
-        Builder updateFromMap(Map map) {
-            if (map.containsKey(ILowpanInterface.KEY_NETWORK_NAME)) {
-                setName(LowpanProperties.KEY_NETWORK_NAME.getFromMap(map));
-            }
-            if (map.containsKey(ILowpanInterface.KEY_NETWORK_PANID)) {
-                setPanid(LowpanProperties.KEY_NETWORK_PANID.getFromMap(map));
-            }
-            if (map.containsKey(ILowpanInterface.KEY_NETWORK_XPANID)) {
-                setXpanid(LowpanProperties.KEY_NETWORK_XPANID.getFromMap(map));
-            }
-            if (map.containsKey(ILowpanInterface.KEY_CHANNEL)) {
-                setChannel(LowpanProperties.KEY_CHANNEL.getFromMap(map));
-            }
-            if (map.containsKey(ILowpanInterface.KEY_NETWORK_TYPE)) {
-                setType(LowpanProperties.KEY_NETWORK_TYPE.getFromMap(map));
-            }
+            mIdentity.mChannel = x;
             return this;
         }
 
         public LowpanIdentity build() {
-            return identity;
+            return mIdentity;
         }
     }
 
@@ -102,28 +134,37 @@
 
     // Instance Variables
 
-    private String mName = null;
-    private byte[] mXpanid = null;
-    private int mType = UNKNOWN;
-    private int mPanid = UNKNOWN;
-    private int mChannel = UNKNOWN;
+    private String mName = "";
+    private boolean mIsNameValid = true;
+    private byte[] mRawName = new byte[0];
+    private String mType = "";
+    private byte[] mXpanid = new byte[0];
+    private int mPanid = UNSPECIFIED_PANID;
+    private int mChannel = UNSPECIFIED_CHANNEL;
 
-    // Public Getters and Setters
+    // Public Getters
 
     public String getName() {
         return mName;
     }
 
+    public boolean isNameValid() {
+        return mIsNameValid;
+    }
+
+    public byte[] getRawName() {
+        return mRawName.clone();
+    }
+
     public byte[] getXpanid() {
-        return mXpanid != null ? mXpanid.clone() : null;
+        return mXpanid.clone();
     }
 
     public int getPanid() {
         return mPanid;
     }
 
-    /** @hide */
-    public int getType() {
+    public String getType() {
         return mType;
     }
 
@@ -131,43 +172,84 @@
         return mChannel;
     }
 
-    static void addToMap(Map<String, Object> parameters, LowpanIdentity networkInfo) {
-        if (networkInfo.getName() != null) {
-            LowpanProperties.KEY_NETWORK_NAME.putInMap(parameters, networkInfo.getName());
-        }
-        if (networkInfo.getPanid() != LowpanIdentity.UNKNOWN) {
-            LowpanProperties.KEY_NETWORK_PANID.putInMap(parameters, networkInfo.getPanid());
-        }
-        if (networkInfo.getChannel() != LowpanIdentity.UNKNOWN) {
-            LowpanProperties.KEY_CHANNEL.putInMap(parameters, networkInfo.getChannel());
-        }
-        if (networkInfo.getXpanid() != null) {
-            LowpanProperties.KEY_NETWORK_XPANID.putInMap(parameters, networkInfo.getXpanid());
-        }
-    }
-
-    void addToMap(Map<String, Object> parameters) {
-        addToMap(parameters, this);
-    }
-
     @Override
     public String toString() {
         StringBuffer sb = new StringBuffer();
 
-        sb.append("Name: ").append(mName == null ? "<none>" : mName);
+        sb.append("Name:").append(getName());
 
-        if (mXpanid != null) {
-            sb.append(", XPANID: ").append(HexDump.toHexString(mXpanid));
+        if (mType.length() > 0) {
+            sb.append(", Type:").append(mType);
         }
 
-        if (mPanid != UNKNOWN) {
-            sb.append(", PANID: ").append(String.format("0x%04X", mPanid));
+        if (mXpanid.length > 0) {
+            sb.append(", XPANID:").append(HexDump.toHexString(mXpanid));
         }
 
-        if (mChannel != UNKNOWN) {
-            sb.append(", Channel: ").append(mChannel);
+        if (mPanid != UNSPECIFIED_PANID) {
+            sb.append(", PANID:").append(String.format("0x%04X", mPanid));
+        }
+
+        if (mChannel != UNSPECIFIED_CHANNEL) {
+            sb.append(", Channel:").append(mChannel);
         }
 
         return sb.toString();
     }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (!(obj instanceof LowpanIdentity)) {
+            return false;
+        }
+        LowpanIdentity rhs = (LowpanIdentity) obj;
+        return Arrays.equals(mRawName, rhs.mRawName)
+                && Arrays.equals(mXpanid, rhs.mXpanid)
+                && mType.equals(rhs.mType)
+                && mPanid == rhs.mPanid
+                && mChannel == rhs.mChannel;
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(
+                Arrays.hashCode(mRawName), mType, Arrays.hashCode(mXpanid), mPanid, mChannel);
+    }
+
+    /** Implement the Parcelable interface. */
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    /** Implement the Parcelable interface. */
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeByteArray(mRawName);
+        dest.writeString(mType);
+        dest.writeByteArray(mXpanid);
+        dest.writeInt(mPanid);
+        dest.writeInt(mChannel);
+    }
+
+    /** Implement the Parcelable interface. */
+    public static final Creator<LowpanIdentity> CREATOR =
+            new Creator<LowpanIdentity>() {
+
+                public LowpanIdentity createFromParcel(Parcel in) {
+                    Builder builder = new Builder();
+
+                    builder.setRawName(in.createByteArray());
+                    builder.setType(in.readString());
+                    builder.setXpanid(in.createByteArray());
+                    builder.setPanid(in.readInt());
+                    builder.setChannel(in.readInt());
+
+                    return builder.build();
+                }
+
+                public LowpanIdentity[] newArray(int size) {
+                    return new LowpanIdentity[size];
+                }
+            };
 }
diff --git a/lowpan/java/android/net/lowpan/LowpanInterface.java b/lowpan/java/android/net/lowpan/LowpanInterface.java
index 55bf399..57e9135 100644
--- a/lowpan/java/android/net/lowpan/LowpanInterface.java
+++ b/lowpan/java/android/net/lowpan/LowpanInterface.java
@@ -28,8 +28,6 @@
 import android.os.ServiceSpecificException;
 import android.util.Log;
 import java.util.HashMap;
-import java.util.Map;
-import java.util.Set;
 
 /**
  * Class for managing a specific Low-power Wireless Personal Area Network (LoWPAN) interface.
@@ -41,13 +39,13 @@
     private static final String TAG = LowpanInterface.class.getSimpleName();
 
     /** Detached role. The interface is not currently attached to a network. */
-    public static final String ROLE_DETACHED = "detached";
+    public static final String ROLE_DETACHED = ILowpanInterface.ROLE_DETACHED;
 
     /** End-device role. End devices do not route traffic for other nodes. */
-    public static final String ROLE_END_DEVICE = "end-device";
+    public static final String ROLE_END_DEVICE = ILowpanInterface.ROLE_END_DEVICE;
 
     /** Router role. Routers help route traffic around the mesh network. */
-    public static final String ROLE_ROUTER = "router";
+    public static final String ROLE_ROUTER = ILowpanInterface.ROLE_ROUTER;
 
     /**
      * Sleepy End-Device role.
@@ -57,7 +55,7 @@
      * extraordinarilly low power consumption, but packet latency can be on the order of dozens of
      * seconds(depending on how the node is configured).
      */
-    public static final String ROLE_SLEEPY_END_DEVICE = "sleepy-end-device";
+    public static final String ROLE_SLEEPY_END_DEVICE = ILowpanInterface.ROLE_SLEEPY_END_DEVICE;
 
     /**
      * Sleepy-router role.
@@ -65,13 +63,13 @@
      * <p>Routers with this role are nominally asleep, waking up periodically to check in with other
      * routers and their children.
      */
-    public static final String ROLE_SLEEPY_ROUTER = "sleepy-router";
+    public static final String ROLE_SLEEPY_ROUTER = ILowpanInterface.ROLE_SLEEPY_ROUTER;
 
     /** TODO: doc */
-    public static final String ROLE_LEADER = "leader";
+    public static final String ROLE_LEADER = ILowpanInterface.ROLE_LEADER;
 
     /** TODO: doc */
-    public static final String ROLE_COORDINATOR = "coordinator";
+    public static final String ROLE_COORDINATOR = ILowpanInterface.ROLE_COORDINATOR;
 
     /**
      * Offline state.
@@ -86,7 +84,7 @@
      * @see #getState()
      * @see #STATE_FAULT
      */
-    public static final String STATE_OFFLINE = "offline";
+    public static final String STATE_OFFLINE = ILowpanInterface.STATE_OFFLINE;
 
     /**
      * Commissioning state.
@@ -98,7 +96,7 @@
      * @see #getState()
      * @hide
      */
-    public static final String STATE_COMMISSIONING = "commissioning";
+    public static final String STATE_COMMISSIONING = ILowpanInterface.STATE_COMMISSIONING;
 
     /**
      * Attaching state.
@@ -116,7 +114,7 @@
      * @see #STATE_ATTACHED
      * @see #getState()
      */
-    public static final String STATE_ATTACHING = "attaching";
+    public static final String STATE_ATTACHING = ILowpanInterface.STATE_ATTACHING;
 
     /**
      * Attached state.
@@ -127,7 +125,7 @@
      * @see #STATE_ATTACHING
      * @see #getState()
      */
-    public static final String STATE_ATTACHED = "attached";
+    public static final String STATE_ATTACHED = ILowpanInterface.STATE_ATTACHED;
 
     /**
      * Fault state.
@@ -141,7 +139,7 @@
      * @see #getState
      * @see #STATE_OFFLINE
      */
-    public static final String STATE_FAULT = "fault";
+    public static final String STATE_FAULT = ILowpanInterface.STATE_FAULT;
 
     /**
      * Network type for Thread 1.x networks.
@@ -150,23 +148,9 @@
      * @see #getLowpanIdentity
      * @hide
      */
-    public static final String NETWORK_TYPE_THREAD = "org.threadgroup.thread.v1";
+    public static final String NETWORK_TYPE_THREAD_V1 = ILowpanInterface.NETWORK_TYPE_THREAD_V1;
 
-    /**
-     * Network type for ZigBeeIP 1.x networks.
-     *
-     * @see android.net.lowpan.LowpanIdentity#getType
-     * @see #getLowpanIdentity
-     * @hide
-     */
-    public static final String NETWORK_TYPE_ZIGBEE_IP = "org.zigbee.zigbeeip.v1";
-
-    private static final String NETWORK_PROPERTY_KEYS[] = {
-        LowpanProperties.KEY_NETWORK_NAME.getName(),
-        LowpanProperties.KEY_NETWORK_PANID.getName(),
-        LowpanProperties.KEY_NETWORK_XPANID.getName(),
-        LowpanProperties.KEY_CHANNEL.getName()
-    };
+    public static final String EMPTY_PARTITION_ID = "";
 
     /**
      * Callback base class for LowpanInterface
@@ -187,8 +171,13 @@
 
         public void onLowpanIdentityChanged(@NonNull LowpanIdentity value) {}
 
-        /** @hide */
-        public void onPropertiesChanged(@NonNull Map properties) {}
+        public void onLinkNetworkAdded(IpPrefix prefix) {}
+
+        public void onLinkNetworkRemoved(IpPrefix prefix) {}
+
+        public void onLinkAddressAdded(LinkAddress address) {}
+
+        public void onLinkAddressRemoved(LinkAddress address) {}
     }
 
     private final ILowpanInterface mBinder;
@@ -222,74 +211,6 @@
         return mBinder;
     }
 
-    // Private Property Helpers
-
-    void setProperties(Map properties) throws LowpanException {
-        try {
-            mBinder.setProperties(properties);
-
-        } catch (RemoteException x) {
-            throw x.rethrowAsRuntimeException();
-
-        } catch (ServiceSpecificException x) {
-            throw LowpanException.rethrowAsLowpanException(x);
-        }
-    }
-
-    @NonNull
-    Map<String, Object> getProperties(String keys[]) throws LowpanException {
-        try {
-            return mBinder.getProperties(keys);
-
-        } catch (RemoteException x) {
-            throw x.rethrowAsRuntimeException();
-
-        } catch (ServiceSpecificException x) {
-            throw LowpanException.rethrowAsLowpanException(x);
-        }
-    }
-
-    /** @hide */
-    public <T> void setProperty(LowpanProperty<T> key, T value) throws LowpanException {
-        HashMap<String, T> prop = new HashMap<>();
-        prop.put(key.getName(), value);
-        setProperties(prop);
-    }
-
-    /** @hide */
-    @Nullable
-    public <T> T getProperty(LowpanProperty<T> key) throws LowpanException {
-        Map<String, Object> map = getProperties(new String[] {key.getName()});
-        if (map != null && !map.isEmpty()) {
-            // We know there is only one value.
-            return (T) map.values().iterator().next();
-        }
-        return null;
-    }
-
-    @Nullable
-    <T> String getPropertyAsString(LowpanProperty<T> key) throws LowpanException {
-        try {
-            return mBinder.getPropertyAsString(key.getName());
-
-        } catch (RemoteException x) {
-            throw x.rethrowAsRuntimeException();
-
-        } catch (ServiceSpecificException x) {
-            throw LowpanException.rethrowAsLowpanException(x);
-        }
-    }
-
-    int getPropertyAsInt(LowpanProperty<Integer> key) throws LowpanException {
-        Integer value = getProperty(key);
-        return (value != null) ? value : 0;
-    }
-
-    boolean getPropertyAsBoolean(LowpanProperty<Boolean> key) throws LowpanException {
-        Boolean value = getProperty(key);
-        return (value != null) ? value : false;
-    }
-
     // Public Actions
 
     /**
@@ -306,15 +227,13 @@
      */
     public void form(@NonNull LowpanProvision provision) throws LowpanException {
         try {
-            Map<String, Object> parameters = new HashMap();
-            provision.addToMap(parameters);
-            mBinder.form(parameters);
+            mBinder.form(provision);
 
         } catch (RemoteException x) {
             throw x.rethrowAsRuntimeException();
 
         } catch (ServiceSpecificException x) {
-            throw LowpanException.rethrowAsLowpanException(x);
+            throw LowpanException.rethrowFromServiceSpecificException(x);
         }
     }
 
@@ -328,15 +247,13 @@
      */
     public void join(@NonNull LowpanProvision provision) throws LowpanException {
         try {
-            Map<String, Object> parameters = new HashMap();
-            provision.addToMap(parameters);
-            mBinder.join(parameters);
+            mBinder.join(provision);
 
         } catch (RemoteException x) {
             throw x.rethrowAsRuntimeException();
 
         } catch (ServiceSpecificException x) {
-            throw LowpanException.rethrowAsLowpanException(x);
+            throw LowpanException.rethrowFromServiceSpecificException(x);
         }
     }
 
@@ -348,13 +265,14 @@
      * <p>This method will block execution until the operation has completed.
      */
     public void attach(@NonNull LowpanProvision provision) throws LowpanException {
-        if (ROLE_DETACHED.equals(getRole())) {
-            Map<String, Object> parameters = new HashMap();
-            provision.addToMap(parameters);
-            setProperties(parameters);
-            setUp(true);
-        } else {
-            throw new LowpanException(LowpanException.LOWPAN_ALREADY);
+        try {
+            mBinder.attach(provision);
+
+        } catch (RemoteException x) {
+            throw x.rethrowAsRuntimeException();
+
+        } catch (ServiceSpecificException x) {
+            throw LowpanException.rethrowFromServiceSpecificException(x);
         }
     }
 
@@ -372,7 +290,7 @@
             throw x.rethrowAsRuntimeException();
 
         } catch (ServiceSpecificException x) {
-            throw LowpanException.rethrowAsLowpanException(x);
+            throw LowpanException.rethrowFromServiceSpecificException(x);
         }
     }
 
@@ -382,9 +300,17 @@
      */
     public @NonNull LowpanCommissioningSession startCommissioningSession(
             @NonNull LowpanBeaconInfo beaconInfo) throws LowpanException {
+        try {
+            mBinder.startCommissioningSession(beaconInfo);
 
-        /* TODO: Implement startCommissioningSession */
-        throw new LowpanException(LowpanException.LOWPAN_FEATURE_NOT_SUPPORTED);
+            return new LowpanCommissioningSession(mBinder, beaconInfo, mLooper);
+
+        } catch (RemoteException x) {
+            throw x.rethrowAsRuntimeException();
+
+        } catch (ServiceSpecificException x) {
+            throw LowpanException.rethrowFromServiceSpecificException(x);
+        }
     }
 
     /**
@@ -403,7 +329,7 @@
             throw x.rethrowAsRuntimeException();
 
         } catch (ServiceSpecificException x) {
-            throw LowpanException.rethrowAsLowpanException(x);
+            throw LowpanException.rethrowFromServiceSpecificException(x);
         }
     }
 
@@ -415,6 +341,9 @@
         try {
             return mBinder.getName();
 
+        } catch (DeadObjectException x) {
+            return "";
+
         } catch (RemoteException x) {
             throw x.rethrowAsRuntimeException();
         }
@@ -428,9 +357,13 @@
      */
     public boolean isEnabled() {
         try {
-            return getPropertyAsBoolean(LowpanProperties.KEY_INTERFACE_ENABLED);
-        } catch (LowpanException x) {
+            return mBinder.isEnabled();
+
+        } catch (DeadObjectException x) {
             return false;
+
+        } catch (RemoteException x) {
+            throw x.rethrowAsRuntimeException();
         }
     }
 
@@ -444,7 +377,15 @@
      * @hide
      */
     public void setEnabled(boolean enabled) throws LowpanException {
-        setProperty(LowpanProperties.KEY_INTERFACE_ENABLED, enabled);
+        try {
+            mBinder.setEnabled(enabled);
+
+        } catch (RemoteException x) {
+            throw x.rethrowAsRuntimeException();
+
+        } catch (ServiceSpecificException x) {
+            throw LowpanException.rethrowFromServiceSpecificException(x);
+        }
     }
 
     /**
@@ -454,22 +395,14 @@
      */
     public boolean isUp() {
         try {
-            return getPropertyAsBoolean(LowpanProperties.KEY_INTERFACE_UP);
-        } catch (LowpanException x) {
-            return false;
-        }
-    }
+            return mBinder.isUp();
 
-    /**
-     * Bring up or shut down the network interface.
-     *
-     * <p>This method brings up or shuts down the network interface, attaching or (gracefully)
-     * detaching from the currently configured LoWPAN network as appropriate.
-     *
-     * @hide
-     */
-    public void setUp(boolean interfaceUp) throws LowpanException {
-        setProperty(LowpanProperties.KEY_INTERFACE_UP, interfaceUp);
+        } catch (DeadObjectException x) {
+            return false;
+
+        } catch (RemoteException x) {
+            throw x.rethrowAsRuntimeException();
+        }
     }
 
     /**
@@ -480,9 +413,13 @@
      */
     public boolean isConnected() {
         try {
-            return getPropertyAsBoolean(LowpanProperties.KEY_INTERFACE_CONNECTED);
-        } catch (LowpanException x) {
+            return mBinder.isConnected();
+
+        } catch (DeadObjectException x) {
             return false;
+
+        } catch (RemoteException x) {
+            throw x.rethrowAsRuntimeException();
         }
     }
 
@@ -492,9 +429,13 @@
      */
     public boolean isCommissioned() {
         try {
-            return getPropertyAsBoolean(LowpanProperties.KEY_INTERFACE_COMMISSIONED);
-        } catch (LowpanException x) {
+            return mBinder.isCommissioned();
+
+        } catch (DeadObjectException x) {
             return false;
+
+        } catch (RemoteException x) {
+            throw x.rethrowAsRuntimeException();
         }
     }
 
@@ -514,90 +455,89 @@
      */
     public String getState() {
         try {
-            return getProperty(LowpanProperties.KEY_INTERFACE_STATE);
-        } catch (LowpanException x) {
-            Log.e(TAG, x.toString());
+            return mBinder.getState();
+
+        } catch (DeadObjectException x) {
             return STATE_FAULT;
+
+        } catch (RemoteException x) {
+            throw x.rethrowAsRuntimeException();
+        }
+    }
+
+    /** Get network partition/fragment identifier. */
+    public String getPartitionId() {
+        try {
+            return mBinder.getPartitionId();
+
+        } catch (DeadObjectException x) {
+            return EMPTY_PARTITION_ID;
+
+        } catch (RemoteException x) {
+            throw x.rethrowAsRuntimeException();
         }
     }
 
     /** TODO: doc */
     public LowpanIdentity getLowpanIdentity() {
-        LowpanIdentity.Builder builder = new LowpanIdentity.Builder();
         try {
-            builder.updateFromMap(getProperties(NETWORK_PROPERTY_KEYS));
-        } catch (LowpanException x) {
-            // We ignore all LoWPAN-specitic exceptions here.
+            return mBinder.getLowpanIdentity();
+
+        } catch (DeadObjectException x) {
+            return new LowpanIdentity();
+
+        } catch (RemoteException x) {
+            throw x.rethrowAsRuntimeException();
         }
-
-        return builder.build();
-    }
-
-    /**
-     * TODO: doc
-     *
-     * @hide
-     */
-    public void setLowpanIdentity(LowpanIdentity network) throws LowpanException {
-        Map<String, Object> map = new HashMap();
-        LowpanIdentity.addToMap(map, network);
-        setProperties(map);
     }
 
     /** TODO: doc */
     @NonNull
     public String getRole() {
-        String role = null;
-
         try {
-            role = getProperty(LowpanProperties.KEY_NETWORK_ROLE);
-        } catch (LowpanException x) {
-            // We ignore all LoWPAN-specitic exceptions here.
-            Log.e(TAG, x.toString());
-        }
+            return mBinder.getRole();
 
-        if (role == null) {
-            role = ROLE_DETACHED;
-        }
+        } catch (DeadObjectException x) {
+            return ROLE_DETACHED;
 
-        return role;
+        } catch (RemoteException x) {
+            throw x.rethrowAsRuntimeException();
+        }
     }
 
     /** TODO: doc */
     @Nullable
     public LowpanCredential getLowpanCredential() {
-        LowpanCredential credential = null;
-
         try {
-            Integer keyIndex = getProperty(LowpanProperties.KEY_NETWORK_MASTER_KEY_INDEX);
+            return mBinder.getLowpanCredential();
 
-            if (keyIndex == null) {
-                credential =
-                        LowpanCredential.createMasterKey(
-                                getProperty(LowpanProperties.KEY_NETWORK_MASTER_KEY));
-            } else {
-                credential =
-                        LowpanCredential.createMasterKey(
-                                getProperty(LowpanProperties.KEY_NETWORK_MASTER_KEY),
-                                keyIndex.intValue());
-            }
-        } catch (LowpanException x) {
-            // We ignore all LoWPAN-specitic exceptions here.
-            Log.e(TAG, x.toString());
+        } catch (RemoteException x) {
+            throw x.rethrowAsRuntimeException();
         }
-
-        return credential;
     }
 
-    /**
-     * TODO: doc
-     *
-     * @hide
-     */
-    public void setLowpanCredential(LowpanCredential networkCredential) throws LowpanException {
-        Map<String, Object> map = new HashMap();
-        networkCredential.addToMap(map);
-        setProperties(map);
+    public @NonNull String[] getSupportedNetworkTypes() throws LowpanException {
+        try {
+            return mBinder.getSupportedNetworkTypes();
+
+        } catch (RemoteException x) {
+            throw x.rethrowAsRuntimeException();
+
+        } catch (ServiceSpecificException x) {
+            throw LowpanException.rethrowFromServiceSpecificException(x);
+        }
+    }
+
+    public @NonNull LowpanChannelInfo[] getSupportedChannels() throws LowpanException {
+        try {
+            return mBinder.getSupportedChannels();
+
+        } catch (RemoteException x) {
+            throw x.rethrowAsRuntimeException();
+
+        } catch (ServiceSpecificException x) {
+            throw LowpanException.rethrowFromServiceSpecificException(x);
+        }
     }
 
     // Listener Support
@@ -627,57 +567,80 @@
                         }
                     }
 
-                    @Override public void onPropertiesChanged(Map properties) {
-                        Runnable runnable =
-                                () -> {
-                                    for (String key : (Set<String>) properties.keySet()) {
-                                        Object value = properties.get(key);
-                                        switch (key) {
-                                            case ILowpanInterface.KEY_INTERFACE_ENABLED:
-                                                cb.onEnabledChanged(
-                                                        ((Boolean) value).booleanValue());
-                                                break;
-                                            case ILowpanInterface.KEY_INTERFACE_UP:
-                                                cb.onUpChanged(((Boolean) value).booleanValue());
-                                                break;
-                                            case ILowpanInterface.KEY_INTERFACE_CONNECTED:
-                                                cb.onConnectedChanged(
-                                                        ((Boolean) value).booleanValue());
-                                                break;
-                                            case ILowpanInterface.KEY_INTERFACE_STATE:
-                                                cb.onStateChanged((String) value);
-                                                break;
-                                            case ILowpanInterface.KEY_NETWORK_NAME:
-                                            case ILowpanInterface.KEY_NETWORK_PANID:
-                                            case ILowpanInterface.KEY_NETWORK_XPANID:
-                                            case ILowpanInterface.KEY_CHANNEL:
-                                                cb.onLowpanIdentityChanged(getLowpanIdentity());
-                                                break;
-                                            case ILowpanInterface.KEY_NETWORK_ROLE:
-                                                cb.onRoleChanged(value.toString());
-                                                break;
-                                        }
-                                    }
-                                    cb.onPropertiesChanged(properties);
-                                };
-
-                        mHandler.post(runnable);
+                    @Override
+                    public void onEnabledChanged(boolean value) {
+                        mHandler.post(() -> cb.onEnabledChanged(value));
                     }
 
-                    @Override public void onLinkNetworkAdded(IpPrefix prefix) {
-                        // Support for this event isn't yet implemented.
+                    @Override
+                    public void onConnectedChanged(boolean value) {
+                        mHandler.post(() -> cb.onConnectedChanged(value));
                     }
 
-                    @Override public void onLinkNetworkRemoved(IpPrefix prefix) {
-                        // Support for this event isn't yet implemented.
+                    @Override
+                    public void onUpChanged(boolean value) {
+                        mHandler.post(() -> cb.onUpChanged(value));
                     }
 
-                    @Override public void onLinkAddressAdded(String address) {
-                        // Support for this event isn't yet implemented.
+                    @Override
+                    public void onRoleChanged(String value) {
+                        mHandler.post(() -> cb.onRoleChanged(value));
                     }
 
-                    @Override public void onLinkAddressRemoved(String address) {
-                        // Support for this event isn't yet implemented.
+                    @Override
+                    public void onStateChanged(String value) {
+                        mHandler.post(() -> cb.onStateChanged(value));
+                    }
+
+                    @Override
+                    public void onLowpanIdentityChanged(LowpanIdentity value) {
+                        mHandler.post(() -> cb.onLowpanIdentityChanged(value));
+                    }
+
+                    @Override
+                    public void onLinkNetworkAdded(IpPrefix value) {
+                        mHandler.post(() -> cb.onLinkNetworkAdded(value));
+                    }
+
+                    @Override
+                    public void onLinkNetworkRemoved(IpPrefix value) {
+                        mHandler.post(() -> cb.onLinkNetworkRemoved(value));
+                    }
+
+                    @Override
+                    public void onLinkAddressAdded(String value) {
+                        LinkAddress la;
+                        try {
+                            la = new LinkAddress(value);
+                        } catch (IllegalArgumentException x) {
+                            Log.e(
+                                    TAG,
+                                    "onLinkAddressAdded: Bad LinkAddress \"" + value + "\", " + x);
+                            return;
+                        }
+                        mHandler.post(() -> cb.onLinkAddressAdded(la));
+                    }
+
+                    @Override
+                    public void onLinkAddressRemoved(String value) {
+                        LinkAddress la;
+                        try {
+                            la = new LinkAddress(value);
+                        } catch (IllegalArgumentException x) {
+                            Log.e(
+                                    TAG,
+                                    "onLinkAddressRemoved: Bad LinkAddress \""
+                                            + value
+                                            + "\", "
+                                            + x);
+                            return;
+                        }
+                        mHandler.post(() -> cb.onLinkAddressRemoved(la));
+                    }
+
+                    @Override
+                    public void onReceiveFromCommissioner(byte[] packet) {
+                        // This is only used by the LowpanCommissioningSession.
                     }
                 };
         try {
@@ -752,9 +715,9 @@
      *
      * @hide
      */
-    public LinkAddress[] copyLinkAddresses() throws LowpanException {
+    public LinkAddress[] getLinkAddresses() throws LowpanException {
         try {
-            String[] linkAddressStrings = mBinder.copyLinkAddresses();
+            String[] linkAddressStrings = mBinder.getLinkAddresses();
             LinkAddress[] ret = new LinkAddress[linkAddressStrings.length];
             int i = 0;
             for (String str : linkAddressStrings) {
@@ -766,7 +729,7 @@
             throw x.rethrowAsRuntimeException();
 
         } catch (ServiceSpecificException x) {
-            throw LowpanException.rethrowAsLowpanException(x);
+            throw LowpanException.rethrowFromServiceSpecificException(x);
         }
     }
 
@@ -775,15 +738,15 @@
      *
      * @hide
      */
-    public IpPrefix[] copyLinkNetworks() throws LowpanException {
+    public IpPrefix[] getLinkNetworks() throws LowpanException {
         try {
-            return mBinder.copyLinkNetworks();
+            return mBinder.getLinkNetworks();
 
         } catch (RemoteException x) {
             throw x.rethrowAsRuntimeException();
 
         } catch (ServiceSpecificException x) {
-            throw LowpanException.rethrowAsLowpanException(x);
+            throw LowpanException.rethrowFromServiceSpecificException(x);
         }
     }
 
@@ -800,7 +763,7 @@
             throw x.rethrowAsRuntimeException();
 
         } catch (ServiceSpecificException x) {
-            throw LowpanException.rethrowAsLowpanException(x);
+            throw LowpanException.rethrowFromServiceSpecificException(x);
         }
     }
 
@@ -837,7 +800,7 @@
             throw x.rethrowAsRuntimeException();
 
         } catch (ServiceSpecificException x) {
-            throw LowpanException.rethrowAsLowpanException(x);
+            throw LowpanException.rethrowFromServiceSpecificException(x);
         }
     }
 
diff --git a/lowpan/java/android/net/lowpan/LowpanProperties.java b/lowpan/java/android/net/lowpan/LowpanProperties.java
index f835260..cc45ff85 100644
--- a/lowpan/java/android/net/lowpan/LowpanProperties.java
+++ b/lowpan/java/android/net/lowpan/LowpanProperties.java
@@ -16,74 +16,14 @@
 
 package android.net.lowpan;
 
-import android.net.IpPrefix;
-import android.net.LinkAddress;
-
 /** {@hide} */
 public final class LowpanProperties {
 
-    public static final LowpanProperty<Boolean> KEY_INTERFACE_ENABLED =
-            new LowpanStandardProperty(
-                    "android.net.lowpan.property.INTERFACE_ENABLED", Boolean.class);
-    public static final LowpanProperty<Boolean> KEY_INTERFACE_COMMISSIONED =
-            new LowpanStandardProperty(
-                    "android.net.lowpan.property.INTERFACE_COMMISSIONED", Boolean.class);
-    public static final LowpanProperty<Boolean> KEY_INTERFACE_CONNECTED =
-            new LowpanStandardProperty(
-                    "android.net.lowpan.property.INTERFACE_CONNECTED", Boolean.class);
-    public static final LowpanProperty<Boolean> KEY_INTERFACE_UP =
-            new LowpanStandardProperty("android.net.lowpan.property.INTERFACE_UP", Boolean.class);
-    public static final LowpanProperty<String> KEY_INTERFACE_STATE =
-            new LowpanStandardProperty("android.net.lowpan.property.INTERFACE_STATE", String.class);
-
-    public static final LowpanProperty<String> KEY_NETWORK_NAME =
-            new LowpanStandardProperty("android.net.lowpan.property.NETWORK_NAME", Boolean.class);
-    public static final LowpanProperty<Integer> KEY_NETWORK_PANID =
-            new LowpanStandardProperty("android.net.lowpan.property.NETWORK_PANID", Integer.class);
-    public static final LowpanProperty<byte[]> KEY_NETWORK_XPANID =
-            new LowpanStandardProperty("android.net.lowpan.property.NETWORK_XPANID", byte[].class);
-    public static final LowpanProperty<byte[]> KEY_NETWORK_MASTER_KEY =
-            new LowpanStandardProperty(
-                    "android.net.lowpan.property.NETWORK_MASTER_KEY", byte[].class);
-    public static final LowpanProperty<Integer> KEY_NETWORK_MASTER_KEY_INDEX =
-            new LowpanStandardProperty(
-                    "android.net.lowpan.property.NETWORK_MASTER_KEY_INDEX", Integer.class);
-    public static final LowpanProperty<Integer> KEY_NETWORK_TYPE =
-            new LowpanStandardProperty("android.net.lowpan.property.NETWORK_TYPE", Integer.class);
-    public static final LowpanProperty<String> KEY_NETWORK_ROLE =
-            new LowpanStandardProperty("android.net.lowpan.property.NETWORK_ROLE", String.class);
-
-    public static final LowpanProperty<Integer> KEY_CHANNEL =
-            new LowpanStandardProperty("android.net.lowpan.property.CHANNEL", Integer.class);
     public static final LowpanProperty<int[]> KEY_CHANNEL_MASK =
             new LowpanStandardProperty("android.net.lowpan.property.CHANNEL_MASK", int[].class);
+
     public static final LowpanProperty<Integer> KEY_MAX_TX_POWER =
             new LowpanStandardProperty("android.net.lowpan.property.MAX_TX_POWER", Integer.class);
-    public static final LowpanProperty<Integer> KEY_RSSI =
-            new LowpanStandardProperty("android.net.lowpan.property.RSSI", Integer.class);
-
-    public static final LowpanProperty<Integer> KEY_LQI =
-            new LowpanStandardProperty("android.net.lowpan.property.LQI", Integer.class);
-    public static final LowpanProperty<byte[]> KEY_BEACON_ADDRESS =
-            new LowpanStandardProperty("android.net.lowpan.property.BEACON_ADDRESS", byte[].class);
-    public static final LowpanProperty<Boolean> KEY_BEACON_CAN_ASSIST =
-            new LowpanStandardProperty(
-                    "android.net.lowpan.property.BEACON_CAN_ASSIST", Boolean.class);
-
-    public static final LowpanProperty<String> KEY_DRIVER_VERSION =
-            new LowpanStandardProperty("android.net.lowpan.property.DRIVER_VERSION", String.class);
-
-    public static final LowpanProperty<String> KEY_NCP_VERSION =
-            new LowpanStandardProperty("android.net.lowpan.property.NCP_VERSION", String.class);
-
-    /** @hide */
-    public static final LowpanProperty<byte[]> KEY_EXTENDED_ADDRESS =
-            new LowpanStandardProperty(
-                    "android.net.lowpan.property.EXTENDED_ADDRESS", byte[].class);
-
-    /** @hide */
-    public static final LowpanProperty<byte[]> KEY_MAC_ADDRESS =
-            new LowpanStandardProperty("android.net.lowpan.property.MAC_ADDRESS", byte[].class);
 
     /** @hide */
     private LowpanProperties() {}
diff --git a/lowpan/java/android/net/lowpan/LowpanProvision.aidl b/lowpan/java/android/net/lowpan/LowpanProvision.aidl
new file mode 100644
index 0000000..100e9dc
--- /dev/null
+++ b/lowpan/java/android/net/lowpan/LowpanProvision.aidl
@@ -0,0 +1,19 @@
+/**
+ * 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.net.lowpan;
+
+parcelable LowpanProvision cpp_header "android/net/lowpan/LowpanProvision.h";
diff --git a/lowpan/java/android/net/lowpan/LowpanProvision.java b/lowpan/java/android/net/lowpan/LowpanProvision.java
index 7028807..f126003 100644
--- a/lowpan/java/android/net/lowpan/LowpanProvision.java
+++ b/lowpan/java/android/net/lowpan/LowpanProvision.java
@@ -18,7 +18,9 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
-import java.util.Map;
+import android.os.Parcel;
+import android.os.Parcelable;
+import java.util.Objects;
 
 /**
  * Describes the information needed to describe a network
@@ -26,7 +28,7 @@
  * @hide
  */
 // @SystemApi
-public class LowpanProvision {
+public class LowpanProvision implements Parcelable {
 
     // Builder
 
@@ -69,20 +71,6 @@
         return mCredential;
     }
 
-    // LoWPAN-Internal Methods
-
-    static void addToMap(Map<String, Object> parameters, LowpanProvision provision)
-            throws LowpanException {
-        provision.mIdentity.addToMap(parameters);
-        if (provision.mCredential != null) {
-            provision.mCredential.addToMap(parameters);
-        }
-    }
-
-    void addToMap(Map<String, Object> parameters) throws LowpanException {
-        addToMap(parameters, this);
-    }
-
     @Override
     public String toString() {
         StringBuffer sb = new StringBuffer();
@@ -90,11 +78,72 @@
         sb.append("LowpanProvision { identity => ").append(mIdentity.toString());
 
         if (mCredential != null) {
-            sb.append(", credential: ").append(mCredential.toString());
+            sb.append(", credential => ").append(mCredential.toString());
         }
 
         sb.append("}");
 
         return sb.toString();
     }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(mIdentity, mCredential);
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (!(obj instanceof LowpanProvision)) {
+            return false;
+        }
+        LowpanProvision rhs = (LowpanProvision) obj;
+
+        if (!mIdentity.equals(rhs.mIdentity)) {
+            return false;
+        }
+
+        if (!Objects.equals(mCredential, rhs.mCredential)) {
+            return false;
+        }
+
+        return true;
+    }
+
+    /** Implement the Parcelable interface. */
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    /** Implement the Parcelable interface. */
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        mIdentity.writeToParcel(dest, flags);
+        if (mCredential == null) {
+            dest.writeBoolean(false);
+        } else {
+            dest.writeBoolean(true);
+            mCredential.writeToParcel(dest, flags);
+        }
+    }
+
+    /** Implement the Parcelable interface. */
+    public static final Creator<LowpanProvision> CREATOR =
+            new Creator<LowpanProvision>() {
+                public LowpanProvision createFromParcel(Parcel in) {
+                    Builder builder = new Builder();
+
+                    builder.setLowpanIdentity(LowpanIdentity.CREATOR.createFromParcel(in));
+
+                    if (in.readBoolean()) {
+                        builder.setLowpanCredential(LowpanCredential.CREATOR.createFromParcel(in));
+                    }
+
+                    return builder.build();
+                }
+
+                public LowpanProvision[] newArray(int size) {
+                    return new LowpanProvision[size];
+                }
+            };
 };
diff --git a/lowpan/java/android/net/lowpan/LowpanRuntimeException.java b/lowpan/java/android/net/lowpan/LowpanRuntimeException.java
new file mode 100644
index 0000000..71a5a13
--- /dev/null
+++ b/lowpan/java/android/net/lowpan/LowpanRuntimeException.java
@@ -0,0 +1,42 @@
+/*
+ * 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.net.lowpan;
+
+import android.util.AndroidRuntimeException;
+
+/**
+ * Generic runtime exception for LoWPAN operations.
+ *
+ * @hide
+ */
+// @SystemApi
+public class LowpanRuntimeException extends AndroidRuntimeException {
+
+    public LowpanRuntimeException() {}
+
+    public LowpanRuntimeException(String message) {
+        super(message);
+    }
+
+    public LowpanRuntimeException(String message, Throwable cause) {
+        super(message, cause);
+    }
+
+    public LowpanRuntimeException(Exception cause) {
+        super(cause);
+    }
+}
diff --git a/lowpan/java/android/net/lowpan/LowpanScanner.java b/lowpan/java/android/net/lowpan/LowpanScanner.java
index b0557ee..59156c4 100644
--- a/lowpan/java/android/net/lowpan/LowpanScanner.java
+++ b/lowpan/java/android/net/lowpan/LowpanScanner.java
@@ -21,7 +21,6 @@
 import android.os.Handler;
 import android.os.RemoteException;
 import android.os.ServiceSpecificException;
-import android.util.Log;
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.HashMap;
@@ -37,7 +36,7 @@
  */
 // @SystemApi
 public class LowpanScanner {
-    private static final String TAG = LowpanInterface.class.getSimpleName();
+    private static final String TAG = LowpanScanner.class.getSimpleName();
 
     // Public Classes
 
@@ -174,7 +173,7 @@
 
         ILowpanNetScanCallback binderListener =
                 new ILowpanNetScanCallback.Stub() {
-                    public void onNetScanBeacon(Map parameters) {
+                    public void onNetScanBeacon(LowpanBeaconInfo beaconInfo) {
                         Callback callback;
                         Handler handler;
 
@@ -187,12 +186,7 @@
                             return;
                         }
 
-                        Runnable runnable =
-                                () ->
-                                        callback.onNetScanBeacon(
-                                                new LowpanBeaconInfo.Builder()
-                                                        .updateFromMap(parameters)
-                                                        .build());
+                        Runnable runnable = () -> callback.onNetScanBeacon(beaconInfo);
 
                         if (handler != null) {
                             handler.post(runnable);
@@ -231,7 +225,7 @@
             throw x.rethrowAsRuntimeException();
 
         } catch (ServiceSpecificException x) {
-            throw LowpanException.rethrowAsLowpanException(x);
+            throw LowpanException.rethrowFromServiceSpecificException(x);
         }
     }
 
@@ -246,9 +240,6 @@
 
         } catch (RemoteException x) {
             throw x.rethrowAsRuntimeException();
-
-        } catch (ServiceSpecificException x) {
-            Log.e(TAG, x.toString());
         }
     }
 
@@ -315,7 +306,7 @@
             throw x.rethrowAsRuntimeException();
 
         } catch (ServiceSpecificException x) {
-            throw LowpanException.rethrowAsLowpanException(x);
+            throw LowpanException.rethrowFromServiceSpecificException(x);
         }
     }
 
@@ -330,9 +321,6 @@
 
         } catch (RemoteException x) {
             throw x.rethrowAsRuntimeException();
-
-        } catch (ServiceSpecificException x) {
-            Log.e(TAG, x.toString());
         }
     }
 }
diff --git a/lowpan/java/android/net/lowpan/NetworkAlreadyExistsException.java b/lowpan/java/android/net/lowpan/NetworkAlreadyExistsException.java
new file mode 100644
index 0000000..90ef498
--- /dev/null
+++ b/lowpan/java/android/net/lowpan/NetworkAlreadyExistsException.java
@@ -0,0 +1,41 @@
+/*
+ * 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.net.lowpan;
+
+/**
+ * Exception indicating the form operation found a network nearby with the same identity.
+ *
+ * @see LowpanInterface
+ * @hide
+ */
+// @SystemApi
+public class NetworkAlreadyExistsException extends LowpanException {
+
+    public NetworkAlreadyExistsException() {}
+
+    public NetworkAlreadyExistsException(String message) {
+        super(message, null);
+    }
+
+    public NetworkAlreadyExistsException(String message, Throwable cause) {
+        super(message, cause);
+    }
+
+    public NetworkAlreadyExistsException(Exception cause) {
+        super(cause);
+    }
+}
diff --git a/lowpan/java/android/net/lowpan/OperationCanceledException.java b/lowpan/java/android/net/lowpan/OperationCanceledException.java
new file mode 100644
index 0000000..fcafe3a
--- /dev/null
+++ b/lowpan/java/android/net/lowpan/OperationCanceledException.java
@@ -0,0 +1,41 @@
+/*
+ * 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.net.lowpan;
+
+/**
+ * Exception indicating this operation was canceled by the driver before it could finish.
+ *
+ * @see LowpanInterface
+ * @hide
+ */
+// @SystemApi
+public class OperationCanceledException extends LowpanException {
+
+    public OperationCanceledException() {}
+
+    public OperationCanceledException(String message) {
+        super(message);
+    }
+
+    public OperationCanceledException(String message, Throwable cause) {
+        super(message, cause);
+    }
+
+    protected OperationCanceledException(Exception cause) {
+        super(cause);
+    }
+}
diff --git a/lowpan/java/android/net/lowpan/WrongStateException.java b/lowpan/java/android/net/lowpan/WrongStateException.java
new file mode 100644
index 0000000..3565419
--- /dev/null
+++ b/lowpan/java/android/net/lowpan/WrongStateException.java
@@ -0,0 +1,41 @@
+/*
+ * 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.net.lowpan;
+
+/**
+ * Exception indicating the interface is the wrong state for an operation.
+ *
+ * @see LowpanInterface
+ * @hide
+ */
+// @SystemApi
+public class WrongStateException extends LowpanException {
+
+    public WrongStateException() {}
+
+    public WrongStateException(String message) {
+        super(message);
+    }
+
+    public WrongStateException(String message, Throwable cause) {
+        super(message, cause);
+    }
+
+    protected WrongStateException(Exception cause) {
+        super(cause);
+    }
+}
diff --git a/lowpan/tests/src/android/net/lowpan/LowpanInterfaceTest.java b/lowpan/tests/src/android/net/lowpan/LowpanInterfaceTest.java
index 455ee08..a495d3d 100644
--- a/lowpan/tests/src/android/net/lowpan/LowpanInterfaceTest.java
+++ b/lowpan/tests/src/android/net/lowpan/LowpanInterfaceTest.java
@@ -16,10 +16,6 @@
 
 package android.net.lowpan;
 
-import static org.junit.Assert.assertTrue;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNull;
-import static org.junit.Assert.assertNotNull;
 import static org.mockito.Mockito.*;
 
 import android.content.Context;
@@ -29,13 +25,12 @@
 import android.os.test.TestLooper;
 import android.support.test.runner.AndroidJUnit4;
 import android.test.suitebuilder.annotation.SmallTest;
+import java.util.Map;
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
-import java.util.Map;
-import java.util.HashMap;
 
 /** Unit tests for android.net.lowpan.LowpanInterface. */
 @RunWith(AndroidJUnit4.class)
@@ -64,7 +59,8 @@
         when(mLowpanInterfaceService.getName()).thenReturn("wpan0");
         when(mLowpanInterfaceService.asBinder()).thenReturn(mLowpanInterfaceBinder);
 
-        mLowpanInterface = new LowpanInterface(mContext, mLowpanInterfaceService, mTestLooper.getLooper());
+        mLowpanInterface =
+                new LowpanInterface(mContext, mLowpanInterfaceService, mTestLooper.getLooper());
     }
 
     @Test
@@ -81,12 +77,8 @@
                                     return listener instanceof ILowpanInterfaceListener;
                                 }));
 
-        // Build a changed property map
-        Map<String, Object> changedProperties = new HashMap<>();
-        LowpanProperties.KEY_INTERFACE_STATE.putInMap(changedProperties, LowpanInterface.STATE_OFFLINE);
-
         // Change some properties
-        mInterfaceListener.onPropertiesChanged(changedProperties);
+        mInterfaceListener.onStateChanged(LowpanInterface.STATE_OFFLINE);
         mTestLooper.dispatchAll();
 
         // Verify that the property was changed
diff --git a/lowpan/tests/src/android/net/lowpan/LowpanManagerTest.java b/lowpan/tests/src/android/net/lowpan/LowpanManagerTest.java
index 481ad76..3dd7504 100644
--- a/lowpan/tests/src/android/net/lowpan/LowpanManagerTest.java
+++ b/lowpan/tests/src/android/net/lowpan/LowpanManagerTest.java
@@ -16,10 +16,9 @@
 
 package android.net.lowpan;
 
-import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
 import static org.mockito.Mockito.*;
 
 import android.content.Context;
diff --git a/media/jni/audioeffect/android_media_AudioEffect.cpp b/media/jni/audioeffect/android_media_AudioEffect.cpp
index d4cc3fb..1f93b34 100644
--- a/media/jni/audioeffect/android_media_AudioEffect.cpp
+++ b/media/jni/audioeffect/android_media_AudioEffect.cpp
@@ -20,7 +20,7 @@
 #define LOG_TAG "AudioEffects-JNI"
 
 #include <utils/Log.h>
-#include <nativehelper/jni.h>
+#include <jni.h>
 #include <nativehelper/JNIHelp.h>
 #include <android_runtime/AndroidRuntime.h>
 #include "media/AudioEffect.h"
diff --git a/media/jni/audioeffect/android_media_Visualizer.cpp b/media/jni/audioeffect/android_media_Visualizer.cpp
index 0645543..f0cfeef 100644
--- a/media/jni/audioeffect/android_media_Visualizer.cpp
+++ b/media/jni/audioeffect/android_media_Visualizer.cpp
@@ -20,7 +20,7 @@
 #define LOG_TAG "visualizers-JNI"
 
 #include <utils/Log.h>
-#include <nativehelper/jni.h>
+#include <jni.h>
 #include <nativehelper/JNIHelp.h>
 #include <android_runtime/AndroidRuntime.h>
 #include <utils/threads.h>
diff --git a/media/jni/midi/android_media_midi_MidiDevice.cpp b/media/jni/midi/android_media_midi_MidiDevice.cpp
index 4df8436..5b35453 100644
--- a/media/jni/midi/android_media_midi_MidiDevice.cpp
+++ b/media/jni/midi/android_media_midi_MidiDevice.cpp
@@ -18,8 +18,8 @@
 #define LOG_TAG "Midi-JNI"
 
 #include <android_util_Binder.h>
+#include <jni.h>
 #include <midi_internal.h>
-#include <nativehelper/jni.h>
 #include <utils/Log.h>
 
 using namespace android;
diff --git a/media/jni/soundpool/android_media_SoundPool.cpp b/media/jni/soundpool/android_media_SoundPool.cpp
index 9d0c1f8..f670636 100644
--- a/media/jni/soundpool/android_media_SoundPool.cpp
+++ b/media/jni/soundpool/android_media_SoundPool.cpp
@@ -20,7 +20,7 @@
 #define LOG_TAG "SoundPool-JNI"
 
 #include <utils/Log.h>
-#include <nativehelper/jni.h>
+#include <jni.h>
 #include <nativehelper/JNIHelp.h>
 #include <android_runtime/AndroidRuntime.h>
 #include "SoundPool.h"
diff --git a/packages/BackupRestoreConfirmation/res/values-hy/strings.xml b/packages/BackupRestoreConfirmation/res/values-hy/strings.xml
index a054068..285c15d 100644
--- a/packages/BackupRestoreConfirmation/res/values-hy/strings.xml
+++ b/packages/BackupRestoreConfirmation/res/values-hy/strings.xml
@@ -25,12 +25,12 @@
     <string name="allow_restore_button_label" msgid="3081286752277127827">"Վերականգնել իմ տվյալները"</string>
     <string name="deny_restore_button_label" msgid="1724367334453104378">"Չվերականգնել"</string>
     <string name="current_password_text" msgid="8268189555578298067">"Խնդրում ենք մուտքագրել ձեր ընթացիկ պահուստային գաղտնաբառը ներքևում`"</string>
-    <string name="device_encryption_restore_text" msgid="1570864916855208992">"Խնդրում ենք մուտքագրել ձեր սարքի կոդավորված գաղտնաբառը ներքևում:"</string>
-    <string name="device_encryption_backup_text" msgid="5866590762672844664">"Խնդրում ենք մուտքագրել ձեր սարքի կոդավորված գաղտնաբառը ներքևում: Այն նաև կօգտագործվի պահուստային արխիվի կոդավորման համար:"</string>
-    <string name="backup_enc_password_text" msgid="4981585714795233099">"Խնդրում ենք մուտքագրել գաղտնաբառը` ամբողջական պահուստավորվող տվյալները կոդավորելու համար: Եթե ​​այն դատարկ թողնեք, ապա կօգտագործվի ձեր առկա պահուստավորման գաղտնաբառը`"</string>
-    <string name="backup_enc_password_optional" msgid="1350137345907579306">"Եթե ​​ցանկանում եք կոդավորել ամբողջական պահուստավորված տվյալները, մուտքագրեք գաղտնաբառ ստորև`"</string>
-    <string name="backup_enc_password_required" msgid="7889652203371654149">"Քանի որ ձեր սարքը կոդավորված է, դուք պետք է կոդավորեք նաև ձեր պահուստը: Խնդրում ենք ստորև սահմանել գաղտնաբառը՝"</string>
-    <string name="restore_enc_password_text" msgid="6140898525580710823">"Եթե ​​վերականգնվող տվյալները կոդավորված են, խնդրում ենք մուտքագրել գաղտնաբառը ստորև`"</string>
+    <string name="device_encryption_restore_text" msgid="1570864916855208992">"Խնդրում ենք մուտքագրել ձեր սարքի գաղտնագրման գաղտնաբառը ներքևում:"</string>
+    <string name="device_encryption_backup_text" msgid="5866590762672844664">"Խնդրում ենք մուտքագրել ձեր սարքի գաղտնագրման գաղտնաբառը ներքևում: Այն նաև կօգտագործվի պահուստային արխիվի գաղտնագրման համար:"</string>
+    <string name="backup_enc_password_text" msgid="4981585714795233099">"Խնդրում ենք մուտքագրել գաղտնաբառը` ամբողջական պահուստավորվող տվյալները գաղտնագրելու համար: Եթե ​​այն դատարկ թողնեք, ապա կօգտագործվի ձեր առկա պահուստավորման գաղտնաբառը`"</string>
+    <string name="backup_enc_password_optional" msgid="1350137345907579306">"Եթե ​​ցանկանում եք գաղտնագրել ամբողջական պահուստավորված տվյալները, մուտքագրեք գաղտնաբառ ստորև`"</string>
+    <string name="backup_enc_password_required" msgid="7889652203371654149">"Քանի որ ձեր սարքը գաղտնագրված է, դուք պետք է գաղտնագրեք նաև ձեր պահուստը: Խնդրում ենք ստորև սահմանել գաղտնաբառը՝"</string>
+    <string name="restore_enc_password_text" msgid="6140898525580710823">"Եթե ​​վերականգնվող տվյալները գաղտնագրված են, խնդրում ենք մուտքագրել գաղտնաբառը ստորև`"</string>
     <string name="toast_backup_started" msgid="550354281452756121">"Պահուստավորումը սկսվում է..."</string>
     <string name="toast_backup_ended" msgid="3818080769548726424">"Պահուստավորումն ավարտվեց"</string>
     <string name="toast_restore_started" msgid="7881679218971277385">"Վերականգնումը մեկնարկեց..."</string>
diff --git a/packages/CaptivePortalLogin/src/com/android/captiveportallogin/CaptivePortalLoginActivity.java b/packages/CaptivePortalLogin/src/com/android/captiveportallogin/CaptivePortalLoginActivity.java
index a7e1490..be87ed2 100644
--- a/packages/CaptivePortalLogin/src/com/android/captiveportallogin/CaptivePortalLoginActivity.java
+++ b/packages/CaptivePortalLogin/src/com/android/captiveportallogin/CaptivePortalLoginActivity.java
@@ -31,6 +31,7 @@
 import android.net.Proxy;
 import android.net.Uri;
 import android.net.http.SslError;
+import android.os.Build;
 import android.os.Bundle;
 import android.provider.Settings;
 import android.util.ArrayMap;
@@ -57,6 +58,7 @@
 import java.lang.InterruptedException;
 import java.lang.reflect.Field;
 import java.lang.reflect.Method;
+import java.util.Objects;
 import java.util.Random;
 import java.util.concurrent.atomic.AtomicBoolean;
 
@@ -286,6 +288,18 @@
         return null;
     }
 
+    private static String host(URL url) {
+        if (url == null) {
+            return null;
+        }
+        return url.getHost();
+    }
+
+    private static String sanitizeURL(URL url) {
+        // In non-Debug build, only show host to avoid leaking private info.
+        return Build.IS_DEBUGGABLE ? Objects.toString(url) : host(url);
+    }
+
     private void testForCaptivePortal() {
         // TODO: reuse NetworkMonitor facilities for consistent captive portal detection.
         new Thread(new Runnable() {
@@ -339,6 +353,8 @@
                     TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 1,
                     getResources().getDisplayMetrics());
         private int mPagesLoaded;
+        // the host of the page that this webview is currently loading. Can be null when undefined.
+        private String mHostname;
 
         // If we haven't finished cleaning up the history, don't allow going back.
         public boolean allowBack() {
@@ -346,8 +362,8 @@
         }
 
         @Override
-        public void onPageStarted(WebView view, String url, Bitmap favicon) {
-            if (url.contains(mBrowserBailOutToken)) {
+        public void onPageStarted(WebView view, String urlString, Bitmap favicon) {
+            if (urlString.contains(mBrowserBailOutToken)) {
                 mLaunchBrowser = true;
                 done(Result.WANTED_AS_IS);
                 return;
@@ -355,11 +371,17 @@
             // The first page load is used only to cause the WebView to
             // fetch the proxy settings.  Don't update the URL bar, and
             // don't check if the captive portal is still there.
-            if (mPagesLoaded == 0) return;
+            if (mPagesLoaded == 0) {
+                return;
+            }
+            final URL url = makeURL(urlString);
+            Log.d(TAG, "onPageSarted: " + sanitizeURL(url));
+            mHostname = host(url);
             // For internally generated pages, leave URL bar listing prior URL as this is the URL
             // the page refers to.
-            if (!url.startsWith(INTERNAL_ASSETS)) {
-                getActionBar().setSubtitle(getHeaderSubtitle(url));
+            if (!urlString.startsWith(INTERNAL_ASSETS)) {
+                String subtitle = (url != null) ? getHeaderSubtitle(url) : urlString;
+                getActionBar().setSubtitle(subtitle);
             }
             getProgressBar().setVisibility(View.VISIBLE);
             testForCaptivePortal();
@@ -401,15 +423,18 @@
 
         @Override
         public void onReceivedSslError(WebView view, SslErrorHandler handler, SslError error) {
-            logMetricsEvent(MetricsEvent.CAPTIVE_PORTAL_LOGIN_ACTIVITY_SSL_ERROR);
-            Log.w(TAG, "SSL error (error: " + error.getPrimaryError() + " host: " +
-                    // Only show host to avoid leaking private info.
-                    Uri.parse(error.getUrl()).getHost() + " certificate: " +
-                    error.getCertificate() + "); displaying SSL warning.");
-            final String sslErrorPage = makeSslErrorPage();
-            if (VDBG) {
-                Log.d(TAG, sslErrorPage);
+            final URL url = makeURL(error.getUrl());
+            final String host = host(url);
+            Log.d(TAG, String.format("SSL error: %s, url: %s, certificate: %s",
+                    error.getPrimaryError(), sanitizeURL(url), error.getCertificate()));
+            if (url == null || !Objects.equals(host, mHostname)) {
+                // Ignore ssl errors for resources coming from a different hostname than the page
+                // that we are currently loading, and only cancel the request.
+                handler.cancel();
+                return;
             }
+            logMetricsEvent(MetricsEvent.CAPTIVE_PORTAL_LOGIN_ACTIVITY_SSL_ERROR);
+            final String sslErrorPage = makeSslErrorPage();
             view.loadDataWithBaseURL(INTERNAL_ASSETS, sslErrorPage, "text/HTML", "UTF-8", null);
         }
 
@@ -502,16 +527,13 @@
         return getString(R.string.action_bar_title, info.getExtraInfo().replaceAll("^\"|\"$", ""));
     }
 
-    private String getHeaderSubtitle(String urlString) {
-        URL url = makeURL(urlString);
-        if (url == null) {
-            return urlString;
-        }
+    private String getHeaderSubtitle(URL url) {
+        String host = host(url);
         final String https = "https";
         if (https.equals(url.getProtocol())) {
-            return https + "://" + url.getHost();
+            return https + "://" + host;
         }
-        return url.getHost();
+        return host;
     }
 
     private void logMetricsEvent(int event) {
diff --git a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/DeviceChooserActivity.java b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/DeviceChooserActivity.java
index 0cf21d2..30c1fff 100644
--- a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/DeviceChooserActivity.java
+++ b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/DeviceChooserActivity.java
@@ -55,6 +55,8 @@
             Log.e(LOG_TAG, "About to show UI, but no devices to show");
         }
 
+        mPairButton = findViewById(R.id.button_pair);
+
         if (getService().mRequest.isSingleDevice()) {
             setContentView(R.layout.device_confirmation);
             final DeviceFilterPair selectedDevice = getService().mDevicesFound.get(0);
@@ -62,9 +64,12 @@
                     R.string.confirmation_title,
                     getCallingAppName(),
                     selectedDevice.getDisplayName()), 0));
+            mPairButton.setOnClickListener(v -> onDeviceConfirmed(getService().mSelectedDevice));
             getService().mSelectedDevice = selectedDevice;
+            onSelectionUpdate();
         } else {
             setContentView(R.layout.device_chooser);
+            mPairButton.setVisibility(View.GONE);
             setTitle(Html.fromHtml(getString(R.string.chooser_title, getCallingAppName()), 0));
             mDeviceListView = findViewById(R.id.device_list);
             final DeviceDiscoveryService.DevicesAdapter adapter = getService().mDevicesAdapter;
@@ -72,16 +77,12 @@
             adapter.registerDataSetObserver(new DataSetObserver() {
                 @Override
                 public void onChanged() {
-                    updatePairButtonEnabled();
+                    onSelectionUpdate();
                 }
             });
             mDeviceListView.addFooterView(getProgressBar(), null, false);
         }
 
-        mPairButton = findViewById(R.id.button_pair);
-        mPairButton.setOnClickListener(v -> onPairTapped(getService().mSelectedDevice));
-        updatePairButtonEnabled();
-
         mCancelButton = findViewById(R.id.button_cancel);
         mCancelButton.setOnClickListener(v -> cancel());
     }
@@ -134,15 +135,20 @@
         return r.getDimensionPixelSize(R.dimen.padding);
     }
 
-    private void updatePairButtonEnabled() {
-        mPairButton.setEnabled(getService().mSelectedDevice != null);
+    private void onSelectionUpdate() {
+        DeviceFilterPair selectedDevice = getService().mSelectedDevice;
+        if (mPairButton.getVisibility() != View.VISIBLE && selectedDevice != null) {
+            onDeviceConfirmed(selectedDevice);
+        } else {
+            mPairButton.setEnabled(selectedDevice != null);
+        }
     }
 
     private DeviceDiscoveryService getService() {
         return DeviceDiscoveryService.sInstance;
     }
 
-   protected void onPairTapped(DeviceFilterPair selectedDevice) {
+   protected void onDeviceConfirmed(DeviceFilterPair selectedDevice) {
         getService().onDeviceSelected(
                 getCallingPackage(), getDeviceMacAddress(selectedDevice.device));
         setResult(RESULT_OK,
diff --git a/packages/PrintSpooler/AndroidManifest.xml b/packages/PrintSpooler/AndroidManifest.xml
index 4b9415e..91e23dd 100644
--- a/packages/PrintSpooler/AndroidManifest.xml
+++ b/packages/PrintSpooler/AndroidManifest.xml
@@ -36,6 +36,8 @@
     <uses-permission android:name="android.permission.START_PRINT_SERVICE_CONFIG_ACTIVITY"/>
     <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
     <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
+    <uses-permission android:name="android.permission.READ_PRINT_SERVICES" />
+    <uses-permission android:name="android.permission.READ_PRINT_SERVICE_RECOMMENDATIONS" />
 
     <application
         android:allowClearUserData="true"
diff --git a/packages/PrintSpooler/res/values-bs/strings.xml b/packages/PrintSpooler/res/values-bs/strings.xml
index d3f1b80..2e9bfa3 100644
--- a/packages/PrintSpooler/res/values-bs/strings.xml
+++ b/packages/PrintSpooler/res/values-bs/strings.xml
@@ -53,7 +53,7 @@
     <string name="print_search_box_shown_utterance" msgid="7967404953901376090">"Okvir za pretraživanje je prikazan"</string>
     <string name="print_search_box_hidden_utterance" msgid="5727755169343113351">"Okvir za pretraživanje je skriven"</string>
     <string name="print_add_printer" msgid="1088656468360653455">"Dodaj štampač"</string>
-    <string name="print_select_printer" msgid="7388760939873368698">"Izaberite štampač"</string>
+    <string name="print_select_printer" msgid="7388760939873368698">"Odaberite štampač"</string>
     <string name="print_forget_printer" msgid="5035287497291910766">"Zaboravi ovaj štampač"</string>
     <plurals name="print_search_result_count_utterance" formatted="false" msgid="6997663738361080868">
       <item quantity="one"><xliff:g id="COUNT_1">%1$s</xliff:g> štampač je pronađen</item>
diff --git a/packages/SettingsLib/res/layout/preference_category_material_settings.xml b/packages/SettingsLib/res/layout/preference_category_material_settings.xml
index 741435e..245e3b7 100644
--- a/packages/SettingsLib/res/layout/preference_category_material_settings.xml
+++ b/packages/SettingsLib/res/layout/preference_category_material_settings.xml
@@ -51,6 +51,7 @@
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
             android:textAppearance="@android:style/TextAppearance.Material.Body2"
+            android:textAlignment="viewStart"
             android:textColor="?android:attr/colorAccent"
             android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"/>
         <TextView
diff --git a/packages/SettingsLib/res/values-ca/strings.xml b/packages/SettingsLib/res/values-ca/strings.xml
index 5662703..00bb4a9 100644
--- a/packages/SettingsLib/res/values-ca/strings.xml
+++ b/packages/SettingsLib/res/values-ca/strings.xml
@@ -36,7 +36,7 @@
     <string name="wifi_no_internet" msgid="3880396223819116454">"No hi ha accés a Internet"</string>
     <string name="saved_network" msgid="4352716707126620811">"Desat per <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="connected_via_network_scorer" msgid="5713793306870815341">"Connectada automàticament a través de: %1$s"</string>
-    <string name="connected_via_network_scorer_default" msgid="7867260222020343104">"Connectada automàticament a través d\'un proveïdor de valoració de la xarxa"</string>
+    <string name="connected_via_network_scorer_default" msgid="7867260222020343104">"Connectada automàticament a través d\'un proveïdor de valoració de xarxes"</string>
     <string name="connected_via_passpoint" msgid="2826205693803088747">"Connectada mitjançant %1$s"</string>
     <string name="available_via_passpoint" msgid="1617440946846329613">"Disponible mitjançant %1$s"</string>
     <string name="wifi_connected_no_internet" msgid="3149853966840874992">"Connectada, sense Internet"</string>
@@ -186,7 +186,7 @@
     <string name="wifi_aggressive_handover" msgid="5309131983693661320">"Transferència agressiva de Wi-Fi a mòbil"</string>
     <string name="wifi_allow_scan_with_traffic" msgid="3601853081178265786">"Permet sempre cerca de Wi-Fi en ininerància"</string>
     <string name="mobile_data_always_on" msgid="8774857027458200434">"Dades mòbils sempre actives"</string>
-    <string name="tethering_hardware_offload" msgid="7470077827090325814">"Acceleració per maquinari per a la compartició de xarxa"</string>
+    <string name="tethering_hardware_offload" msgid="7470077827090325814">"Acceleració per maquinari per compartir la xarxa"</string>
     <string name="bluetooth_disable_absolute_volume" msgid="2660673801947898809">"Desactiva el volum absolut"</string>
     <string name="bluetooth_enable_inband_ringing" msgid="3291686366721786740">"Activa el so al mateix canal"</string>
     <string name="bluetooth_select_avrcp_version_string" msgid="3750059931120293633">"Versió AVRCP de Bluetooth"</string>
@@ -218,7 +218,7 @@
     <string name="allow_mock_location_summary" msgid="317615105156345626">"Permet les ubicacions simulades"</string>
     <string name="debug_view_attributes" msgid="6485448367803310384">"Inspecció d\'atributs de visualització"</string>
     <string name="mobile_data_always_on_summary" msgid="8149773901431697910">"Mantén les dades mòbils sempre actives, fins i tot quan la Wi‑Fi està activada (per canviar de xarxa ràpidament)."</string>
-    <string name="tethering_hardware_offload_summary" msgid="7726082075333346982">"Fes servir l\'acceleració per maquinari per a la compartició de xarxa, si està disponible"</string>
+    <string name="tethering_hardware_offload_summary" msgid="7726082075333346982">"Fes servir l\'acceleració per maquinari per compartir la xarxa, si està disponible"</string>
     <string name="adb_warning_title" msgid="6234463310896563253">"Voleu permetre la depuració USB?"</string>
     <string name="adb_warning_message" msgid="7316799925425402244">"La depuració USB només està indicada per a activitats de desenvolupament. Fes-la servir intercanviar dades entre l\'ordinador i el dispositiu, per instal·lar aplicacions al dispositiu sense rebre notificacions i per llegir dades de registre."</string>
     <string name="adb_keys_warning_message" msgid="5659849457135841625">"Vols revocar l\'accés a la depuració d\'USB dels ordinadors que has autoritzat anteriorment?"</string>
diff --git a/packages/SettingsLib/res/values-es/strings.xml b/packages/SettingsLib/res/values-es/strings.xml
index e2e0e62..4a6f12d 100644
--- a/packages/SettingsLib/res/values-es/strings.xml
+++ b/packages/SettingsLib/res/values-es/strings.xml
@@ -104,11 +104,11 @@
     <string name="process_kernel_label" msgid="3916858646836739323">"SO Android"</string>
     <string name="data_usage_uninstalled_apps" msgid="614263770923231598">"Aplicaciones eliminadas"</string>
     <string name="data_usage_uninstalled_apps_users" msgid="7986294489899813194">"Usuarios y aplicaciones eliminados"</string>
-    <string name="tether_settings_title_usb" msgid="6688416425801386511">"Compartir por USB"</string>
+    <string name="tether_settings_title_usb" msgid="6688416425801386511">"Compartir conexión por USB"</string>
     <string name="tether_settings_title_wifi" msgid="3277144155960302049">"Zona Wi-Fi portátil"</string>
-    <string name="tether_settings_title_bluetooth" msgid="355855408317564420">"Compartir por Bluetooth"</string>
-    <string name="tether_settings_title_usb_bluetooth" msgid="5355828977109785001">"Compartir Internet"</string>
-    <string name="tether_settings_title_all" msgid="8356136101061143841">"Compartir Internet y zona Wi-Fi"</string>
+    <string name="tether_settings_title_bluetooth" msgid="355855408317564420">"Compartir conexión por Bluetooth"</string>
+    <string name="tether_settings_title_usb_bluetooth" msgid="5355828977109785001">"Compartir conexión"</string>
+    <string name="tether_settings_title_all" msgid="8356136101061143841">"Compartir conexión y zona Wi-Fi"</string>
     <string name="managed_user_title" msgid="8109605045406748842">"Todas las aplicaciones de trabajo"</string>
     <string name="user_guest" msgid="8475274842845401871">"Invitado"</string>
     <string name="unknown" msgid="1592123443519355854">"Desconocido"</string>
@@ -162,7 +162,7 @@
     <string name="development_settings_summary" msgid="1815795401632854041">"Establecer opciones de desarrollo de aplicaciones"</string>
     <string name="development_settings_not_available" msgid="4308569041701535607">"Las opciones de desarrollador no están disponibles para este usuario"</string>
     <string name="vpn_settings_not_available" msgid="956841430176985598">"Los ajustes de VPN no están disponibles para este usuario"</string>
-    <string name="tethering_settings_not_available" msgid="6765770438438291012">"Los ajustes para compartir Internet no están disponibles para este usuario"</string>
+    <string name="tethering_settings_not_available" msgid="6765770438438291012">"Los ajustes para compartir conexión no están disponibles para este usuario"</string>
     <string name="apn_settings_not_available" msgid="7873729032165324000">"Los ajustes del nombre de punto de acceso no están disponibles para este usuario"</string>
     <string name="enable_adb" msgid="7982306934419797485">"Depuración por USB"</string>
     <string name="enable_adb_summary" msgid="4881186971746056635">"Activar el modo de depuración cuando el dispositivo esté conectado por USB"</string>
@@ -186,7 +186,7 @@
     <string name="wifi_aggressive_handover" msgid="5309131983693661320">"Transferencia agresiva de Wi-Fi a móvil"</string>
     <string name="wifi_allow_scan_with_traffic" msgid="3601853081178265786">"Permitir siempre búsquedas de Wi-Fi"</string>
     <string name="mobile_data_always_on" msgid="8774857027458200434">"Datos móviles siempre activos"</string>
-    <string name="tethering_hardware_offload" msgid="7470077827090325814">"Aceleración por hardware para conexión mediante dispositivo portátil"</string>
+    <string name="tethering_hardware_offload" msgid="7470077827090325814">"Aceleración por hardware para conexión compartida"</string>
     <string name="bluetooth_disable_absolute_volume" msgid="2660673801947898809">"Inhabilitar volumen absoluto"</string>
     <string name="bluetooth_enable_inband_ringing" msgid="3291686366721786740">"Habilitar tono de llamada por Bluetooth"</string>
     <string name="bluetooth_select_avrcp_version_string" msgid="3750059931120293633">"Versión AVRCP del Bluetooth"</string>
@@ -218,7 +218,7 @@
     <string name="allow_mock_location_summary" msgid="317615105156345626">"Permitir ubicaciones simuladas"</string>
     <string name="debug_view_attributes" msgid="6485448367803310384">"Inspección de atributos de vista"</string>
     <string name="mobile_data_always_on_summary" msgid="8149773901431697910">"Mantén los datos móviles siempre activos, aunque la conexión Wi‑Fi esté activada (para cambiar de red rápidamente)."</string>
-    <string name="tethering_hardware_offload_summary" msgid="7726082075333346982">"Usar la aceleración por hardware para conexión mediante dispositivo portátil si está disponible"</string>
+    <string name="tethering_hardware_offload_summary" msgid="7726082075333346982">"Usar la conexión compartida con aceleración por hardware si está disponible"</string>
     <string name="adb_warning_title" msgid="6234463310896563253">"¿Permitir depuración por USB?"</string>
     <string name="adb_warning_message" msgid="7316799925425402244">"La depuración por USB solo está indicada para actividades de desarrollo. Puedes utilizarla para intercambiar datos entre el ordenador y el dispositivo, para instalar aplicaciones en el dispositivo sin recibir notificaciones y para leer datos de registro."</string>
     <string name="adb_keys_warning_message" msgid="5659849457135841625">"¿Quieres revocar el acceso a la depuración por USB de todos los ordenadores que has autorizado?"</string>
diff --git a/packages/SettingsLib/res/values/attrs.xml b/packages/SettingsLib/res/values/attrs.xml
index 6d852df..a8a1793 100644
--- a/packages/SettingsLib/res/values/attrs.xml
+++ b/packages/SettingsLib/res/values/attrs.xml
@@ -48,9 +48,4 @@
 
     <attr name="footerPreferenceStyle" format="reference" />
 
-    <declare-styleable name="PreferenceImageView">
-        <attr name="maxWidth" format="dimension" />
-        <attr name="maxHeight" format="dimension" />
-    </declare-styleable>
-
 </resources>
diff --git a/packages/SettingsLib/res/values/strings.xml b/packages/SettingsLib/res/values/strings.xml
index 064cc84..439fff3 100644
--- a/packages/SettingsLib/res/values/strings.xml
+++ b/packages/SettingsLib/res/values/strings.xml
@@ -938,9 +938,6 @@
     <!-- Content description for drawer menu button [CHAR_LIMIT=30]-->
     <string name="content_description_menu_button">Menu</string>
 
-    <!-- Label for Greenwich mean time, used in a string like GMT+05:00. [CHAR LIMIT=NONE] -->
-    <string name="time_zone_gmt">GMT</string>
-
     <!-- Label for carrier demo mode factory reset confirmation dialog. [CHAR LIMIT=NONE] -->
     <string name="retail_demo_reset_message">Enter password to perform factory reset in demo mode</string>
     <!-- Label for positive button on carrier demo  mode factory reset confirmation dialog [CHAR LIMIT=40] -->
diff --git a/packages/SettingsLib/res/values/styles_support_preference.xml b/packages/SettingsLib/res/values/styles_support_preference.xml
index 2a5c30f..cf9f3c6 100644
--- a/packages/SettingsLib/res/values/styles_support_preference.xml
+++ b/packages/SettingsLib/res/values/styles_support_preference.xml
@@ -26,7 +26,7 @@
     </style>
 
     <!-- Preferences -->
-    <style name="Preference.SettingsBase" parent="@*android:style/Preference.Material">
+    <style name="Preference.SettingsBase" parent="@style/Preference.Material">
         <item name="allowDividerAbove">false</item>
         <item name="allowDividerBelow">true</item>
         <item name="singleLineTitle">false</item>
diff --git a/packages/SettingsLib/src/com/android/settingslib/NetworkPolicyEditor.java b/packages/SettingsLib/src/com/android/settingslib/NetworkPolicyEditor.java
index 2e77f42..c346898 100644
--- a/packages/SettingsLib/src/com/android/settingslib/NetworkPolicyEditor.java
+++ b/packages/SettingsLib/src/com/android/settingslib/NetworkPolicyEditor.java
@@ -21,6 +21,7 @@
 import static android.net.NetworkPolicy.SNOOZE_NEVER;
 import static android.net.NetworkPolicy.WARNING_DISABLED;
 import static android.net.NetworkTemplate.MATCH_WIFI;
+
 import static com.android.internal.util.Preconditions.checkNotNull;
 
 import android.net.NetworkPolicy;
@@ -150,7 +151,7 @@
 
     public int getPolicyCycleDay(NetworkTemplate template) {
         final NetworkPolicy policy = getPolicy(template);
-        return (policy != null) ? policy.cycleDay : -1;
+        return (policy != null) ? policy.cycleDay : CYCLE_NONE;
     }
 
     public void setPolicyCycleDay(NetworkTemplate template, int cycleDay, String cycleTimezone) {
@@ -204,53 +205,6 @@
         writeAsync();
     }
 
-    public boolean getPolicyMetered(NetworkTemplate template) {
-        NetworkPolicy policy = getPolicy(template);
-        if (policy != null) {
-            return policy.metered;
-        } else {
-            return false;
-        }
-    }
-
-    public void setPolicyMetered(NetworkTemplate template, boolean metered) {
-        boolean modified = false;
-
-        NetworkPolicy policy = getPolicy(template);
-        if (metered) {
-            if (policy == null) {
-                policy = buildDefaultPolicy(template);
-                policy.metered = true;
-                policy.inferred = false;
-                mPolicies.add(policy);
-                modified = true;
-            } else if (!policy.metered) {
-                policy.metered = true;
-                policy.inferred = false;
-                modified = true;
-            }
-
-        } else {
-            if (policy == null) {
-                // ignore when policy doesn't exist
-            } else if (policy.metered) {
-                policy.metered = false;
-                policy.inferred = false;
-                modified = true;
-            }
-        }
-
-        // Remove legacy unquoted policies while we're here
-        final NetworkTemplate unquoted = buildUnquotedNetworkTemplate(template);
-        final NetworkPolicy unquotedPolicy = getPolicy(unquoted);
-        if (unquotedPolicy != null) {
-            mPolicies.remove(unquotedPolicy);
-            modified = true;
-        }
-
-        if (modified) writeAsync();
-    }
-
     /**
      * Build a revised {@link NetworkTemplate} that matches the same rule, but
      * with an unquoted {@link NetworkTemplate#getNetworkId()}. Used to work
diff --git a/packages/SettingsLib/src/com/android/settingslib/applications/PackageManagerWrapper.java b/packages/SettingsLib/src/com/android/settingslib/applications/PackageManagerWrapper.java
index caa7929..6c79a61 100644
--- a/packages/SettingsLib/src/com/android/settingslib/applications/PackageManagerWrapper.java
+++ b/packages/SettingsLib/src/com/android/settingslib/applications/PackageManagerWrapper.java
@@ -123,4 +123,9 @@
      * @return the label as a CharSequence
      */
     CharSequence loadLabel(ApplicationInfo app);
+
+    /**
+     * Retrieve all activities that can be performed for the given intent.
+     */
+    List<ResolveInfo> queryIntentActivities(Intent intent, int flags);
 }
diff --git a/packages/SettingsLib/src/com/android/settingslib/applications/PackageManagerWrapperImpl.java b/packages/SettingsLib/src/com/android/settingslib/applications/PackageManagerWrapperImpl.java
index 9b2cd7c..dcb40b2 100644
--- a/packages/SettingsLib/src/com/android/settingslib/applications/PackageManagerWrapperImpl.java
+++ b/packages/SettingsLib/src/com/android/settingslib/applications/PackageManagerWrapperImpl.java
@@ -113,4 +113,9 @@
     public CharSequence loadLabel(ApplicationInfo app) {
         return app.loadLabel(mPm);
     }
+
+    @Override
+    public List<ResolveInfo> queryIntentActivities(Intent intent, int flags) {
+        return mPm.queryIntentActivities(intent, flags);
+    }
 }
diff --git a/packages/SettingsLib/src/com/android/settingslib/datetime/ZoneGetter.java b/packages/SettingsLib/src/com/android/settingslib/datetime/ZoneGetter.java
index 1cbb745..1771208 100644
--- a/packages/SettingsLib/src/com/android/settingslib/datetime/ZoneGetter.java
+++ b/packages/SettingsLib/src/com/android/settingslib/datetime/ZoneGetter.java
@@ -18,6 +18,7 @@
 
 import android.content.Context;
 import android.content.res.XmlResourceParser;
+import android.icu.text.TimeZoneFormat;
 import android.icu.text.TimeZoneNames;
 import android.support.v4.text.BidiFormatter;
 import android.support.v4.text.TextDirectionHeuristicsCompat;
@@ -88,8 +89,9 @@
     private static final String XMLTAG_TIMEZONE = "timezone";
 
     public static CharSequence getTimeZoneOffsetAndName(Context context, TimeZone tz, Date now) {
-        Locale locale = Locale.getDefault();
-        CharSequence gmtText = getGmtOffsetText(context, locale, tz, now);
+        Locale locale = context.getResources().getConfiguration().locale;
+        TimeZoneFormat tzFormatter = TimeZoneFormat.getInstance(locale);
+        CharSequence gmtText = getGmtOffsetText(tzFormatter, locale, tz, now);
         TimeZoneNames timeZoneNames = TimeZoneNames.getInstance(locale);
         String zoneNameString = getZoneLongName(timeZoneNames, tz, now);
         if (zoneNameString == null) {
@@ -101,7 +103,7 @@
     }
 
     public static List<Map<String, Object>> getZonesList(Context context) {
-        final Locale locale = Locale.getDefault();
+        final Locale locale = context.getResources().getConfiguration().locale;
         final Date now = new Date();
         final TimeZoneNames timeZoneNames = TimeZoneNames.getInstance(locale);
         final ZoneGetterData data = new ZoneGetterData(context);
@@ -243,12 +245,15 @@
         builder.setSpan(span, start, builder.length(), 0);
     }
 
-    private static String twoDigits(int input) {
-        StringBuilder builder = new StringBuilder(3);
-        if (input < 0) builder.append('-');
-        String string = Integer.toString(Math.abs(input));
-        if (string.length() == 1) builder.append("0");
-        builder.append(string);
+    // Input must be positive. minDigits must be 1 or 2.
+    private static String formatDigits(int input, int minDigits, String localizedDigits) {
+        final int tens = input / 10;
+        final int units = input % 10;
+        StringBuilder builder = new StringBuilder(minDigits);
+        if (input >= 10 || minDigits == 2) {
+            builder.append(localizedDigits.charAt(tens));
+        }
+        builder.append(localizedDigits.charAt(units));
         return builder.toString();
     }
 
@@ -256,36 +261,83 @@
      * Get the GMT offset text label for the given time zone, in the format "GMT-08:00". This will
      * also add TTS spans to give hints to the text-to-speech engine for the type of data it is.
      *
-     * @param context The context which the string is displayed in.
+     * @param tzFormatter The timezone formatter to use.
      * @param locale The locale which the string is displayed in. This should be the same as the
-     *               locale of the context.
+     *               locale of the time zone formatter.
      * @param tz Time zone to get the GMT offset from.
      * @param now The current time, used to tell whether daylight savings is active.
      * @return A CharSequence suitable for display as the offset label of {@code tz}.
      */
-    private static CharSequence getGmtOffsetText(Context context, Locale locale, TimeZone tz,
-            Date now) {
-        SpannableStringBuilder builder = new SpannableStringBuilder();
+    private static CharSequence getGmtOffsetText(TimeZoneFormat tzFormatter, Locale locale,
+            TimeZone tz, Date now) {
+        final SpannableStringBuilder builder = new SpannableStringBuilder();
 
-        appendWithTtsSpan(builder, "GMT",
-                new TtsSpan.TextBuilder(context.getString(R.string.time_zone_gmt)).build());
-
-        int offsetMillis = tz.getOffset(now.getTime());
-        if (offsetMillis >= 0) {
-            appendWithTtsSpan(builder, "+", new TtsSpan.VerbatimBuilder("+").build());
+        final String gmtPattern = tzFormatter.getGMTPattern();
+        final int placeholderIndex = gmtPattern.indexOf("{0}");
+        final String gmtPatternPrefix, gmtPatternSuffix;
+        if (placeholderIndex == -1) {
+            // Bad pattern. Replace with defaults.
+            gmtPatternPrefix = "GMT";
+            gmtPatternSuffix = "";
+        } else {
+            gmtPatternPrefix = gmtPattern.substring(0, placeholderIndex);
+            gmtPatternSuffix = gmtPattern.substring(placeholderIndex + 3); // After the "{0}".
         }
 
+        if (!gmtPatternPrefix.isEmpty()) {
+            appendWithTtsSpan(builder, gmtPatternPrefix,
+                    new TtsSpan.TextBuilder(gmtPatternPrefix).build());
+        }
+
+        int offsetMillis = tz.getOffset(now.getTime());
+        final boolean negative = offsetMillis < 0;
+        final TimeZoneFormat.GMTOffsetPatternType patternType;
+        if (negative) {
+            offsetMillis = -offsetMillis;
+            patternType = TimeZoneFormat.GMTOffsetPatternType.NEGATIVE_HM;
+        } else {
+            patternType = TimeZoneFormat.GMTOffsetPatternType.POSITIVE_HM;
+        }
+        final String gmtOffsetPattern = tzFormatter.getGMTOffsetPattern(patternType);
+        final String localizedDigits = tzFormatter.getGMTOffsetDigits();
+
         final int offsetHours = (int) (offsetMillis / DateUtils.HOUR_IN_MILLIS);
-        appendWithTtsSpan(builder, twoDigits(offsetHours),
-                new TtsSpan.MeasureBuilder().setNumber(offsetHours).setUnit("hour").build());
-
-        builder.append(":");
-
         final int offsetMinutes = (int) (offsetMillis / DateUtils.MINUTE_IN_MILLIS);
         final int offsetMinutesRemaining = Math.abs(offsetMinutes) % 60;
-        appendWithTtsSpan(builder, twoDigits(offsetMinutesRemaining),
-                new TtsSpan.MeasureBuilder().setNumber(offsetMinutesRemaining)
-                        .setUnit("minute").build());
+
+        for (int i = 0; i < gmtOffsetPattern.length(); i++) {
+            char c = gmtOffsetPattern.charAt(i);
+            if (c == '+' || c == '-' || c == '\u2212' /* MINUS SIGN */) {
+                final String sign = String.valueOf(c);
+                appendWithTtsSpan(builder, sign, new TtsSpan.VerbatimBuilder(sign).build());
+            } else if (c == 'H' || c == 'm') {
+                final int numDigits;
+                if (i + 1 < gmtOffsetPattern.length() && gmtOffsetPattern.charAt(i + 1) == c) {
+                    numDigits = 2;
+                    i++; // Skip the next formatting character.
+                } else {
+                    numDigits = 1;
+                }
+                final int number;
+                final String unit;
+                if (c == 'H') {
+                    number = offsetHours;
+                    unit = "hour";
+                } else { // c == 'm'
+                    number = offsetMinutesRemaining;
+                    unit = "minute";
+                }
+                appendWithTtsSpan(builder, formatDigits(number, numDigits, localizedDigits),
+                        new TtsSpan.MeasureBuilder().setNumber(number).setUnit(unit).build());
+            } else {
+                builder.append(c);
+            }
+        }
+
+        if (!gmtPatternSuffix.isEmpty()) {
+            appendWithTtsSpan(builder, gmtPatternSuffix,
+                    new TtsSpan.TextBuilder(gmtPatternSuffix).build());
+        }
 
         CharSequence gmtText = new SpannableString(builder);
 
@@ -305,7 +357,8 @@
         public final int zoneCount;
 
         public ZoneGetterData(Context context) {
-            final Locale locale = Locale.getDefault();
+            final Locale locale = context.getResources().getConfiguration().locale;
+            final TimeZoneFormat tzFormatter = TimeZoneFormat.getInstance(locale);
             final Date now = new Date();
             final List<String> olsonIdsToDisplayList = readTimezonesToDisplay(context);
 
@@ -319,7 +372,7 @@
                 olsonIdsToDisplay[i] = olsonId;
                 final TimeZone tz = TimeZone.getTimeZone(olsonId);
                 timeZones[i] = tz;
-                gmtOffsetTexts[i] = getGmtOffsetText(context, locale, tz, now);
+                gmtOffsetTexts[i] = getGmtOffsetText(tzFormatter, locale, tz, now);
             }
 
             // Create a lookup of local zone IDs.
diff --git a/packages/SettingsLib/src/com/android/settingslib/development/AbstractEnableAdbPreferenceController.java b/packages/SettingsLib/src/com/android/settingslib/development/AbstractEnableAdbPreferenceController.java
new file mode 100644
index 0000000..75b6696
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/development/AbstractEnableAdbPreferenceController.java
@@ -0,0 +1,116 @@
+/*
+ * 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.settingslib.development;
+
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.Intent;
+import android.os.UserManager;
+import android.provider.Settings;
+import android.support.v14.preference.SwitchPreference;
+import android.support.v4.content.LocalBroadcastManager;
+import android.support.v7.preference.Preference;
+import android.support.v7.preference.PreferenceScreen;
+import android.support.v7.preference.TwoStatePreference;
+import android.text.TextUtils;
+
+import com.android.settingslib.core.AbstractPreferenceController;
+
+public abstract class AbstractEnableAdbPreferenceController extends AbstractPreferenceController {
+    private static final String KEY_ENABLE_ADB = "enable_adb";
+    public static final String ACTION_ENABLE_ADB_STATE_CHANGED =
+            "com.android.settingslib.development.AbstractEnableAdbController."
+                    + "ENABLE_ADB_STATE_CHANGED";
+
+    private SwitchPreference mPreference;
+
+    public AbstractEnableAdbPreferenceController(Context context) {
+        super(context);
+    }
+
+    @Override
+    public void displayPreference(PreferenceScreen screen) {
+        super.displayPreference(screen);
+        if (isAvailable()) {
+            mPreference = (SwitchPreference) screen.findPreference(KEY_ENABLE_ADB);
+        }
+    }
+
+    @Override
+    public boolean isAvailable() {
+        return mContext.getSystemService(UserManager.class).isAdminUser();
+    }
+
+    @Override
+    public String getPreferenceKey() {
+        return KEY_ENABLE_ADB;
+    }
+
+    private boolean isAdbEnabled() {
+        final ContentResolver cr = mContext.getContentResolver();
+        return Settings.Global.getInt(cr, Settings.Global.ADB_ENABLED, 0) != 0;
+    }
+
+    @Override
+    public void updateState(Preference preference) {
+        ((TwoStatePreference)preference).setChecked(isAdbEnabled());
+    }
+
+    public void enablePreference(boolean enabled) {
+        if (isAvailable()) {
+            mPreference.setEnabled(enabled);
+        }
+    }
+
+    public void resetPreference() {
+        if (mPreference.isChecked()) {
+            mPreference.setChecked(false);
+            handlePreferenceTreeClick(mPreference);
+        }
+    }
+
+    public boolean haveDebugSettings() {
+        return isAdbEnabled();
+    }
+
+    @Override
+    public boolean handlePreferenceTreeClick(Preference preference) {
+        if (TextUtils.equals(KEY_ENABLE_ADB, preference.getKey())) {
+            if (!isAdbEnabled()) {
+                showConfirmationDialog((SwitchPreference) preference);
+            } else {
+                writeAdbSetting(false);
+            }
+            return true;
+        } else {
+            return false;
+        }
+    }
+
+    protected void writeAdbSetting(boolean enabled) {
+        Settings.Global.putInt(mContext.getContentResolver(),
+                Settings.Global.ADB_ENABLED, enabled ? 1 : 0);
+        notifyStateChanged();
+    }
+
+    protected void notifyStateChanged() {
+        LocalBroadcastManager.getInstance(mContext)
+                .sendBroadcast(new Intent(ACTION_ENABLE_ADB_STATE_CHANGED));
+    }
+
+    public abstract void showConfirmationDialog(SwitchPreference preference);
+}
diff --git a/packages/SettingsLib/src/com/android/settingslib/net/DataUsageController.java b/packages/SettingsLib/src/com/android/settingslib/net/DataUsageController.java
index b69232c..ed3696c 100644
--- a/packages/SettingsLib/src/com/android/settingslib/net/DataUsageController.java
+++ b/packages/SettingsLib/src/com/android/settingslib/net/DataUsageController.java
@@ -16,6 +16,14 @@
 
 package com.android.settingslib.net;
 
+import static android.net.ConnectivityManager.TYPE_MOBILE;
+import static android.net.NetworkStatsHistory.FIELD_RX_BYTES;
+import static android.net.NetworkStatsHistory.FIELD_TX_BYTES;
+import static android.net.TrafficStats.MB_IN_BYTES;
+import static android.telephony.TelephonyManager.SIM_STATE_READY;
+import static android.text.format.DateUtils.FORMAT_ABBREV_MONTH;
+import static android.text.format.DateUtils.FORMAT_SHOW_DATE;
+
 import android.content.Context;
 import android.net.ConnectivityManager;
 import android.net.INetworkStatsService;
@@ -29,22 +37,15 @@
 import android.telephony.SubscriptionManager;
 import android.telephony.TelephonyManager;
 import android.text.format.DateUtils;
-import android.text.format.Time;
 import android.util.Log;
+import android.util.Pair;
 
 import com.android.internal.R;
 
+import java.time.ZonedDateTime;
 import java.util.Date;
 import java.util.Locale;
 
-import static android.net.ConnectivityManager.TYPE_MOBILE;
-import static android.net.NetworkStatsHistory.FIELD_RX_BYTES;
-import static android.net.NetworkStatsHistory.FIELD_TX_BYTES;
-import static android.telephony.TelephonyManager.SIM_STATE_READY;
-import static android.text.format.DateUtils.FORMAT_ABBREV_MONTH;
-import static android.text.format.DateUtils.FORMAT_SHOW_DATE;
-import static android.net.TrafficStats.MB_IN_BYTES;
-
 public class DataUsageController {
 
     private static final String TAG = "DataUsageController";
@@ -107,13 +108,6 @@
         return null;
     }
 
-    private static Time addMonth(Time t, int months) {
-        final Time rt = new Time(t);
-        rt.set(t.monthDay, t.month + months, t.year);
-        rt.normalize(false);
-        return rt;
-    }
-
     public DataUsageInfo getDataUsageInfo() {
         final String subscriberId = getActiveSubscriberId(mContext);
         if (subscriberId == null) {
@@ -140,22 +134,11 @@
             final NetworkStatsHistory history = session.getHistoryForNetwork(template, FIELDS);
             final long now = System.currentTimeMillis();
             final long start, end;
-            if (policy != null && policy.cycleDay > 0) {
-                // period = determined from cycleDay
-                if (DEBUG) Log.d(TAG, "Cycle day=" + policy.cycleDay + " tz="
-                        + policy.cycleTimezone);
-                final Time nowTime = new Time(policy.cycleTimezone);
-                nowTime.setToNow();
-                final Time policyTime = new Time(nowTime);
-                policyTime.set(policy.cycleDay, policyTime.month, policyTime.year);
-                policyTime.normalize(false);
-                if (nowTime.after(policyTime)) {
-                    start = policyTime.toMillis(false);
-                    end = addMonth(policyTime, 1).toMillis(false);
-                } else {
-                    start = addMonth(policyTime, -1).toMillis(false);
-                    end = policyTime.toMillis(false);
-                }
+            if (policy != null) {
+                final Pair<ZonedDateTime, ZonedDateTime> cycle = NetworkPolicyManager
+                        .cycleIterator(policy).next();
+                start = cycle.first.toInstant().toEpochMilli();
+                end = cycle.second.toInstant().toEpochMilli();
             } else {
                 // period = last 4 wks
                 end = now;
diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java b/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java
index ec41415..e0a88e4 100644
--- a/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java
+++ b/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java
@@ -552,15 +552,11 @@
     }
 
     /**
-     * Returns if the network is marked metered. Metering can be marked through its config in
-     * {@link WifiConfiguration}, after connection in {@link WifiInfo}, or from a score config in
-     * {@link ScoredNetwork}.
+     * Returns if the network should be considered metered.
      */
     public boolean isMetered() {
         return mIsScoredNetworkMetered
-                || (mConfig != null && mConfig.meteredHint)
-                || (mInfo != null && mInfo.getMeteredHint()
-                || (mNetworkInfo != null && mNetworkInfo.isMetered()));
+                || WifiConfiguration.isMetered(mConfig, mInfo);
     }
 
     public NetworkInfo getNetworkInfo() {
@@ -729,7 +725,7 @@
             }
         }
 
-        if (WifiTracker.sVerboseLogging > 0) {
+        if (WifiTracker.sVerboseLogging) {
             // Add RSSI/band information for this config, what was seen up to 6 seconds ago
             // verbose WiFi Logging is only turned on thru developers settings
             if (mInfo != null && mNetworkInfo != null) { // This is the active connection
@@ -1170,8 +1166,9 @@
 
             if (nc != null) {
                 if (nc.hasCapability(nc.NET_CAPABILITY_CAPTIVE_PORTAL)) {
-                    return context.getString(
-                        com.android.internal.R.string.network_available_sign_in);
+                    int id = context.getResources()
+                            .getIdentifier("network_available_sign_in", "string", "android");
+                    return context.getString(id);
                 } else if (!nc.hasCapability(NetworkCapabilities.NET_CAPABILITY_VALIDATED)) {
                     return context.getString(R.string.wifi_connected_no_internet);
                 }
diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java
index 9ccd332..f0ac31f 100644
--- a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java
+++ b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java
@@ -68,11 +68,13 @@
     // TODO(b/36733768): Remove flag includeSaved and includePasspoints.
 
     private static final String TAG = "WifiTracker";
-    private static final boolean DBG = Log.isLoggable(TAG, Log.DEBUG);
+    private static final boolean DBG() {
+        return Log.isLoggable(TAG, Log.DEBUG);
+    }
 
     /** verbose logging flag. this flag is set thru developer debugging options
      * and used so as to assist with in-the-field WiFi connectivity debugging  */
-    public static int sVerboseLogging = 0;
+    public static boolean sVerboseLogging;
 
     // TODO: Allow control of this?
     // Combo scans can take 5-6s to complete - set to 10s.
@@ -193,7 +195,7 @@
         mConnectivityManager = connectivityManager;
 
         // check if verbose logging has been turned on or off
-        sVerboseLogging = mWifiManager.getVerboseLoggingLevel();
+        sVerboseLogging = (mWifiManager.getVerboseLoggingLevel() > 0);
 
         mFilter = new IntentFilter();
         mFilter.addAction(WifiManager.WIFI_STATE_CHANGED_ACTION);
@@ -251,16 +253,12 @@
             List<WifiConfiguration> configs = mWifiManager.getConfiguredNetworks();
             updateAccessPointsLocked(newScanResults, configs);
 
-            if (DBG) {
-                Log.d(TAG, "force update - internal access point list:\n" + mInternalAccessPoints);
-            }
-
             // Synchronously copy access points
             mMainHandler.removeMessages(MainHandler.MSG_ACCESS_POINT_CHANGED);
             mMainHandler.handleMessage(
                     Message.obtain(mMainHandler, MainHandler.MSG_ACCESS_POINT_CHANGED));
-            if (DBG) {
-                Log.d(TAG, "force update - external access point list:\n" + mAccessPoints);
+            if (sVerboseLogging) {
+                Log.i(TAG, "force update - external access point list:\n" + mAccessPoints);
             }
         }
     }
@@ -338,7 +336,7 @@
     private void requestScoresForNetworkKeys(Collection<NetworkKey> keys) {
         if (keys.isEmpty()) return;
 
-        if (DBG) {
+        if (DBG()) {
             Log.d(TAG, "Requesting scores for Network Keys: " + keys);
         }
         mNetworkScoreManager.requestScores(keys.toArray(new NetworkKey[keys.size()]));
@@ -365,7 +363,7 @@
                 mConnectivityManager.unregisterNetworkCallback(mNetworkCallback);
                 mRegistered = false;
             }
-            unregisterAndClearScoreCache();
+            unregisterScoreCache();
             pauseScanning();
             mContext.getContentResolver().unregisterContentObserver(mObserver);
 
@@ -375,11 +373,14 @@
         mStaleScanResults = true;
     }
 
-    private void unregisterAndClearScoreCache() {
+    private void unregisterScoreCache() {
         mNetworkScoreManager.unregisterNetworkScoreCache(NetworkKey.TYPE_WIFI, mScoreCache);
-        mScoreCache.clearScores();
 
-        // Synchronize on mLock to avoid concurrent modification during updateAccessPoints
+        // We do not want to clear the existing scores in the cache, as this method is called during
+        // stop tracking on activity pause. Hence, on resumption we want the ability to show the
+        // last known, potentially stale, scores. However, by clearing requested scores, the scores
+        // will be requested again upon resumption of tracking, and if any changes have occurred
+        // the listeners (UI) will be updated accordingly.
         synchronized (mLock) {
             mRequestedScores.clear();
         }
@@ -440,19 +441,19 @@
         }
 
         if (mScanId > NUM_SCANS_TO_CONFIRM_AP_LOSS) {
-            if (DBG) Log.d(TAG, "------ Dumping SSIDs that were expired on this scan ------");
+            if (DBG()) Log.d(TAG, "------ Dumping SSIDs that were expired on this scan ------");
             Integer threshold = mScanId - NUM_SCANS_TO_CONFIRM_AP_LOSS;
             for (Iterator<Map.Entry<String, Integer>> it = mSeenBssids.entrySet().iterator();
                     it.hasNext(); /* nothing */) {
                 Map.Entry<String, Integer> e = it.next();
                 if (e.getValue() < threshold) {
                     ScanResult result = mScanResultCache.get(e.getKey());
-                    if (DBG) Log.d(TAG, "Removing " + e.getKey() + ":(" + result.SSID + ")");
+                    if (DBG()) Log.d(TAG, "Removing " + e.getKey() + ":(" + result.SSID + ")");
                     mScanResultCache.remove(e.getKey());
                     it.remove();
                 }
             }
-            if (DBG) Log.d(TAG, "---- Done Dumping SSIDs that were expired on this scan ----");
+            if (DBG()) Log.d(TAG, "---- Done Dumping SSIDs that were expired on this scan ----");
         }
 
         return mScanResultCache.values();
@@ -606,7 +607,7 @@
         Collections.sort(accessPoints);
 
         // Log accesspoints that were deleted
-        if (DBG) {
+        if (DBG()) {
             Log.d(TAG, "------ Dumping SSIDs that were not seen on this scan ------");
             for (AccessPoint prevAccessPoint : mInternalAccessPoints) {
                 if (prevAccessPoint.getSsid() == null)
@@ -1061,7 +1062,7 @@
             oldAccessPoints.put(accessPoint.mId, accessPoint);
         }
 
-        if (DBG) {
+        if (DBG()) {
             Log.d(TAG, "Starting to copy AP items on the MainHandler");
         }
         synchronized (mLock) {
diff --git a/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/AccessPointTest.java b/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/AccessPointTest.java
index c08dd6e..aed0270 100644
--- a/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/AccessPointTest.java
+++ b/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/AccessPointTest.java
@@ -298,23 +298,6 @@
     }
 
     @Test
-    public void testIsMetered_returnTrueWhenNetworkInfoIsMetered() {
-        WifiConfiguration configuration = createWifiConfiguration();
-
-        NetworkInfo networkInfo =
-                new NetworkInfo(ConnectivityManager.TYPE_WIFI, 2, "WIFI", "WIFI_SUBTYPE");
-        networkInfo.setMetered(true);
-        AccessPoint accessPoint = new AccessPoint(mContext, configuration);
-        WifiInfo wifiInfo = new WifiInfo();
-        wifiInfo.setSSID(WifiSsid.createFromAsciiEncoded(configuration.SSID));
-        wifiInfo.setBSSID(configuration.BSSID);
-        wifiInfo.setNetworkId(configuration.networkId);
-        accessPoint.update(configuration, wifiInfo, networkInfo);
-
-        assertThat(accessPoint.isMetered()).isTrue();
-    }
-
-    @Test
     public void testIsMetered_returnTrueWhenScoredNetworkIsMetered() {
         AccessPoint ap = createAccessPointWithScanResultCache();
 
diff --git a/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/WifiTrackerTest.java b/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/WifiTrackerTest.java
index b6d0c45..c87d01a 100644
--- a/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/WifiTrackerTest.java
+++ b/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/WifiTrackerTest.java
@@ -473,6 +473,17 @@
     }
 
     @Test
+    public void stopTracking_shouldNotClearExistingScores()
+            throws InterruptedException {
+        // Start the tracker and inject the initial scan results and then stop tracking
+        WifiTracker tracker =  createTrackerWithImmediateBroadcastsAndInjectInitialScanResults();
+        updateScoresAndWaitForAccessPointsChangedCallback(tracker);
+        tracker.stopTracking();
+
+        assertThat(mScoreCacheCaptor.getValue().getScoredNetwork(NETWORK_KEY_1)).isNotNull();
+    }
+
+    @Test
     public void scoreCacheUpdateScoresShouldTriggerOnAccessPointsChanged()
             throws InterruptedException {
         WifiTracker tracker = createMockedWifiTracker();
diff --git a/packages/SettingsLib/tests/robotests/Android.mk b/packages/SettingsLib/tests/robotests/Android.mk
index eca2052..55b635e 100644
--- a/packages/SettingsLib/tests/robotests/Android.mk
+++ b/packages/SettingsLib/tests/robotests/Android.mk
@@ -41,7 +41,7 @@
 
 # Include the testing libraries (JUnit4 + Robolectric libs).
 LOCAL_STATIC_JAVA_LIBRARIES := \
-    platform-system-robolectric \
+    mockito-robolectric-prebuilt \
     truth-prebuilt
 
 LOCAL_JAVA_LIBRARIES := \
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/development/EnableAdbPreferenceControllerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/development/EnableAdbPreferenceControllerTest.java
new file mode 100644
index 0000000..0778b27
--- /dev/null
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/development/EnableAdbPreferenceControllerTest.java
@@ -0,0 +1,158 @@
+/*
+ * 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.settingslib.development;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Answers.RETURNS_DEEP_STUBS;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.os.UserManager;
+import android.provider.Settings;
+import android.support.v14.preference.SwitchPreference;
+import android.support.v7.preference.Preference;
+import android.support.v7.preference.PreferenceScreen;
+
+import com.android.settingslib.SettingLibRobolectricTestRunner;
+import com.android.settingslib.TestConfig;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.annotation.Config;
+import org.robolectric.shadows.ShadowApplication;
+
+@RunWith(SettingLibRobolectricTestRunner.class)
+@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
+public class EnableAdbPreferenceControllerTest {
+    @Mock(answer = RETURNS_DEEP_STUBS)
+    private PreferenceScreen mScreen;
+    @Mock
+    private UserManager mUserManager;
+    @Mock
+    private PackageManager mPackageManager;
+
+    private Context mContext;
+    private SwitchPreference mPreference;
+    private ConcreteEnableAdbPreferenceController mController;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+        ShadowApplication shadowContext = ShadowApplication.getInstance();
+        shadowContext.setSystemService(Context.USER_SERVICE, mUserManager);
+        mContext = spy(shadowContext.getApplicationContext());
+        when(mContext.getPackageManager()).thenReturn(mPackageManager);
+        mPreference = new SwitchPreference(mContext);
+        when(mScreen.findPreference(anyString())).thenReturn(mPreference);
+        mController = new ConcreteEnableAdbPreferenceController(mContext);
+        mPreference.setKey(mController.getPreferenceKey());
+    }
+
+    @Test
+    public void displayPreference_isNotAdmin_shouldRemovePreference() {
+        when(mUserManager.isAdminUser()).thenReturn(false);
+        when(mScreen.getPreferenceCount()).thenReturn(1);
+        when(mScreen.getPreference(0)).thenReturn(mPreference);
+
+        mController.displayPreference(mScreen);
+
+        verify(mScreen).removePreference(any(Preference.class));
+    }
+
+    @Test
+    public void displayPreference_isAdmin_shouldNotRemovePreference() {
+        when(mUserManager.isAdminUser()).thenReturn(true);
+
+        mController.displayPreference(mScreen);
+
+        verify(mScreen, never()).removePreference(any(Preference.class));
+    }
+
+
+    @Test
+    public void resetPreference_shouldUncheck() {
+        when(mUserManager.isAdminUser()).thenReturn(true);
+        mController.displayPreference(mScreen);
+        mPreference.setChecked(true);
+
+        mController.resetPreference();
+
+        assertThat(mPreference.isChecked()).isFalse();
+    }
+
+    @Test
+    public void handlePreferenceTreeClick_shouldUpdateSettings() {
+        when(mUserManager.isAdminUser()).thenReturn(true);
+        Settings.Secure.putInt(mContext.getContentResolver(),
+                Settings.Global.ADB_ENABLED, 1);
+        mPreference.setChecked(true);
+        mController.displayPreference(mScreen);
+
+        mController.handlePreferenceTreeClick(mPreference);
+
+        assertThat(Settings.Secure.getInt(mContext.getContentResolver(),
+                Settings.Global.ADB_ENABLED, 0)).isEqualTo(0);
+    }
+
+    @Test
+    public void updateState_settingsOn_shouldCheck() {
+        when(mUserManager.isAdminUser()).thenReturn(true);
+        Settings.Secure.putInt(mContext.getContentResolver(),
+                Settings.Global.ADB_ENABLED, 1);
+        mPreference.setChecked(false);
+        mController.displayPreference(mScreen);
+
+        mController.updateState(mPreference);
+
+        assertThat(mPreference.isChecked()).isTrue();
+    }
+
+    @Test
+    public void updateState_settingsOff_shouldUncheck() {
+        when(mUserManager.isAdminUser()).thenReturn(true);
+        Settings.Secure.putInt(mContext.getContentResolver(),
+                Settings.Global.ADB_ENABLED, 0);
+        mPreference.setChecked(true);
+        mController.displayPreference(mScreen);
+
+        mController.updateState(mPreference);
+
+        assertThat(mPreference.isChecked()).isFalse();
+    }
+
+    class ConcreteEnableAdbPreferenceController extends AbstractEnableAdbPreferenceController {
+        public ConcreteEnableAdbPreferenceController(Context context) {
+            super(context);
+        }
+
+        @Override
+        public void showConfirmationDialog(SwitchPreference preference) {
+            // Don't show a dialog, just set setting.
+            writeAdbSetting(true);
+        }
+    }
+}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/TileUtilsTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/TileUtilsTest.java
index 0364418..e9ca753 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/TileUtilsTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/TileUtilsTest.java
@@ -18,11 +18,12 @@
 
 import static com.google.common.truth.Truth.assertThat;
 
-import static org.mockito.Matchers.any;
-import static org.mockito.Matchers.anyInt;
-import static org.mockito.Matchers.anyString;
-import static org.mockito.Matchers.argThat;
-import static org.mockito.Matchers.eq;
+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;
+import static org.mockito.ArgumentMatchers.isNull;
 import static org.mockito.Mockito.atLeastOnce;
 import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.verify;
@@ -59,7 +60,6 @@
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.ArgumentCaptor;
-import org.mockito.ArgumentMatcher;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 import org.robolectric.RobolectricTestRunner;
@@ -100,6 +100,7 @@
         MockitoAnnotations.initMocks(this);
         when(mContext.getPackageManager()).thenReturn(mPackageManager);
         when(mPackageManager.getResourcesForApplication(anyString())).thenReturn(mResources);
+        when(mPackageManager.getResourcesForApplication((String) isNull())).thenReturn(mResources);
         when(mPackageManager.getApplicationInfo(eq("abc"), anyInt()))
                 .thenReturn(application.getApplicationInfo());
         mContentResolver = spy(application.getContentResolver());
@@ -115,7 +116,6 @@
         List<Tile> outTiles = new ArrayList<>();
         List<ResolveInfo> info = new ArrayList<>();
         info.add(newInfo(true, testCategory));
-        Map<Pair<String, String>, Tile> cache = new ArrayMap<>();
         when(mPackageManager.queryIntentActivitiesAsUser(eq(intent), anyInt(), anyInt()))
                 .thenReturn(info);
 
@@ -169,7 +169,6 @@
 
     @Test
     public void getTilesForIntent_shouldSkipFilteredApps() {
-        final String testCategory = "category1";
         Intent intent = new Intent();
         Map<Pair<String, String>, Tile> addedCache = new ArrayMap<>();
         List<Tile> outTiles = new ArrayList<>();
@@ -210,11 +209,9 @@
         userHandleList.add(UserHandle.CURRENT);
         when(mUserManager.getUserProfiles()).thenReturn(userHandleList);
 
-        when(mPackageManager.queryIntentActivitiesAsUser(argThat(new ArgumentMatcher<Intent>() {
-            public boolean matches(Object event) {
-                return testAction.equals(((Intent) event).getAction());
-            }
-        }), anyInt(), anyInt())).thenReturn(info);
+        when(mPackageManager.queryIntentActivitiesAsUser(argThat(
+                event -> testAction.equals(event.getAction())), anyInt(), anyInt()))
+                .thenReturn(info);
 
         List<DashboardCategory> categoryList = TileUtils.getCategories(
                 mContext, cache, false /* categoryDefinedInManifest */, testAction,
diff --git a/packages/SettingsProvider/res/values/defaults.xml b/packages/SettingsProvider/res/values/defaults.xml
index e9dadc5..4007cc9 100644
--- a/packages/SettingsProvider/res/values/defaults.xml
+++ b/packages/SettingsProvider/res/values/defaults.xml
@@ -187,4 +187,7 @@
 
     <!--  default setting for Settings.System.END_BUTTON_BEHAVIOR : END_BUTTON_BEHAVIOR_SLEEP -->
     <integer name="def_end_button_behavior">0x2</integer>
+
+    <!-- default setting for Settings.Global.DEFAULT_RESTRICT_BACKGROUND_DATA -->
+    <bool name="def_restrict_background_data">false</bool>
 </resources>
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
index 1bd58ad..15e6e45 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
@@ -32,8 +32,6 @@
 import android.content.pm.IPackageManager;
 import android.content.pm.PackageInfo;
 import android.content.pm.PackageManager;
-import android.content.pm.ResolveInfo;
-import android.content.pm.ServiceInfo;
 import android.content.pm.UserInfo;
 import android.content.res.Resources;
 import android.database.Cursor;
@@ -61,7 +59,7 @@
 import android.os.UserManager;
 import android.os.UserManagerInternal;
 import android.provider.Settings;
-import android.service.notification.NotificationListenerService;
+import android.provider.Settings.Global;
 import android.text.TextUtils;
 import android.util.ArrayMap;
 import android.util.ArraySet;
@@ -82,7 +80,6 @@
 import java.io.FileDescriptor;
 import java.io.FileNotFoundException;
 import java.io.PrintWriter;
-import java.nio.charset.StandardCharsets;
 import java.nio.ByteBuffer;
 import java.security.InvalidKeyException;
 import java.security.NoSuchAlgorithmException;
@@ -2895,7 +2892,7 @@
         }
 
         private final class UpgradeController {
-            private static final int SETTINGS_VERSION = 146;
+            private static final int SETTINGS_VERSION = 147;
 
             private final int mUserId;
 
@@ -3422,6 +3419,23 @@
                     currentVersion = 146;
                 }
 
+                if (currentVersion == 146) {
+                    // Version 146: Set the default value for DEFAULT_RESTRICT_BACKGROUND_DATA.
+                    if (userId == UserHandle.USER_SYSTEM) {
+                        final SettingsState globalSettings = getGlobalSettingsLocked();
+                        final Setting currentSetting = globalSettings.getSettingLocked(
+                                Global.DEFAULT_RESTRICT_BACKGROUND_DATA);
+                        if (currentSetting.isNull()) {
+                            globalSettings.insertSettingLocked(
+                                    Global.DEFAULT_RESTRICT_BACKGROUND_DATA,
+                                    getContext().getResources().getBoolean(
+                                            R.bool.def_restrict_background_data) ? "1" : "0",
+                                    null, true, SettingsState.SYSTEM_PACKAGE_NAME);
+                        }
+                    }
+                    currentVersion = 147;
+                }
+
                 // vXXX: Add new settings above this point.
 
                 if (currentVersion != newVersion) {
diff --git a/packages/Shell/src/com/android/shell/BugreportProgressService.java b/packages/Shell/src/com/android/shell/BugreportProgressService.java
index 988b986..a8b184c 100644
--- a/packages/Shell/src/com/android/shell/BugreportProgressService.java
+++ b/packages/Shell/src/com/android/shell/BugreportProgressService.java
@@ -1060,7 +1060,6 @@
         }
         return new Notification.Builder(context, NOTIFICATION_CHANNEL_ID)
                 .addExtras(sNotificationBundle)
-                .setCategory(Notification.CATEGORY_SYSTEM)
                 .setSmallIcon(
                         isTv(context) ? R.drawable.ic_bug_report_black_24dp
                                 : com.android.internal.R.drawable.stat_sys_adb)
diff --git a/packages/SystemUI/res-keyguard/drawable/ic_access_alarms_big.xml b/packages/SystemUI/res-keyguard/drawable/ic_access_alarms_big.xml
index fd385fc..fffa0bd 100644
--- a/packages/SystemUI/res-keyguard/drawable/ic_access_alarms_big.xml
+++ b/packages/SystemUI/res-keyguard/drawable/ic_access_alarms_big.xml
@@ -19,6 +19,6 @@
     android:viewportWidth="24.0"
     android:viewportHeight="24.0">
     <path
-        android:fillColor="?attr/bgProtectSecondaryTextColor"
+        android:fillColor="@android:color/white"
         android:pathData="M21.35,6.49c-0.35,0.42 -0.98,0.47 -1.4,0.12l-3.07,-2.57a1,1 0,1 1,1.29 -1.53l3.07,2.57c0.42,0.35 0.47,0.98 0.11,1.41zM7.24,2.63a1,1 0,0 0,-1.41 -0.13L2.77,5.07A0.996,0.996 0,1 0,4.05 6.6l3.06,-2.57c0.43,-0.35 0.48,-0.98 0.13,-1.4zM11.75,8c-0.41,0 -0.75,0.34 -0.75,0.75v4.68c0,0.35 0.18,0.68 0.49,0.86l3.65,2.19c0.34,0.2 0.78,0.1 0.98,-0.24a0.71,0.71 0,0 0,-0.25 -0.99l-3.37,-2v-4.5c0,-0.41 -0.34,-0.75 -0.75,-0.75zM12,5.9c-3.91,0 -7.1,3.18 -7.1,7.1s3.19,7.1 7.1,7.1 7.1,-3.18 7.1,-7.1 -3.19,-7.1 -7.1,-7.1M12,4a9,9 0,1 1,-0.001 18.001A9,9 0,0 1,12 4z" />
 </vector>
diff --git a/packages/SystemUI/res-keyguard/layout/keyguard_password_view.xml b/packages/SystemUI/res-keyguard/layout/keyguard_password_view.xml
index 26a179a..b821e7e 100644
--- a/packages/SystemUI/res-keyguard/layout/keyguard_password_view.xml
+++ b/packages/SystemUI/res-keyguard/layout/keyguard_password_view.xml
@@ -54,7 +54,7 @@
              android:textStyle="normal"
              android:inputType="textPassword"
              android:textSize="16sp"
-             android:textColor="?attr/bgProtectTextColor"
+             android:textColor="?attr/wallpaperTextColor"
              android:textAppearance="?android:attr/textAppearanceMedium"
              android:imeOptions="flagForceAscii|actionDone"
              android:maxLength="500"
diff --git a/packages/SystemUI/res-keyguard/layout/keyguard_pin_view.xml b/packages/SystemUI/res-keyguard/layout/keyguard_pin_view.xml
index 501d0a5..3283e04 100644
--- a/packages/SystemUI/res-keyguard/layout/keyguard_pin_view.xml
+++ b/packages/SystemUI/res-keyguard/layout/keyguard_pin_view.xml
@@ -54,7 +54,7 @@
                     android:layout_centerHorizontal="true"
                     android:layout_marginRight="72dp"
                     androidprv:scaledTextSize="@integer/scaled_password_text_size"
-                    android:textColor="?attr/bgProtectTextColor"
+                    android:textColor="?attr/wallpaperTextColor"
                     android:contentDescription="@string/keyguard_accessibility_pin_area"
                     />
             <ImageButton
diff --git a/packages/SystemUI/res-keyguard/layout/keyguard_presentation.xml b/packages/SystemUI/res-keyguard/layout/keyguard_presentation.xml
index 87b1ee7..6dea493 100644
--- a/packages/SystemUI/res-keyguard/layout/keyguard_presentation.xml
+++ b/packages/SystemUI/res-keyguard/layout/keyguard_presentation.xml
@@ -41,7 +41,7 @@
                 android:layout_width="wrap_content"
                 android:layout_height="wrap_content"
                 android:layout_gravity="center_horizontal|top"
-                android:textColor="?attr/bgProtectTextColor"
+                android:textColor="?attr/wallpaperTextColor"
                 android:singleLine="true"
                 style="@style/widget_big_thin"
                 android:format12Hour="@string/keyguard_widget_12_hours_format"
diff --git a/packages/SystemUI/res-keyguard/layout/keyguard_sim_pin_view.xml b/packages/SystemUI/res-keyguard/layout/keyguard_sim_pin_view.xml
index c4732e4..cf87f90 100644
--- a/packages/SystemUI/res-keyguard/layout/keyguard_sim_pin_view.xml
+++ b/packages/SystemUI/res-keyguard/layout/keyguard_sim_pin_view.xml
@@ -67,7 +67,7 @@
                     android:layout_centerHorizontal="true"
                     android:layout_marginRight="72dp"
                     androidprv:scaledTextSize="@integer/scaled_password_text_size"
-                    android:textColor="?attr/bgProtectTextColor"
+                    android:textColor="?attr/wallpaperTextColor"
                     android:contentDescription="@string/keyguard_accessibility_sim_pin_area"
                     />
             <ImageButton
diff --git a/packages/SystemUI/res-keyguard/layout/keyguard_sim_puk_view.xml b/packages/SystemUI/res-keyguard/layout/keyguard_sim_puk_view.xml
index 1c7defd..3cae493 100644
--- a/packages/SystemUI/res-keyguard/layout/keyguard_sim_puk_view.xml
+++ b/packages/SystemUI/res-keyguard/layout/keyguard_sim_puk_view.xml
@@ -68,7 +68,7 @@
                     android:layout_centerHorizontal="true"
                     android:layout_marginRight="72dp"
                     androidprv:scaledTextSize="@integer/scaled_password_text_size"
-                    android:textColor="?attr/bgProtectTextColor"
+                    android:textColor="?attr/wallpaperTextColor"
                     android:contentDescription="@string/keyguard_accessibility_sim_puk_area"
                     />
             <ImageButton
diff --git a/packages/SystemUI/res-keyguard/layout/keyguard_status_area.xml b/packages/SystemUI/res-keyguard/layout/keyguard_status_area.xml
index a0850f4..f5efcaa 100644
--- a/packages/SystemUI/res-keyguard/layout/keyguard_status_area.xml
+++ b/packages/SystemUI/res-keyguard/layout/keyguard_status_area.xml
@@ -29,7 +29,7 @@
         android:id="@+id/date_view"
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
-        android:textColor="?attr/bgProtectTextColor"
+        android:textColor="?attr/wallpaperTextColor"
         style="@style/widget_label"
         android:letterSpacing="0.15"
         android:gravity="center"
@@ -39,7 +39,9 @@
         android:layout_height="wrap_content"
         android:drawablePadding="6dp"
         android:drawableStart="@drawable/ic_access_alarms_big"
-        android:textColor="?attr/bgProtectSecondaryTextColor"
+        android:drawableTint="?attr/wallpaperTextColorSecondary"
+        android:drawableTintMode="src_in"
+        android:textColor="?attr/wallpaperTextColorSecondary"
         android:letterSpacing="0.15"
         style="@style/widget_label"
         android:layout_marginStart="6dp"
diff --git a/packages/SystemUI/res-keyguard/layout/keyguard_status_view.xml b/packages/SystemUI/res-keyguard/layout/keyguard_status_view.xml
index 44fae86..ef16eba 100644
--- a/packages/SystemUI/res-keyguard/layout/keyguard_status_view.xml
+++ b/packages/SystemUI/res-keyguard/layout/keyguard_status_view.xml
@@ -28,41 +28,46 @@
     androidprv:layout_maxWidth="@dimen/keyguard_security_width"
     androidprv:layout_maxHeight="@dimen/keyguard_security_height"
     android:gravity="center_horizontal|top">
-    <RelativeLayout
-        android:id="@+id/keyguard_clock_container"
+    <LinearLayout
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
-        android:layout_gravity="center_horizontal|top">
-        <TextClock
-            android:id="@+id/clock_view"
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:layout_gravity="center_horizontal"
-            android:layout_centerHorizontal="true"
-            android:layout_alignParentTop="true"
-            android:textColor="?attr/bgProtectTextColor"
-            android:singleLine="true"
-            style="@style/widget_big_thin"
-            android:format12Hour="@string/keyguard_widget_12_hours_format"
-            android:format24Hour="@string/keyguard_widget_24_hours_format"
-            android:layout_marginBottom="@dimen/bottom_text_spacing_digital" />
-        <com.android.systemui.ChargingView
-            android:id="@+id/battery_doze"
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:layout_alignTop="@id/clock_view"
-            android:layout_alignBottom="@id/clock_view"
-            android:layout_toEndOf="@id/clock_view"
-            android:visibility="invisible"
-            android:src="@drawable/ic_aod_charging_24dp"
-            android:contentDescription="@string/accessibility_ambient_display_charging"
-        />
-
-        <include layout="@layout/keyguard_status_area"
-            android:id="@+id/keyguard_status_area"
+        android:orientation="vertical">
+        <RelativeLayout
+            android:id="@+id/keyguard_clock_container"
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
-            android:layout_below="@id/clock_view" />
+            android:layout_gravity="center_horizontal|top">
+            <TextClock
+                android:id="@+id/clock_view"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_gravity="center_horizontal"
+                android:layout_centerHorizontal="true"
+                android:layout_alignParentTop="true"
+                android:textColor="?attr/wallpaperTextColor"
+                android:singleLine="true"
+                style="@style/widget_big_thin"
+                android:format12Hour="@string/keyguard_widget_12_hours_format"
+                android:format24Hour="@string/keyguard_widget_24_hours_format"
+                android:layout_marginBottom="@dimen/bottom_text_spacing_digital" />
+            <com.android.systemui.ChargingView
+                android:id="@+id/battery_doze"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_alignTop="@id/clock_view"
+                android:layout_alignBottom="@id/clock_view"
+                android:layout_toEndOf="@id/clock_view"
+                android:visibility="invisible"
+                android:src="@drawable/ic_aod_charging_24dp"
+                android:contentDescription="@string/accessibility_ambient_display_charging"
+            />
+
+            <include layout="@layout/keyguard_status_area"
+                android:id="@+id/keyguard_status_area"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:layout_below="@id/clock_view" />
+        </RelativeLayout>
 
         <TextView
             android:id="@+id/owner_info"
@@ -72,12 +77,11 @@
             android:layout_height="wrap_content"
             android:layout_marginTop="@dimen/date_owner_info_margin"
             android:layout_centerHorizontal="true"
-            android:layout_below="@id/keyguard_status_area"
-            android:textColor="?attr/bgProtectSecondaryTextColor"
+            android:textColor="?attr/wallpaperTextColorSecondary"
             android:textSize="@dimen/widget_label_font_size"
             android:letterSpacing="0.05"
             android:ellipsize="marquee"
             android:singleLine="true" />
 
-    </RelativeLayout>
+    </LinearLayout>
 </com.android.keyguard.KeyguardStatusView>
diff --git a/packages/SystemUI/res-keyguard/values/styles.xml b/packages/SystemUI/res-keyguard/values/styles.xml
index 795f20e..ea867ee 100644
--- a/packages/SystemUI/res-keyguard/values/styles.xml
+++ b/packages/SystemUI/res-keyguard/values/styles.xml
@@ -20,11 +20,11 @@
 <resources>
     <!-- Keyguard PIN pad styles -->
     <style name="Keyguard.TextView" parent="@android:style/Widget.DeviceDefault.TextView">
-        <item name="android:textColor">?attr/bgProtectSecondaryTextColor</item>
+        <item name="android:textColor">?attr/wallpaperTextColorSecondary</item>
         <item name="android:textSize">@dimen/kg_status_line_font_size</item>
     </style>
     <style name="Keyguard.TextView.EmergencyButton" parent="@android:style/DeviceDefault.ButtonBar">
-        <item name="android:textColor">?attr/bgProtectSecondaryTextColor</item>
+        <item name="android:textColor">?attr/wallpaperTextColorSecondary</item>
         <item name="android:textSize">@dimen/kg_status_line_font_size</item>
         <item name="android:background">@null</item>
     </style>
@@ -34,16 +34,16 @@
         <item name="android:background">@null</item>
         <item name="android:textSize">32sp</item>
         <item name="android:fontFamily">@*android:string/config_headlineFontFamilyLight</item>
-        <item name="android:textColor">?attr/bgProtectTextColor</item>
+        <item name="android:textColor">?attr/wallpaperTextColor</item>
         <item name="android:paddingBottom">-16dp</item>
     </style>
     <style name="Keyguard.ImageButton.NumPadEnter" parent="@android:style/Widget.ImageButton">
-        <item name="android:tint">?attr/bgProtectTextColor</item>
+        <item name="android:tint">@color/background_protected</item>
     </style>
     <style name="Widget.TextView.NumPadKey.Klondike" parent="Widget.TextView.NumPadKey">
         <item name="android:textSize">12sp</item>
         <item name="android:fontFamily">sans-serif</item>
-        <item name="android:textColor">?attr/bgProtectSecondaryTextColor</item>
+        <item name="android:textColor">?attr/wallpaperTextColorSecondary</item>
         <item name="android:paddingBottom">0dp</item>
     </style>
 
@@ -61,9 +61,9 @@
     </style>
 
     <style name="PasswordTheme" parent="systemui_theme">
-        <item name="android:textColor">?attr/bgProtectTextColor</item>
-        <item name="android:colorControlNormal">?attr/bgProtectTextColor</item>
-        <item name="android:colorControlActivated">?attr/bgProtectTextColor</item>
+        <item name="android:textColor">?attr/wallpaperTextColor</item>
+        <item name="android:colorControlNormal">?attr/wallpaperTextColor</item>
+        <item name="android:colorControlActivated">?attr/wallpaperTextColor</item>
     </style>
 
     <style name="keyguard_presentation_theme" parent="@android:style/Theme.Material.NoActionBar.Fullscreen">
diff --git a/packages/SystemUI/res/color/background_protect_secondary.xml b/packages/SystemUI/res/color/background_protect_secondary.xml
index 26dc151..97744db 100644
--- a/packages/SystemUI/res/color/background_protect_secondary.xml
+++ b/packages/SystemUI/res/color/background_protect_secondary.xml
@@ -16,5 +16,5 @@
   -->
 
 <selector xmlns:android="http://schemas.android.com/apk/res/android">
-    <item android:color="?attr/bgProtectSecondaryTextColor" />
+    <item android:color="?attr/wallpaperTextColorSecondary" />
 </selector>
\ No newline at end of file
diff --git a/packages/SystemUI/res/color/background_protected.xml b/packages/SystemUI/res/color/background_protected.xml
index ab8ed87..ff2009d 100644
--- a/packages/SystemUI/res/color/background_protected.xml
+++ b/packages/SystemUI/res/color/background_protected.xml
@@ -16,5 +16,5 @@
   -->
 
 <selector xmlns:android="http://schemas.android.com/apk/res/android">
-    <item android:color="?attr/bgProtectTextColor" />
+    <item android:color="?attr/wallpaperTextColor" />
 </selector>
\ No newline at end of file
diff --git a/packages/SystemUI/res/color/pin_delete_color.xml b/packages/SystemUI/res/color/pin_delete_color.xml
index bb8b8e3..7d4f132 100644
--- a/packages/SystemUI/res/color/pin_delete_color.xml
+++ b/packages/SystemUI/res/color/pin_delete_color.xml
@@ -15,5 +15,5 @@
   ~ limitations under the License
   -->
 <selector xmlns:android="http://schemas.android.com/apk/res/android">
-    <item android:alpha="0.61" android:color="?attr/bgProtectTextColor" />
+    <item android:alpha="0.61" android:color="?attr/wallpaperTextColor" />
 </selector>
\ No newline at end of file
diff --git a/packages/SystemUI/res/color/pin_divider_color.xml b/packages/SystemUI/res/color/pin_divider_color.xml
index ca5f431..aff2317 100644
--- a/packages/SystemUI/res/color/pin_divider_color.xml
+++ b/packages/SystemUI/res/color/pin_divider_color.xml
@@ -15,5 +15,5 @@
   ~ limitations under the License
   -->
 <selector xmlns:android="http://schemas.android.com/apk/res/android">
-    <item android:alpha="0.45" android:color="?attr/bgProtectSecondaryTextColor" />
+    <item android:alpha="0.45" android:color="?attr/wallpaperTextColorSecondary" />
 </selector>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/keyguard_bottom_area.xml b/packages/SystemUI/res/layout/keyguard_bottom_area.xml
index 5244122..ddd8ef8 100644
--- a/packages/SystemUI/res/layout/keyguard_bottom_area.xml
+++ b/packages/SystemUI/res/layout/keyguard_bottom_area.xml
@@ -39,7 +39,7 @@
             android:layout_height="wrap_content"
             android:gravity="center_horizontal"
             android:textStyle="italic"
-            android:textColor="?attr/bgProtectSecondaryTextColor"
+            android:textColor="?attr/wallpaperTextColorSecondary"
             android:textAppearance="?android:attr/textAppearanceSmall"
             android:visibility="gone" />
 
@@ -49,7 +49,7 @@
             android:layout_height="wrap_content"
             android:gravity="center_horizontal"
             android:textStyle="italic"
-            android:textColor="?attr/bgProtectSecondaryTextColor"
+            android:textColor="?attr/wallpaperTextColorSecondary"
             android:textAppearance="?android:attr/textAppearanceSmall"
             android:accessibilityLiveRegion="polite" />
 
@@ -69,7 +69,7 @@
         android:src="@drawable/ic_camera_alt_24dp"
         android:scaleType="center"
         android:contentDescription="@string/accessibility_camera_button"
-        android:tint="?attr/bgProtectTextColor" />
+        android:tint="?attr/wallpaperTextColor" />
 
     <com.android.systemui.statusbar.KeyguardAffordanceView
         android:id="@+id/left_button"
@@ -79,7 +79,7 @@
         android:src="@drawable/ic_phone_24dp"
         android:scaleType="center"
         android:contentDescription="@string/accessibility_phone_button"
-        android:tint="?attr/bgProtectTextColor" />
+        android:tint="?attr/wallpaperTextColor" />
 
     <com.android.systemui.statusbar.phone.LockIcon
         android:id="@+id/lock_icon"
@@ -89,7 +89,7 @@
         android:src="@drawable/ic_lock_24dp"
         android:contentDescription="@string/accessibility_unlock_button"
         android:scaleType="center"
-        android:tint="?attr/bgProtectTextColor" />
+        android:tint="?attr/wallpaperTextColor" />
 
     <FrameLayout
         android:id="@+id/overlay_container"
diff --git a/packages/SystemUI/res/layout/keyguard_status_bar.xml b/packages/SystemUI/res/layout/keyguard_status_bar.xml
index 71a681c..7b30d6a 100644
--- a/packages/SystemUI/res/layout/keyguard_status_bar.xml
+++ b/packages/SystemUI/res/layout/keyguard_status_bar.xml
@@ -63,7 +63,7 @@
         android:gravity="center_vertical"
         android:ellipsize="marquee"
         android:textAppearance="?android:attr/textAppearanceSmall"
-        android:textColor="?attr/bgProtectSecondaryTextColor"
+        android:textColor="?attr/wallpaperTextColorSecondary"
         android:singleLine="true" />
 
 </com.android.systemui.statusbar.phone.KeyguardStatusBarView>
diff --git a/packages/SystemUI/res/layout/recents_empty.xml b/packages/SystemUI/res/layout/recents_empty.xml
index 8048c68..d7f058c 100644
--- a/packages/SystemUI/res/layout/recents_empty.xml
+++ b/packages/SystemUI/res/layout/recents_empty.xml
@@ -23,8 +23,8 @@
     android:drawableTop="@drawable/recents_empty"
     android:drawablePadding="25dp"
     android:textSize="16sp"
-    android:drawableTint="?attr/bgProtectTextColor"
-    android:textColor="?attr/bgProtectTextColor"
+    android:drawableTint="?attr/wallpaperTextColor"
+    android:textColor="?attr/wallpaperTextColor"
     android:text="@string/recents_empty_message"
     android:fontFamily="sans-serif"
     android:visibility="gone" />
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/recents_stack_action_button.xml b/packages/SystemUI/res/layout/recents_stack_action_button.xml
index 10b4316..4707a8c 100644
--- a/packages/SystemUI/res/layout/recents_stack_action_button.xml
+++ b/packages/SystemUI/res/layout/recents_stack_action_button.xml
@@ -24,7 +24,7 @@
     android:paddingBottom="12dp"
     android:text="@string/recents_stack_action_button_label"
     android:textSize="14sp"
-    android:textColor="?attr/bgProtectTextColor"
+    android:textColor="?attr/wallpaperTextColor"
     android:textAllCaps="true"
     android:shadowColor="#99000000"
     android:shadowDx="0"
diff --git a/packages/SystemUI/res/layout/status_bar_no_notifications.xml b/packages/SystemUI/res/layout/status_bar_no_notifications.xml
index ed4bc67..0a25b69 100644
--- a/packages/SystemUI/res/layout/status_bar_no_notifications.xml
+++ b/packages/SystemUI/res/layout/status_bar_no_notifications.xml
@@ -27,7 +27,7 @@
             android:layout_height="64dp"
             android:paddingTop="28dp"
             android:gravity="top|center_horizontal"
-            android:textColor="?attr/bgProtectTextColor"
+            android:textColor="?attr/wallpaperTextColor"
             android:textSize="16sp"
             android:text="@string/empty_shade_text"/>
 </com.android.systemui.statusbar.EmptyShadeView>
diff --git a/packages/SystemUI/res/layout/status_bar_notification_dismiss_all.xml b/packages/SystemUI/res/layout/status_bar_notification_dismiss_all.xml
index eefa9bc..8dc4cb4 100644
--- a/packages/SystemUI/res/layout/status_bar_notification_dismiss_all.xml
+++ b/packages/SystemUI/res/layout/status_bar_notification_dismiss_all.xml
@@ -30,6 +30,6 @@
             android:focusable="true"
             android:contentDescription="@string/accessibility_clear_all"
             android:text="@string/clear_all_notifications_text"
-            android:textColor="?attr/bgProtectTextColor"
+            android:textColor="?attr/wallpaperTextColor"
             android:textAllCaps="true"/>
 </com.android.systemui.statusbar.DismissView>
diff --git a/packages/SystemUI/res/values-bs/strings.xml b/packages/SystemUI/res/values-bs/strings.xml
index 451c2f7..337b68f 100644
--- a/packages/SystemUI/res/values-bs/strings.xml
+++ b/packages/SystemUI/res/values-bs/strings.xml
@@ -97,7 +97,7 @@
     <string name="phone_label" msgid="2320074140205331708">"otvori telefon"</string>
     <string name="voice_assist_label" msgid="3956854378310019854">"otvori glasovnu pomoć"</string>
     <string name="camera_label" msgid="7261107956054836961">"otvori kameru"</string>
-    <string name="recents_caption_resize" msgid="3517056471774958200">"Izaberite novi raspored zadataka"</string>
+    <string name="recents_caption_resize" msgid="3517056471774958200">"Odaberite novi raspored zadataka"</string>
     <string name="cancel" msgid="6442560571259935130">"Otkaži"</string>
     <string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"Dugme za uvećavanje u slučaju nekompatibilnosti."</string>
     <string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"Uvećani prikaz manjeg ekrana na većem ekranu."</string>
@@ -355,7 +355,7 @@
     <string name="description_target_search" msgid="3091587249776033139">"Pretraživanje"</string>
     <string name="description_direction_up" msgid="7169032478259485180">"Povucite gore za <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>."</string>
     <string name="description_direction_left" msgid="7207478719805562165">"Povucite lijevo za <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>."</string>
-    <string name="zen_priority_introduction" msgid="1149025108714420281">"Neće vas ometati zvukovi i vibracije, osim alarma, podsjetnika, događaja i pozivalaca koje odredite. I dalje ćete čuti sve što ste izabrali za reprodukciju, uključujući muziku, videozapise i igre."</string>
+    <string name="zen_priority_introduction" msgid="1149025108714420281">"Neće vas ometati zvukovi i vibracije, osim alarma, podsjetnika, događaja i pozivalaca koje odredite. I dalje ćete čuti sve što ste odabrali za reprodukciju, uključujući muziku, videozapise i igre."</string>
     <string name="zen_alarms_introduction" msgid="4934328096749380201">"Neće vas ometati zvukovi i vibracije, osim alarma. I dalje ćete čuti sve što izaberete za reprodukciju, uključujući muziku, videozapise i igre."</string>
     <string name="zen_priority_customize_button" msgid="7948043278226955063">"Prilagodi"</string>
     <string name="zen_silence_introduction_voice" msgid="3948778066295728085">"Ovim se blokiraju SVI zvukovi i vibracije, uključujući alarme, muziku, videozapise i igre. I dalje ćete moći obavljati pozive."</string>
diff --git a/packages/SystemUI/res/values-de/strings.xml b/packages/SystemUI/res/values-de/strings.xml
index 0e32df5..ed57564 100644
--- a/packages/SystemUI/res/values-de/strings.xml
+++ b/packages/SystemUI/res/values-de/strings.xml
@@ -314,7 +314,7 @@
     <string name="quick_settings_more_settings" msgid="326112621462813682">"Weitere Einstellungen"</string>
     <string name="quick_settings_done" msgid="3402999958839153376">"Fertig"</string>
     <string name="quick_settings_connected" msgid="1722253542984847487">"Verbunden"</string>
-    <string name="quick_settings_connected_battery_level" msgid="4136051440381328892">"Verbunden, Akkustand <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
+    <string name="quick_settings_connected_battery_level" msgid="4136051440381328892">"Verbunden, Akkustand: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
     <string name="quick_settings_connecting" msgid="47623027419264404">"Verbindung wird hergestellt…"</string>
     <string name="quick_settings_tethering_label" msgid="7153452060448575549">"Tethering"</string>
     <string name="quick_settings_hotspot_label" msgid="6046917934974004879">"Hotspot"</string>
diff --git a/packages/SystemUI/res/values-el/strings.xml b/packages/SystemUI/res/values-el/strings.xml
index 99cbb93..c206d86 100644
--- a/packages/SystemUI/res/values-el/strings.xml
+++ b/packages/SystemUI/res/values-el/strings.xml
@@ -310,7 +310,7 @@
     <string name="quick_settings_more_settings" msgid="326112621462813682">"Περισσότερες ρυθμίσεις"</string>
     <string name="quick_settings_done" msgid="3402999958839153376">"Τέλος"</string>
     <string name="quick_settings_connected" msgid="1722253542984847487">"Συνδέθηκε"</string>
-    <string name="quick_settings_connected_battery_level" msgid="4136051440381328892">"Σύνδεση, μπαταρία <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
+    <string name="quick_settings_connected_battery_level" msgid="4136051440381328892">"Συνδεδεμένη, μπαταρία <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
     <string name="quick_settings_connecting" msgid="47623027419264404">"Σύνδεση…"</string>
     <string name="quick_settings_tethering_label" msgid="7153452060448575549">"Πρόσδεση"</string>
     <string name="quick_settings_hotspot_label" msgid="6046917934974004879">"Σημείο πρόσβασης Wi-Fi"</string>
diff --git a/packages/SystemUI/res/values-es/strings.xml b/packages/SystemUI/res/values-es/strings.xml
index 5575dda..c479cfe 100644
--- a/packages/SystemUI/res/values-es/strings.xml
+++ b/packages/SystemUI/res/values-es/strings.xml
@@ -155,7 +155,7 @@
     <string name="accessibility_cell_data" msgid="5326139158682385073">"Datos móviles"</string>
     <string name="accessibility_cell_data_on" msgid="5927098403452994422">"Datos móviles activados"</string>
     <string name="accessibility_cell_data_off" msgid="443267573897409704">"Datos móviles desactivados"</string>
-    <string name="accessibility_bluetooth_tether" msgid="4102784498140271969">"Compartir por Bluetooth"</string>
+    <string name="accessibility_bluetooth_tether" msgid="4102784498140271969">"Compartir conexión por Bluetooth"</string>
     <string name="accessibility_airplane_mode" msgid="834748999790763092">"Modo avión"</string>
     <string name="accessibility_vpn_on" msgid="5993385083262856059">"La red VPN está activada."</string>
     <string name="accessibility_no_sims" msgid="3957997018324995781">"No hay tarjeta SIM."</string>
@@ -314,7 +314,7 @@
     <string name="quick_settings_connected" msgid="1722253542984847487">"Conectado"</string>
     <string name="quick_settings_connected_battery_level" msgid="4136051440381328892">"Conectado (<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> de batería)"</string>
     <string name="quick_settings_connecting" msgid="47623027419264404">"Conectando..."</string>
-    <string name="quick_settings_tethering_label" msgid="7153452060448575549">"Compartir Internet"</string>
+    <string name="quick_settings_tethering_label" msgid="7153452060448575549">"Compartir conexión"</string>
     <string name="quick_settings_hotspot_label" msgid="6046917934974004879">"Zona Wi-Fi"</string>
     <string name="quick_settings_notifications_label" msgid="4818156442169154523">"Notificaciones"</string>
     <string name="quick_settings_flashlight_label" msgid="2133093497691661546">"Linterna"</string>
@@ -407,7 +407,7 @@
     <string name="user_remove_user_message" msgid="1453218013959498039">"Se eliminarán todas las aplicaciones y todos los datos de este usuario."</string>
     <string name="user_remove_user_remove" msgid="7479275741742178297">"Quitar"</string>
     <string name="battery_saver_notification_title" msgid="237918726750955859">"Ahorro de batería activado"</string>
-    <string name="battery_saver_notification_text" msgid="820318788126672692">"Reduce el rendimiento y el envío de datos en segundo plano"</string>
+    <string name="battery_saver_notification_text" msgid="820318788126672692">"Reduce el rendimiento y los datos en segundo plano"</string>
     <string name="battery_saver_notification_action_text" msgid="109158658238110382">"Desactivar ahorro de batería"</string>
     <string name="media_projection_dialog_text" msgid="3071431025448218928">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> empezará a capturar todo lo que aparezca en la pantalla."</string>
     <string name="media_projection_remember_text" msgid="3103510882172746752">"No volver a mostrar"</string>
@@ -589,7 +589,7 @@
     <string name="battery_panel_title" msgid="7944156115535366613">"Uso de la batería"</string>
     <string name="battery_detail_charging_summary" msgid="1279095653533044008">"Ahorro de batería no disponible mientras se carga el dispositivo"</string>
     <string name="battery_detail_switch_title" msgid="6285872470260795421">"Ahorro de batería"</string>
-    <string name="battery_detail_switch_summary" msgid="9049111149407626804">"Reduce el rendimiento y las conexiones automáticas"</string>
+    <string name="battery_detail_switch_summary" msgid="9049111149407626804">"Reduce el rendimiento y los datos en segundo plano"</string>
     <string name="keyboard_key_button_template" msgid="6230056639734377300">"Botón <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="keyboard_key_home" msgid="2243500072071305073">"Inicio"</string>
     <string name="keyboard_key_back" msgid="2337450286042721351">"Atrás"</string>
diff --git a/packages/SystemUI/res/values-eu/strings.xml b/packages/SystemUI/res/values-eu/strings.xml
index 122baca..820fdb3 100644
--- a/packages/SystemUI/res/values-eu/strings.xml
+++ b/packages/SystemUI/res/values-eu/strings.xml
@@ -312,8 +312,7 @@
     <string name="quick_settings_more_settings" msgid="326112621462813682">"Ezarpen gehiago"</string>
     <string name="quick_settings_done" msgid="3402999958839153376">"Eginda"</string>
     <string name="quick_settings_connected" msgid="1722253542984847487">"Konektatuta"</string>
-    <!-- no translation found for quick_settings_connected_battery_level (4136051440381328892) -->
-    <skip />
+    <string name="quick_settings_connected_battery_level" msgid="4136051440381328892">"Konektatuta. Bateria: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
     <string name="quick_settings_connecting" msgid="47623027419264404">"Konektatzen…"</string>
     <string name="quick_settings_tethering_label" msgid="7153452060448575549">"Konexioa partekatzea"</string>
     <string name="quick_settings_hotspot_label" msgid="6046917934974004879">"Sare publikoa"</string>
diff --git a/packages/SystemUI/res/values-in/strings.xml b/packages/SystemUI/res/values-in/strings.xml
index 51d356e2..12ff89d 100644
--- a/packages/SystemUI/res/values-in/strings.xml
+++ b/packages/SystemUI/res/values-in/strings.xml
@@ -310,7 +310,7 @@
     <string name="quick_settings_more_settings" msgid="326112621462813682">"Setelan lainnya"</string>
     <string name="quick_settings_done" msgid="3402999958839153376">"Selesai"</string>
     <string name="quick_settings_connected" msgid="1722253542984847487">"Tersambung"</string>
-    <string name="quick_settings_connected_battery_level" msgid="4136051440381328892">"Terhubung, daya baterai <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
+    <string name="quick_settings_connected_battery_level" msgid="4136051440381328892">"Terhubung, baterai <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
     <string name="quick_settings_connecting" msgid="47623027419264404">"Menyambung..."</string>
     <string name="quick_settings_tethering_label" msgid="7153452060448575549">"Menambatkan"</string>
     <string name="quick_settings_hotspot_label" msgid="6046917934974004879">"Hotspot"</string>
diff --git a/packages/SystemUI/res/values-iw/strings.xml b/packages/SystemUI/res/values-iw/strings.xml
index 22a1464..ba5816b 100644
--- a/packages/SystemUI/res/values-iw/strings.xml
+++ b/packages/SystemUI/res/values-iw/strings.xml
@@ -534,7 +534,7 @@
     <string name="tuner_warning" msgid="8730648121973575701">"‏System UI Tuner מספק לך דרכים נוספות להתאים אישית את ממשק המשתמש של Android. התכונות הניסיוניות האלה עשויות להשתנות, להתקלקל או להיעלם בגרסאות עתידיות. המשך בזהירות."</string>
     <string name="tuner_persistent_warning" msgid="8597333795565621795">"התכונות הניסיוניות האלה עשויות להשתנות, להתקלקל או להיעלם בגרסאות עתידיות. המשך בזהירות."</string>
     <string name="got_it" msgid="2239653834387972602">"הבנתי"</string>
-    <string name="tuner_toast" msgid="603429811084428439">"‏מזל טוב! System UI Tuner נוסף ל\'הגדרות\'"</string>
+    <string name="tuner_toast" msgid="603429811084428439">"‏מזל טוב! ה-System UI Tuner נוסף ל\'הגדרות\'"</string>
     <string name="remove_from_settings" msgid="8389591916603406378">"הסר מההגדרות"</string>
     <string name="remove_from_settings_prompt" msgid="6069085993355887748">"‏האם להסיר את System UI Tuner ולהפסיק להשתמש בכל התכונות שלו?"</string>
     <string name="activity_not_found" msgid="348423244327799974">"האפליקציה אינה מותקנת במכשיר"</string>
diff --git a/packages/SystemUI/res/values-zh-rCN/strings.xml b/packages/SystemUI/res/values-zh-rCN/strings.xml
index 4065ff6..b8588d9 100644
--- a/packages/SystemUI/res/values-zh-rCN/strings.xml
+++ b/packages/SystemUI/res/values-zh-rCN/strings.xml
@@ -310,8 +310,7 @@
     <string name="quick_settings_more_settings" msgid="326112621462813682">"更多设置"</string>
     <string name="quick_settings_done" msgid="3402999958839153376">"完成"</string>
     <string name="quick_settings_connected" msgid="1722253542984847487">"已连接"</string>
-    <!-- no translation found for quick_settings_connected_battery_level (4136051440381328892) -->
-    <skip />
+    <string name="quick_settings_connected_battery_level" msgid="4136051440381328892">"已连接,电池电量为 <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
     <string name="quick_settings_connecting" msgid="47623027419264404">"正在连接…"</string>
     <string name="quick_settings_tethering_label" msgid="7153452060448575549">"网络共享"</string>
     <string name="quick_settings_hotspot_label" msgid="6046917934974004879">"热点"</string>
diff --git a/packages/SystemUI/res/values/attrs.xml b/packages/SystemUI/res/values/attrs.xml
index 81f0138..0560082 100644
--- a/packages/SystemUI/res/values/attrs.xml
+++ b/packages/SystemUI/res/values/attrs.xml
@@ -127,7 +127,7 @@
 
     <attr name="lightIconTheme" format="reference" />
     <attr name="darkIconTheme" format="reference" />
-    <attr name="bgProtectTextColor" format="color" />
-    <attr name="bgProtectSecondaryTextColor" format="color" />
+    <attr name="wallpaperTextColor" format="color" />
+    <attr name="wallpaperTextColorSecondary" format="color" />
 </resources>
 
diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml
index 02a2e8f..38b2969 100644
--- a/packages/SystemUI/res/values/config.xml
+++ b/packages/SystemUI/res/values/config.xml
@@ -267,6 +267,12 @@
     <!-- Doze: alpha to apply to small icons when dozing -->
     <integer name="doze_small_icon_alpha">222</integer><!-- 87% of 0xff -->
 
+    <!-- Doze: the brightness value to use for the lower brightness AOD mode -->
+    <integer name="config_doze_aod_brightness_low">6</integer>
+
+    <!-- Doze: the brightness value to use for the higher brightness AOD mode -->
+    <integer name="config_doze_aod_brightness_high">27</integer>
+
     <!-- Doze: whether the double tap sensor reports 2D touch coordinates -->
     <bool name="doze_double_tap_reports_touch_coordinates">false</bool>
 
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index b02f189..f060c69 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -831,6 +831,9 @@
             burn-in on AOD -->
     <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="corner_size">16dp</dimen>
     <dimen name="top_padding">0dp</dimen>
     <dimen name="bottom_padding">48dp</dimen>
diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml
index e9dd7e6..07f11a4 100644
--- a/packages/SystemUI/res/values/styles.xml
+++ b/packages/SystemUI/res/values/styles.xml
@@ -41,8 +41,8 @@
         <item name="android:windowShowWallpaper">true</item>
         <item name="android:windowDisablePreview">true</item>
         <item name="clearAllStyle">@style/ClearAllButtonDefaultMargins</item>
-        <item name="bgProtectTextColor">?android:attr/textColorPrimaryInverse</item>
-        <item name="bgProtectSecondaryTextColor">?android:attr/textColorSecondaryInverse</item>
+        <item name="wallpaperTextColor">?android:attr/textColorPrimaryInverse</item>
+        <item name="wallpaperTextColorSecondary">?android:attr/textColorSecondaryInverse</item>
     </style>
 
     <style name="ClearAllButtonDefaultMargins">
@@ -55,8 +55,8 @@
     <!-- Performance optimized Recents theme (no wallpaper) -->
     <style name="RecentsTheme.NoWallpaper">
         <item name="android:windowBackground">@android:color/black</item>
-        <item name="bgProtectTextColor">@android:color/white</item>
-        <item name="bgProtectSecondaryTextColor">@android:color/white</item>
+        <item name="wallpaperTextColor">@android:color/white</item>
+        <item name="wallpaperTextColorSecondary">@android:color/white</item>
     </style>
 
     <!-- Theme used for the activity that shows when the system forced an app to be resizable -->
@@ -306,8 +306,9 @@
     <style name="systemui_theme" parent="systemui_base">
         <item name="lightIconTheme">@style/DualToneLightTheme</item>
         <item name="darkIconTheme">@style/DualToneDarkTheme</item>
-        <item name="bgProtectTextColor">?android:attr/textColorPrimaryInverse</item>
-        <item name="bgProtectSecondaryTextColor">?android:attr/textColorSecondaryInverse</item>
+        <item name="wallpaperTextColor">?android:attr/textColorPrimaryInverse</item>
+        <item name="wallpaperTextColorSecondary">?android:attr/textColorSecondaryInverse</item>
+        <item name="android:colorControlHighlight">?android:attr/textColorSecondaryInverse</item>
         <item name="*android:lockPatternStyle">@style/LockPatternStyle</item>
     </style>
 
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusView.java
index 2262869..820a773 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusView.java
@@ -19,9 +19,13 @@
 import android.app.ActivityManager;
 import android.app.AlarmManager;
 import android.content.Context;
+import android.content.res.ColorStateList;
 import android.content.res.Configuration;
 import android.content.res.Resources;
 import android.graphics.Color;
+import android.graphics.PorterDuff;
+import android.os.Handler;
+import android.os.Looper;
 import android.os.UserHandle;
 import android.support.v4.graphics.ColorUtils;
 import android.text.TextUtils;
@@ -46,6 +50,7 @@
 public class KeyguardStatusView extends GridLayout {
     private static final boolean DEBUG = KeyguardConstants.DEBUG;
     private static final String TAG = "KeyguardStatusView";
+    private static final int MARQUEE_DELAY_MS = 2000;
 
     private final LockPatternUtils mLockPatternUtils;
     private final AlarmManager mAlarmManager;
@@ -56,11 +61,16 @@
     private TextView mOwnerInfo;
     private ViewGroup mClockContainer;
     private ChargingView mBatteryDoze;
+    private View mKeyguardStatusArea;
+    private Runnable mPendingMarqueeStart;
+    private Handler mHandler;
 
     private View[] mVisibleInDoze;
     private boolean mPulsing;
-    private float mDarkAmount;
+    private float mDarkAmount = 0;
     private int mTextColor;
+    private int mDateTextColor;
+    private int mAlarmTextColor;
 
     private KeyguardUpdateMonitorCallback mInfoCallback = new KeyguardUpdateMonitorCallback() {
 
@@ -107,9 +117,29 @@
         super(context, attrs, defStyle);
         mAlarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
         mLockPatternUtils = new LockPatternUtils(getContext());
+        mHandler = new Handler(Looper.myLooper());
     }
 
     private void setEnableMarquee(boolean enabled) {
+        if (DEBUG) Log.v(TAG, "Schedule setEnableMarquee: " + (enabled ? "Enable" : "Disable"));
+        if (enabled) {
+            if (mPendingMarqueeStart == null) {
+                mPendingMarqueeStart = () -> {
+                    setEnableMarqueeImpl(true);
+                    mPendingMarqueeStart = null;
+                };
+                mHandler.postDelayed(mPendingMarqueeStart, MARQUEE_DELAY_MS);
+            }
+        } else {
+            if (mPendingMarqueeStart != null) {
+                mHandler.removeCallbacks(mPendingMarqueeStart);
+                mPendingMarqueeStart = null;
+            }
+            setEnableMarqueeImpl(false);
+        }
+    }
+
+    private void setEnableMarqueeImpl(boolean enabled) {
         if (DEBUG) Log.v(TAG, (enabled ? "Enable" : "Disable") + " transport text marquee");
         if (mAlarmStatusView != null) mAlarmStatusView.setSelected(enabled);
         if (mOwnerInfo != null) mOwnerInfo.setSelected(enabled);
@@ -126,8 +156,11 @@
         mClockView.setAccessibilityDelegate(new KeyguardClockAccessibilityDelegate(mContext));
         mOwnerInfo = findViewById(R.id.owner_info);
         mBatteryDoze = findViewById(R.id.battery_doze);
-        mVisibleInDoze = new View[]{mBatteryDoze, mClockView};
+        mKeyguardStatusArea = findViewById(R.id.keyguard_status_area);
+        mVisibleInDoze = new View[]{mBatteryDoze, mClockView, mKeyguardStatusArea};
         mTextColor = mClockView.getCurrentTextColor();
+        mDateTextColor = mDateView.getCurrentTextColor();
+        mAlarmTextColor = mAlarmStatusView.getCurrentTextColor();
 
         boolean shouldMarquee = KeyguardUpdateMonitor.getInstance(mContext).isDeviceInteractive();
         setEnableMarquee(shouldMarquee);
@@ -186,8 +219,7 @@
     }
 
     public int getClockBottom() {
-        return mClockView.getBottom() +
-                ((MarginLayoutParams) mClockView.getLayoutParams()).bottomMargin;
+        return mKeyguardStatusArea.getBottom();
     }
 
     public float getClockTextSize() {
@@ -304,6 +336,10 @@
         updateDozeVisibleViews();
         mBatteryDoze.setDark(dark);
         mClockView.setTextColor(ColorUtils.blendARGB(mTextColor, Color.WHITE, darkAmount));
+        mDateView.setTextColor(ColorUtils.blendARGB(mDateTextColor, Color.WHITE, darkAmount));
+        int blendedAlarmColor = ColorUtils.blendARGB(mAlarmTextColor, Color.WHITE, darkAmount);
+        mAlarmStatusView.setTextColor(blendedAlarmColor);
+        mAlarmStatusView.setCompoundDrawableTintList(ColorStateList.valueOf(blendedAlarmColor));
     }
 
     public void setPulsing(boolean pulsing) {
diff --git a/packages/SystemUI/src/com/android/systemui/DockedStackExistsListener.java b/packages/SystemUI/src/com/android/systemui/DockedStackExistsListener.java
index c382882..6296297 100644
--- a/packages/SystemUI/src/com/android/systemui/DockedStackExistsListener.java
+++ b/packages/SystemUI/src/com/android/systemui/DockedStackExistsListener.java
@@ -14,56 +14,74 @@
 
 package com.android.systemui;
 
+import android.os.IBinder;
 import android.os.RemoteException;
 import android.util.Log;
 import android.view.IDockedStackListener;
 import android.view.WindowManagerGlobal;
 
+import java.lang.ref.WeakReference;
+import java.util.ArrayList;
 import java.util.function.Consumer;
 
 /**
  * Utility wrapper to listen for whether or not a docked stack exists, to be
  * used for things like the different overview icon in that mode.
  */
-public class DockedStackExistsListener extends IDockedStackListener.Stub {
+public class DockedStackExistsListener {
 
     private static final String TAG = "DockedStackExistsListener";
 
-    private final Consumer<Boolean> mCallback;
+    private static ArrayList<WeakReference<Consumer<Boolean>>> sCallbacks = new ArrayList<>();
 
-    private DockedStackExistsListener(Consumer<Boolean> callback) {
-        mCallback = callback;
+    static {
+        try {
+            WindowManagerGlobal.getWindowManagerService().registerDockedStackListener(
+                    new IDockedStackListener.Stub() {
+                        @Override
+                        public void onDividerVisibilityChanged(boolean b) throws RemoteException {
+
+                        }
+
+                        @Override
+                        public void onDockedStackExistsChanged(boolean exists)
+                                throws RemoteException {
+                            DockedStackExistsListener.onDockedStackExistsChanged(exists);
+                        }
+
+                        @Override
+                        public void onDockedStackMinimizedChanged(boolean b, long l, boolean b1)
+                                throws RemoteException {
+
+                        }
+
+                        @Override
+                        public void onAdjustedForImeChanged(boolean b, long l)
+                                throws RemoteException {
+
+                        }
+
+                        @Override
+                        public void onDockSideChanged(int i) throws RemoteException {
+
+                        }
+                    });
+        } catch (RemoteException e) {
+            Log.e(TAG, "Failed registering docked stack exists listener", e);
+        }
     }
 
-    @Override
-    public void onDividerVisibilityChanged(boolean visible) throws RemoteException {
-    }
 
-    @Override
-    public void onDockedStackExistsChanged(final boolean exists) throws RemoteException {
-        mCallback.accept(exists);
-    }
-
-    @Override
-    public void onDockedStackMinimizedChanged(boolean minimized, long animDuration,
-                                              boolean isHomeStackResizable) throws RemoteException {
-    }
-
-    @Override
-    public void onAdjustedForImeChanged(boolean adjustedForIme, long animDuration)
-            throws RemoteException {
-    }
-
-    @Override
-    public void onDockSideChanged(int newDockSide) throws RemoteException {
+    private static void onDockedStackExistsChanged(boolean exists) {
+        synchronized (sCallbacks) {
+            sCallbacks.removeIf(wf -> wf.get() == null);
+            sCallbacks.forEach(wf -> wf.get().accept(exists));
+        }
     }
 
     public static void register(Consumer<Boolean> callback) {
-        try {
-            WindowManagerGlobal.getWindowManagerService().registerDockedStackListener(
-                    new DockedStackExistsListener(callback));
-        } catch (RemoteException e) {
-            Log.e(TAG, "Failed registering docked stack exists listener", e);
+        synchronized (sCallbacks) {
+            sCallbacks.add(new WeakReference<>(callback));
         }
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java b/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java
index 6571294..907a79e 100644
--- a/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java
+++ b/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java
@@ -199,7 +199,7 @@
             // Load background image dimensions, if we haven't saved them yet
             if (mBackgroundWidth <= 0 || mBackgroundHeight <= 0) {
                 // Need to load the image to get dimensions
-                loadWallpaper(forDraw, true /* needsReset */);
+                loadWallpaper(forDraw, false /* needsReset */);
                 if (DEBUG) {
                     Log.d(TAG, "Reloading, redoing updateSurfaceSize later.");
                 }
diff --git a/packages/SystemUI/src/com/android/systemui/colorextraction/SysuiColorExtractor.java b/packages/SystemUI/src/com/android/systemui/colorextraction/SysuiColorExtractor.java
index ccb8117..44cf003 100644
--- a/packages/SystemUI/src/com/android/systemui/colorextraction/SysuiColorExtractor.java
+++ b/packages/SystemUI/src/com/android/systemui/colorextraction/SysuiColorExtractor.java
@@ -16,6 +16,7 @@
 
 package com.android.systemui.colorextraction;
 
+import android.app.WallpaperColors;
 import android.app.WallpaperManager;
 import android.content.Context;
 import android.os.Handler;
@@ -41,16 +42,16 @@
     private final GradientColors mWpHiddenColors;
 
     public SysuiColorExtractor(Context context) {
-        this(context, new Tonal(), true);
+        this(context, new Tonal(context), true);
     }
 
     @VisibleForTesting
     public SysuiColorExtractor(Context context, ExtractionType type, boolean registerVisibility) {
         super(context, type);
-
         mWpHiddenColors = new GradientColors();
-        mWpHiddenColors.setMainColor(FALLBACK_COLOR);
-        mWpHiddenColors.setSecondaryColor(FALLBACK_COLOR);
+
+        WallpaperColors systemColors = getWallpaperColors(WallpaperManager.FLAG_SYSTEM);
+        updateDefaultGradients(systemColors);
 
         if (registerVisibility) {
             try {
@@ -71,6 +72,24 @@
         }
     }
 
+    private void updateDefaultGradients(WallpaperColors colors) {
+        Tonal.applyFallback(colors, mWpHiddenColors);
+    }
+
+    @Override
+    public void onColorsChanged(WallpaperColors colors, int which) {
+        super.onColorsChanged(colors, which);
+
+        if ((which & WallpaperManager.FLAG_SYSTEM) != 0) {
+            updateDefaultGradients(colors);
+        }
+    }
+
+    @VisibleForTesting
+    GradientColors getFallbackColors() {
+        return mWpHiddenColors;
+    }
+
     /**
      * Get TYPE_NORMAL colors when wallpaper is visible, or fallback otherwise.
      *
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeLog.java b/packages/SystemUI/src/com/android/systemui/doze/DozeLog.java
index ce0a151..0993ace 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeLog.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeLog.java
@@ -35,7 +35,7 @@
     private static final int SIZE = Build.IS_DEBUGGABLE ? 400 : 50;
     static final SimpleDateFormat FORMAT = new SimpleDateFormat("MM-dd HH:mm:ss.SSS");
 
-    private static final int PULSE_REASONS = 5;
+    private static final int PULSE_REASONS = 6;
 
     public static final int PULSE_REASON_NONE = -1;
     public static final int PULSE_REASON_INTENT = 0;
@@ -43,6 +43,7 @@
     public static final int PULSE_REASON_SENSOR_SIGMOTION = 2;
     public static final int PULSE_REASON_SENSOR_PICKUP = 3;
     public static final int PULSE_REASON_SENSOR_DOUBLE_TAP = 4;
+    public static final int PULSE_REASON_SENSOR_LONG_PRESS = 5;
 
     private static boolean sRegisterKeyguardCallback = true;
 
@@ -179,6 +180,7 @@
             case PULSE_REASON_SENSOR_SIGMOTION: return "sigmotion";
             case PULSE_REASON_SENSOR_PICKUP: return "pickup";
             case PULSE_REASON_SENSOR_DOUBLE_TAP: return "doubletap";
+            case PULSE_REASON_SENSOR_LONG_PRESS: return "longpress";
             default: throw new IllegalArgumentException("bad reason: " + pulseReason);
         }
     }
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeScreenBrightness.java b/packages/SystemUI/src/com/android/systemui/doze/DozeScreenBrightness.java
index e461986..28a45aa 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeScreenBrightness.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeScreenBrightness.java
@@ -23,6 +23,9 @@
 import android.hardware.SensorManager;
 import android.os.Handler;
 
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.systemui.R;
+
 /**
  * Controls the screen brightness when dozing.
  */
@@ -34,6 +37,9 @@
     private final Sensor mLightSensor;
     private boolean mRegistered;
 
+    private final int mHighBrightness;
+    private final int mLowBrightness;
+
     public DozeScreenBrightness(Context context, DozeMachine.Service service,
             SensorManager sensorManager, Sensor lightSensor, Handler handler) {
         mContext = context;
@@ -41,6 +47,11 @@
         mSensorManager = sensorManager;
         mLightSensor = lightSensor;
         mHandler = handler;
+
+        mLowBrightness = context.getResources().getInteger(
+                R.integer.config_doze_aod_brightness_low);
+        mHighBrightness = context.getResources().getInteger(
+                R.integer.config_doze_aod_brightness_high);
     }
 
     @Override
@@ -67,7 +78,17 @@
     @Override
     public void onSensorChanged(SensorEvent event) {
         if (mRegistered) {
-            mDozeService.setDozeScreenBrightness(Math.max(1, (int) event.values[0]));
+            mDozeService.setDozeScreenBrightness(computeBrightness((int) event.values[0]));
+        }
+    }
+
+    private int computeBrightness(int sensorValue) {
+        // The sensor reports 0 for off, 1 for low brightness and 2 for high brightness.
+        // We currently use DozeScreenState for screen off, so we treat off as low brightness.
+        if (sensorValue >= 2) {
+            return mHighBrightness;
+        } else {
+            return mLowBrightness;
         }
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java b/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java
index 545a1ea..0d5527c 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java
@@ -98,7 +98,14 @@
                         Settings.Secure.DOZE_PULSE_ON_DOUBLE_TAP,
                         true /* configured */,
                         DozeLog.PULSE_REASON_SENSOR_DOUBLE_TAP,
-                        dozeParameters.doubleTapReportsTouchCoordinates())
+                        dozeParameters.doubleTapReportsTouchCoordinates()),
+                new TriggerSensor(
+                        findSensorWithType(config.longPressSensorType()),
+                        Settings.Secure.DOZE_PULSE_ON_LONG_PRESS,
+                        false /* settingDef */,
+                        true /* configured */,
+                        DozeLog.PULSE_REASON_SENSOR_LONG_PRESS,
+                        true /* reports touch coordinates */),
         };
 
         mProxSensor = new ProxSensor();
@@ -263,6 +270,7 @@
         final int mPulseReason;
         final String mSetting;
         final boolean mReportsTouchCoordinates;
+        final boolean mSettingDefault;
 
         private boolean mRequested;
         private boolean mRegistered;
@@ -270,8 +278,15 @@
 
         public TriggerSensor(Sensor sensor, String setting, boolean configured, int pulseReason,
                 boolean reportsTouchCoordinates) {
+            this(sensor, setting, true /* settingDef */, configured, pulseReason,
+                    reportsTouchCoordinates);
+        }
+
+        public TriggerSensor(Sensor sensor, String setting, boolean settingDef,
+                boolean configured, int pulseReason, boolean reportsTouchCoordinates) {
             mSensor = sensor;
             mSetting = setting;
+            mSettingDefault = settingDef;
             mConfigured = configured;
             mPulseReason = pulseReason;
             mReportsTouchCoordinates = reportsTouchCoordinates;
@@ -305,7 +320,7 @@
             if (TextUtils.isEmpty(mSetting)) {
                 return true;
             }
-            return Settings.Secure.getIntForUser(mResolver, mSetting, 1,
+            return Settings.Secure.getIntForUser(mResolver, mSetting, mSettingDefault ? 1 : 0,
                     UserHandle.USER_CURRENT) != 0;
         }
 
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java b/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java
index ec6caf1..d1f5337 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java
@@ -123,8 +123,9 @@
             float screenX, float screenY) {
         boolean isDoubleTap = pulseReason == DozeLog.PULSE_REASON_SENSOR_DOUBLE_TAP;
         boolean isPickup = pulseReason == DozeLog.PULSE_REASON_SENSOR_PICKUP;
+        boolean isLongPress = pulseReason == DozeLog.PULSE_REASON_SENSOR_LONG_PRESS;
 
-        if (mConfig.alwaysOnEnabled(UserHandle.USER_CURRENT)) {
+        if (mConfig.alwaysOnEnabled(UserHandle.USER_CURRENT) && !isLongPress) {
             proximityCheckThenCall((result) -> {
                 if (result == ProximityCheck.RESULT_NEAR) {
                     // In pocket, drop event.
diff --git a/packages/SystemUI/src/com/android/systemui/fragments/ExtensionFragmentListener.java b/packages/SystemUI/src/com/android/systemui/fragments/ExtensionFragmentListener.java
index 4ff10e9..18fb423 100644
--- a/packages/SystemUI/src/com/android/systemui/fragments/ExtensionFragmentListener.java
+++ b/packages/SystemUI/src/com/android/systemui/fragments/ExtensionFragmentListener.java
@@ -45,6 +45,7 @@
         mFragmentHostManager.getFragmentManager().beginTransaction()
                 .replace(id, (Fragment) mExtension.get(), mTag)
                 .commit();
+        mExtension.clearItem(false);
     }
 
     @Override
@@ -57,6 +58,7 @@
         } catch (ClassCastException e) {
             Log.e(TAG, extension.getClass().getName() + " must be a Fragment", e);
         }
+        mExtension.clearItem(true);
     }
 
     public static <T> void attachExtensonToFragment(View view, String tag, int id,
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchState.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchState.java
index dd3361b..686b3bb 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchState.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchState.java
@@ -29,7 +29,7 @@
  */
 public class PipTouchState {
     private static final String TAG = "PipTouchHandler";
-    private static final boolean DEBUG = true;
+    private static final boolean DEBUG = false;
 
     private ViewConfiguration mViewConfig;
 
diff --git a/packages/SystemUI/src/com/android/systemui/qs/CellTileView.java b/packages/SystemUI/src/com/android/systemui/qs/CellTileView.java
index 5b3ec08..3d8f9ff 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/CellTileView.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/CellTileView.java
@@ -16,12 +16,14 @@
 
 import android.content.Context;
 import android.graphics.drawable.Drawable;
+import android.service.quicksettings.Tile;
 import android.widget.ImageView;
 
 import com.android.settingslib.Utils;
 import com.android.systemui.R;
 import com.android.systemui.plugins.qs.QSTile.Icon;
 import com.android.systemui.plugins.qs.QSTile.State;
+import com.android.systemui.qs.tileimpl.QSTileImpl;
 import com.android.systemui.statusbar.phone.SignalDrawable;
 
 import java.util.Objects;
@@ -35,7 +37,8 @@
     public CellTileView(Context context) {
         super(context);
         mSignalDrawable = new SignalDrawable(mContext);
-        mSignalDrawable.setDarkIntensity(isDark(mContext));
+        mSignalDrawable.setColors(QSTileImpl.getColorForState(context, Tile.STATE_UNAVAILABLE),
+                QSTileImpl.getColorForState(context, Tile.STATE_ACTIVE));
         mSignalDrawable.setIntrinsicSize(context.getResources().getDimensionPixelSize(
                 R.dimen.qs_tile_icon_size));
     }
@@ -48,10 +51,6 @@
         }
     }
 
-    private static int isDark(Context context) {
-        return Utils.getColorAttr(context, android.R.attr.colorForeground) == 0xff000000 ? 1 : 0;
-    }
-
     public static class SignalIcon extends Icon {
 
         private final int mState;
@@ -68,7 +67,8 @@
         public Drawable getDrawable(Context context) {
             //TODO: Not the optimal solution to create this drawable
             SignalDrawable d = new SignalDrawable(context);
-            d.setDarkIntensity(isDark(context));
+            d.setColors(QSTileImpl.getColorForState(context, Tile.STATE_UNAVAILABLE),
+                    QSTileImpl.getColorForState(context, Tile.STATE_ACTIVE));
             d.setLevel(getState());
             return d;
         }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/SlashDrawable.java b/packages/SystemUI/src/com/android/systemui/qs/SlashDrawable.java
index a10aa54..c356148 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/SlashDrawable.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/SlashDrawable.java
@@ -33,11 +33,12 @@
 import android.graphics.Rect;
 import android.graphics.RectF;
 import android.graphics.drawable.Drawable;
-import android.util.Log;
 import android.util.FloatProperty;
 
 public class SlashDrawable extends Drawable {
 
+    public static final float CORNER_RADIUS = 1f;
+
     private final Path mPath = new Path();
     private final Paint mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
 
@@ -61,6 +62,7 @@
     private boolean mSlashed;
     private Mode mTintMode;
     private ColorStateList mTintList;
+    private boolean mAnimationEnabled = true;
 
     public SlashDrawable(Drawable d) {
         mDrawable = d;
@@ -97,6 +99,10 @@
         invalidateSelf();
     }
 
+    public void setAnimationEnabled(boolean enabled) {
+        mAnimationEnabled = enabled;
+    }
+
     // Animate this value on change
     private float mCurrentSlashLength;
     private final FloatProperty mSlashLengthProp = new FloatProperty<SlashDrawable>("slashLength") {
@@ -119,12 +125,15 @@
         final float end = mSlashed ? SLASH_HEIGHT / SCALE : 0f;
         final float start = mSlashed ? 0f : SLASH_HEIGHT / SCALE;
 
-        ObjectAnimator anim = ObjectAnimator.ofFloat(this, mSlashLengthProp, start, end);
-        anim.addUpdateListener((ValueAnimator valueAnimator) -> {
+        if (mAnimationEnabled) {
+            ObjectAnimator anim = ObjectAnimator.ofFloat(this, mSlashLengthProp, start, end);
+            anim.addUpdateListener((ValueAnimator valueAnimator) -> invalidateSelf());
+            anim.setDuration(QS_ANIM_LENGTH);
+            anim.start();
+        } else {
+            mCurrentSlashLength = end;
             invalidateSelf();
-        });
-        anim.setDuration(QS_ANIM_LENGTH);
-        anim.start();
+        }
     }
 
     @Override
@@ -133,8 +142,8 @@
         Matrix m = new Matrix();
         final int width = getBounds().width();
         final int height = getBounds().height();
-        final float radiusX = scale(1f, width);
-        final float radiusY = scale(1f, height);
+        final float radiusX = scale(CORNER_RADIUS, width);
+        final float radiusY = scale(CORNER_RADIUS, height);
         updateRect(
                 scale(LEFT, width),
                 scale(TOP, height),
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSIconViewImpl.java b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSIconViewImpl.java
index 5ab3927..8074cb9 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSIconViewImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSIconViewImpl.java
@@ -98,10 +98,14 @@
                 d.setAutoMirrored(false);
                 d.setLayoutDirection(getLayoutDirection());
             }
-            iv.setImageDrawable(d);
-            if (state.slash != null && iv instanceof SlashImageView) {
-                ((SlashImageView) iv).setState(state.slash);
+
+            if (iv instanceof SlashImageView) {
+                ((SlashImageView) iv).setAnimationEnabled(shouldAnimate);
+                ((SlashImageView) iv).setState(state.slash, d);
+            } else {
+                iv.setImageDrawable(d);
             }
+
             iv.setTag(R.id.qs_icon_tag, state.icon);
             iv.setTag(R.id.qs_slash_tag, state.slash);
             iv.setPadding(0, padding, 0, padding);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/SlashImageView.java b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/SlashImageView.java
index 315a815..13912fe 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/SlashImageView.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/SlashImageView.java
@@ -14,8 +14,10 @@
 
 package com.android.systemui.qs.tileimpl;
 
+import android.annotation.Nullable;
 import android.content.Context;
 import android.graphics.drawable.Drawable;
+import android.support.annotation.NonNull;
 import android.widget.ImageView;
 
 import com.android.internal.annotations.VisibleForTesting;
@@ -26,6 +28,7 @@
 
     @VisibleForTesting
     protected SlashDrawable mSlash;
+    private boolean mAnimationEnabled = true;
 
     public SlashImageView(Context context) {
         super(context);
@@ -34,6 +37,7 @@
     private void ensureSlashDrawable() {
         if (mSlash == null) {
             mSlash = new SlashDrawable(getDrawable());
+            mSlash.setAnimationEnabled(mAnimationEnabled);
             super.setImageDrawable(mSlash);
         }
     }
@@ -46,13 +50,28 @@
         } else if (mSlash == null) {
             super.setImageDrawable(drawable);
         } else {
+            mSlash.setAnimationEnabled(mAnimationEnabled);
             mSlash.setDrawable(drawable);
         }
     }
 
-    public void setState(SlashState slashState) {
+    public void setAnimationEnabled(boolean enabled) {
+        mAnimationEnabled = enabled;
+    }
+
+    private void setSlashState(@NonNull SlashState slashState) {
         ensureSlashDrawable();
         mSlash.setRotation(slashState.rotation);
         mSlash.setSlashed(slashState.isSlashed);
     }
+
+    public void setState(@Nullable SlashState state, @Nullable Drawable drawable) {
+        if (state != null) {
+            setImageDrawable(drawable);
+            setSlashState(state);
+        } else {
+            mSlash = null;
+            setImageDrawable(drawable);
+        }
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
index fa16f8e..f2ea6a6 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
@@ -337,7 +337,7 @@
         getWindow().getAttributes().privateFlags |=
                 WindowManager.LayoutParams.PRIVATE_FLAG_FORCE_DECOR_VIEW_VISIBILITY;
 
-        mLastConfig = Utilities.getAppConfiguration(this);
+        mLastConfig = new Configuration(Utilities.getAppConfiguration(this));
         mFocusTimerDuration = getResources().getInteger(R.integer.recents_auto_advance_duration);
         mIterateTrigger = new DozeTrigger(mFocusTimerDuration, new Runnable() {
             @Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/AnimatedImageView.java b/packages/SystemUI/src/com/android/systemui/statusbar/AnimatedImageView.java
index e5aad03..ba92c45 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/AnimatedImageView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/AnimatedImageView.java
@@ -32,7 +32,7 @@
     private final boolean mHasOverlappingRendering;
     AnimationDrawable mAnim;
     boolean mAttached;
-    private boolean mAllowAnimation;
+    private boolean mAllowAnimation = true;
 
     // Tracks the last image that was set, so that we don't refresh the image if it is exactly
     // the same as the previous one. If this is a resid, we track that. If it's a drawable, we
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
index a601028..e5f68ad 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
@@ -298,7 +298,8 @@
     private void updateNotificationClipHeight(ExpandableNotificationRow row,
             float notificationClipEnd) {
         float viewEnd = row.getTranslationY() + row.getActualHeight();
-        boolean isPinned = row.isPinned() || row.isHeadsUpAnimatingAway();
+        boolean isPinned = (row.isPinned() || row.isHeadsUpAnimatingAway())
+                && !mAmbientState.isDozingAndNotPulsing(row);
         if (viewEnd > notificationClipEnd
                 && (mAmbientState.isShadeExpanded() || !isPinned)) {
             int clipBottomAmount = (int) (viewEnd - notificationClipEnd);
@@ -450,7 +451,7 @@
                 ? fullTransitionAmount
                 : transitionAmount;
         iconState.clampedAppearAmount = clampedAmount;
-        float contentTransformationAmount = !row.isAboveShelf()
+        float contentTransformationAmount = !mAmbientState.isAboveShelf(row)
                     && (isLastChild || iconState.translateContent)
                 ? iconTransitionAmount
                 : 0.0f;
@@ -519,7 +520,7 @@
                 iconState.scaleY = 1.0f;
                 iconState.hidden = false;
             }
-            if (row.isAboveShelf() || (!row.isInShelf() && (isLastChild && row.areGutsExposed()
+            if (mAmbientState.isAboveShelf(row) || (!row.isInShelf() && (isLastChild && row.areGutsExposed()
                     || row.getTranslationZ() > mAmbientState.getBaseZHeight()))) {
                 iconState.hidden = true;
             }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java
index a3e5e45..3e183b6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java
@@ -401,24 +401,6 @@
                 && pm.resolveActivity(PHONE_INTENT, 0) != null;
     }
 
-    private boolean isCameraDisabledByDpm() {
-        final DevicePolicyManager dpm =
-                (DevicePolicyManager) getContext().getSystemService(Context.DEVICE_POLICY_SERVICE);
-        if (dpm != null && mStatusBar != null) {
-            try {
-                final int userId = ActivityManager.getService().getCurrentUser().id;
-                final int disabledFlags = dpm.getKeyguardDisabledFeatures(null, userId);
-                final  boolean disabledBecauseKeyguardSecure =
-                        (disabledFlags & DevicePolicyManager.KEYGUARD_DISABLE_SECURE_CAMERA) != 0
-                                && mStatusBar.isKeyguardSecure();
-                return dpm.getCameraDisabled(null) || disabledBecauseKeyguardSecure;
-            } catch (RemoteException e) {
-                Log.e(TAG, "Can't get userId", e);
-            }
-        }
-        return false;
-    }
-
     private void watchForCameraPolicyChanges() {
         final IntentFilter filter = new IntentFilter();
         filter.addAction(DevicePolicyManager.ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED);
@@ -871,7 +853,8 @@
         @Override
         public IconState getIcon() {
             ResolveInfo resolved = resolveCameraIntent();
-            mIconState.isVisible = !isCameraDisabledByDpm() && resolved != null
+            boolean isCameraDisabled = (mStatusBar != null) && !mStatusBar.isCameraAllowedByAdmin();
+            mIconState.isVisible = !isCameraDisabled && resolved != null
                     && getResources().getBoolean(R.bool.config_keyguardShowCameraAffordance)
                     && mUserSetupComplete;
             mIconState.drawable = mContext.getDrawable(R.drawable.ic_camera_alt_24dp);
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 652288d..e65bab6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java
@@ -78,6 +78,7 @@
     private AccelerateInterpolator mAccelerateInterpolator = new AccelerateInterpolator();
     private int mClockBottom;
     private float mDarkAmount;
+    private int mDozingStackPadding;
 
     /**
      * Refreshes the dimension values.
@@ -97,6 +98,7 @@
                 R.dimen.burn_in_prevention_offset_x);
         mBurnInPreventionOffsetY = res.getDimensionPixelSize(
                 R.dimen.burn_in_prevention_offset_y);
+        mDozingStackPadding = res.getDimensionPixelSize(R.dimen.dozing_stack_padding);
     }
 
     public void setup(int maxKeyguardNotifications, int maxPanelHeight, float expandedHeight,
@@ -135,7 +137,7 @@
 
         result.stackScrollerPadding = (int) interpolate(
                 result.stackScrollerPadding,
-                mClockBottom + y,
+                mClockBottom + y + mDozingStackPadding,
                 mDarkAmount);
 
         result.clockX = (int) interpolate(0, burnInPreventionOffsetX(), mDarkAmount);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java
index f7c2fc8..c1581c8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java
@@ -333,7 +333,7 @@
     }
 
     public void onOverlayChanged() {
-        @ColorInt int textColor = Utils.getColorAttr(mContext, R.attr.bgProtectTextColor);
+        @ColorInt int textColor = Utils.getColorAttr(mContext, R.attr.wallpaperTextColor);
         mCarrierLabel.setTextColor(textColor);
         mBatteryView.setFillColor(textColor);
         mIconManager.setTint(textColor);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
index 3940a15..4ffc15f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
@@ -2499,6 +2499,10 @@
      * @param keyguardIsShowing whether keyguard is being shown
      */
     public boolean canCameraGestureBeLaunched(boolean keyguardIsShowing) {
+        if (!mStatusBar.isCameraAllowedByAdmin()) {
+            return false;
+        }
+
         ResolveInfo resolveInfo = mKeyguardBottomArea.resolveCameraIntent();
         String packageToLaunch = (resolveInfo == null || resolveInfo.activityInfo == null)
                 ? null : resolveInfo.activityInfo.packageName;
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 6d3fcff..6672f5e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
@@ -25,6 +25,7 @@
 import android.graphics.Color;
 import android.graphics.Rect;
 import android.graphics.drawable.Drawable;
+import android.util.Log;
 import android.util.MathUtils;
 import android.view.View;
 import android.view.ViewGroup;
@@ -102,6 +103,7 @@
     protected long mDurationOverride = -1;
     private long mAnimationDelay;
     private Runnable mOnAnimationFinished;
+    private boolean mDeferFinishedListener;
     private final Interpolator mInterpolator = new DecelerateInterpolator();
     private boolean mDozing;
     private float mDozeInFrontAlpha;
@@ -446,7 +448,7 @@
         anim.addListener(new AnimatorListenerAdapter() {
             @Override
             public void onAnimationEnd(Animator animation) {
-                if (mOnAnimationFinished != null) {
+                if (!mDeferFinishedListener && mOnAnimationFinished != null) {
                     mOnAnimationFinished.run();
                     mOnAnimationFinished = null;
                 }
@@ -557,7 +559,12 @@
         float animEndValue = -1;
         if (previousAnimator != null) {
             if (animate || alpha == currentAlpha) {
+                // We are not done yet! Defer calling the finished listener.
+                if (animate) {
+                    mDeferFinishedListener = true;
+                }
                 previousAnimator.cancel();
+                mDeferFinishedListener = false;
             } else {
                 animEndValue = ViewState.getChildTag(scrim, TAG_END_ALPHA);
             }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/SignalDrawable.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/SignalDrawable.java
index d537cda..15ef742 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/SignalDrawable.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/SignalDrawable.java
@@ -36,6 +36,7 @@
 
 import com.android.settingslib.R;
 import com.android.settingslib.Utils;
+import com.android.systemui.qs.SlashDrawable;
 
 public class SignalDrawable extends Drawable {
 
@@ -198,6 +199,11 @@
         return true;
     }
 
+    public void setColors(int background, int foreground) {
+        mPaint.setColor(background);
+        mForegroundPaint.setColor(foreground);
+    }
+
     public void setDarkIntensity(float darkIntensity) {
         if (darkIntensity == mOldDarkIntensity) {
             return;
@@ -333,10 +339,9 @@
             mForegroundPath.reset();
             mFullPath.op(mCutPath, Path.Op.DIFFERENCE);
         } else if (mState == STATE_AIRPLANE) {
-            // Airplane mode is slashed, full-signal
-            mForegroundPath.set(mFullPath);
-            mFullPath.reset();
-            mSlash.draw((int) height, (int) width, canvas, mForegroundPaint);
+            // Airplane mode is slashed, fully drawn background
+            mForegroundPath.reset();
+            mSlash.draw((int) height, (int) width, canvas, mPaint);
         } else if (mState != STATE_CARRIER_CHANGE) {
             mForegroundPath.reset();
             int sigWidth = Math.round(calcFit(mLevel / (mNumLevels - 1)) * (width - 2 * padding));
@@ -473,6 +478,7 @@
 
         void draw(int height, int width, @NonNull Canvas canvas, Paint paint) {
             Matrix m = new Matrix();
+            final float radius = scale(SlashDrawable.CORNER_RADIUS, width);
             updateRect(
                     scale(LEFT, width),
                     scale(TOP, height),
@@ -481,7 +487,7 @@
 
             mPath.reset();
             // Draw the slash vertically
-            mPath.addRect(mSlashRect, Direction.CW);
+            mPath.addRoundRect(mSlashRect, radius, radius, Direction.CW);
             m.setRotate(ROTATION, width / 2, height / 2);
             mPath.transform(m);
             canvas.drawPath(mPath, paint);
@@ -491,7 +497,7 @@
             mPath.transform(m);
             m.setTranslate(mSlashRect.width(), 0);
             mPath.transform(m);
-            mPath.addRect(mSlashRect, Direction.CW);
+            mPath.addRoundRect(mSlashRect, radius, radius, Direction.CW);
             m.setRotate(ROTATION, width / 2, height / 2);
             mPath.transform(m);
             canvas.clipOutPath(mPath);
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 e0dbcc6..2afdab8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -1,5 +1,3 @@
-
-
 /*
  * Copyright (C) 2010 The Android Open Source Project
  *
@@ -104,6 +102,7 @@
 import android.os.UserManager;
 import android.os.Vibrator;
 import android.provider.Settings;
+import android.service.notification.NotificationListenerService;
 import android.service.notification.NotificationListenerService.RankingMap;
 import android.service.notification.StatusBarNotification;
 import android.service.vr.IVrManager;
@@ -565,7 +564,7 @@
         }};
 
     private boolean mWaitingForKeyguardExit;
-    private boolean mDozing;
+    protected boolean mDozing;
     private boolean mDozingRequested;
     protected boolean mScrimSrcModeEnabled;
 
@@ -4607,11 +4606,13 @@
             try {
                 mOverlayManager.setEnabled("com.android.systemui.theme.lightwallpaper",
                         useDarkText, mCurrentUserId);
-                mStatusBarWindowManager.setKeyguardDark(useDarkText);
             } catch (RemoteException e) {
                 Log.w(TAG, "Can't change theme", e);
             }
         }
+
+        // Make sure we have the correct navbar/statusbar colors.
+        mStatusBarWindowManager.setKeyguardDark(useDarkText);
     }
 
     private void updateDozingState() {
@@ -5278,6 +5279,18 @@
         }
     }
 
+    boolean isCameraAllowedByAdmin() {
+        if (mDevicePolicyManager.getCameraDisabled(null, mCurrentUserId)) {
+            return false;
+        } else if (isKeyguardShowing() && isKeyguardSecure()) {
+            // Check if the admin has disabled the camera specifically for the keyguard
+            return (mDevicePolicyManager.getKeyguardDisabledFeatures(null, mCurrentUserId)
+                    & DevicePolicyManager.KEYGUARD_DISABLE_SECURE_CAMERA) == 0;
+        }
+
+        return true;
+    }
+
     public void notifyFpAuthModeChanged() {
         updateDozing();
     }
@@ -5301,6 +5314,10 @@
     }
 
     public boolean isKeyguardShowing() {
+        if (mStatusBarKeyguardViewManager == null) {
+            Slog.i(TAG, "isKeyguardShowing() called before startKeyguard(), returning true");
+            return true;
+        }
         return mStatusBarKeyguardViewManager.isShowing();
     }
 
@@ -5347,6 +5364,12 @@
 
         @Override
         public void pulseWhileDozing(@NonNull PulseCallback callback, int reason) {
+            if (reason == DozeLog.PULSE_REASON_SENSOR_LONG_PRESS) {
+                mPowerManager.wakeUp(SystemClock.uptimeMillis(), "com.android.systemui:NODOZE");
+                startAssist(new Bundle());
+                return;
+            }
+
             mDozeScrimController.pulse(new PulseCallback() {
 
                 @Override
@@ -5570,6 +5593,10 @@
 
     private Set<String> mNonBlockablePkgs;
 
+    public boolean isDeviceInteractive() {
+        return mDeviceInteractive;
+    }
+
     @Override  // NotificationData.Environment
     public boolean isDeviceProvisioned() {
         return mDeviceProvisionedController.isDeviceProvisioned();
@@ -7219,7 +7246,12 @@
             return false;
         }
 
-        if (mNotificationData.shouldSuppressScreenOn(sbn.getKey())) {
+        if (!isDozing() && mNotificationData.shouldSuppressScreenOn(sbn.getKey())) {
+            if (DEBUG) Log.d(TAG, "No peeking: suppressed by DND: " + sbn.getKey());
+            return false;
+        }
+
+        if (isDozing() && mNotificationData.shouldSuppressScreenOff(sbn.getKey())) {
             if (DEBUG) Log.d(TAG, "No peeking: suppressed by DND: " + sbn.getKey());
             return false;
         }
@@ -7246,6 +7278,9 @@
             if (mAccessibilityManager.isTouchExplorationEnabled()) {
                 if (DEBUG) Log.d(TAG, "No peeking: accessible fullscreen: " + sbn.getKey());
                 return false;
+            } else if (mDozing) {
+                // We never want heads up when we are dozing.
+                return false;
             } else {
                 // we only allow head-up on the lockscreen if it doesn't have a fullscreen intent
                 return !mStatusBarKeyguardViewManager.isShowing()
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
index be338de..191b7a2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
@@ -16,6 +16,10 @@
 
 package com.android.systemui.statusbar.phone;
 
+import static com.android.keyguard.KeyguardHostView.OnDismissAction;
+import static com.android.systemui.statusbar.phone.FingerprintUnlockController.MODE_WAKE_AND_UNLOCK;
+import static com.android.systemui.statusbar.phone.FingerprintUnlockController.MODE_WAKE_AND_UNLOCK_PULSING;
+
 import android.content.ComponentCallbacks2;
 import android.content.Context;
 import android.os.Bundle;
@@ -30,18 +34,15 @@
 import com.android.internal.widget.LockPatternUtils;
 import com.android.keyguard.KeyguardUpdateMonitor;
 import com.android.keyguard.KeyguardUpdateMonitorCallback;
+import com.android.keyguard.LatencyTracker;
 import com.android.keyguard.ViewMediatorCallback;
 import com.android.systemui.DejankUtils;
-import com.android.keyguard.LatencyTracker;
 import com.android.systemui.Dependency;
 import com.android.systemui.SystemUIFactory;
 import com.android.systemui.keyguard.DismissCallbackRegistry;
 import com.android.systemui.statusbar.CommandQueue;
 import com.android.systemui.statusbar.RemoteInputController;
 
-import static com.android.keyguard.KeyguardHostView.OnDismissAction;
-import static com.android.systemui.statusbar.phone.FingerprintUnlockController.*;
-
 import java.util.ArrayList;
 
 /**
@@ -95,6 +96,7 @@
     protected boolean mLastRemoteInputActive;
     private boolean mLastDozing;
     private boolean mLastDeferScrimFadeOut;
+    private int mLastFpMode;
 
     private OnDismissAction mAfterKeyguardGoneAction;
     private final ArrayList<Runnable> mAfterKeyguardGoneRunnables = new ArrayList<>();
@@ -583,6 +585,7 @@
         mLastRemoteInputActive = remoteInputActive;
         mLastDozing = mDozing;
         mLastDeferScrimFadeOut = mDeferScrimFadeOut;
+        mLastFpMode = mFingerprintUnlockController.getMode();
         mStatusBar.onKeyguardViewManagerStatesUpdated();
     }
 
@@ -590,15 +593,20 @@
      * @return Whether the navigation bar should be made visible based on the current state.
      */
     protected boolean isNavBarVisible() {
-        return (!(mShowing && !mOccluded) && !mDozing || mBouncer.isShowing() || mRemoteInputActive)
-                && !mDeferScrimFadeOut;
+        int fpMode = mFingerprintUnlockController.getMode();
+        boolean keyguardShowing = mShowing && !mOccluded;
+        boolean hideWhileDozing = mDozing && fpMode != MODE_WAKE_AND_UNLOCK_PULSING;
+        return (!keyguardShowing && !hideWhileDozing || mBouncer.isShowing()
+                || mRemoteInputActive) && !mDeferScrimFadeOut;
     }
 
     /**
      * @return Whether the navigation bar was made visible based on the last known state.
      */
     protected boolean getLastNavBarVisible() {
-        return (!(mLastShowing && !mLastOccluded) && !mLastDozing || mLastBouncerShowing
+        boolean keyguardShowing = mLastShowing && !mLastOccluded;
+        boolean hideWhileDozing = mLastDozing && mLastFpMode != MODE_WAKE_AND_UNLOCK_PULSING;
+        return (!keyguardShowing && !hideWhileDozing || mLastBouncerShowing
                 || mLastRemoteInputActive) && !mLastDeferScrimFadeOut;
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/TrustDrawable.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/TrustDrawable.java
index e8a456e..028da86 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/TrustDrawable.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/TrustDrawable.java
@@ -83,7 +83,7 @@
 
         mPaint = new Paint();
         mPaint.setStyle(Paint.Style.STROKE);
-        mPaint.setColor(Utils.getColorAttr(context, R.attr.bgProtectTextColor));
+        mPaint.setColor(Utils.getColorAttr(context, R.attr.wallpaperTextColor));
         mPaint.setAntiAlias(true);
         mPaint.setStrokeWidth(mThickness);
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/ExtensionController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/ExtensionController.java
index 40e3806..ede8411 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/ExtensionController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/ExtensionController.java
@@ -38,6 +38,13 @@
          * (like configuration) may have changed.
          */
         T reload();
+
+        /**
+         * Null out the cached item for the purpose of memory saving, should only be done
+         * when any other references are already gotten.
+         * @param isDestroyed
+         */
+        void clearItem(boolean isDestroyed);
     }
 
     interface ExtensionBuilder<T> {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/ExtensionControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/ExtensionControllerImpl.java
index b79137e..cc10775 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/ExtensionControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/ExtensionControllerImpl.java
@@ -26,6 +26,7 @@
 import com.android.systemui.statusbar.policy.ConfigurationController.ConfigurationListener;
 import com.android.systemui.tuner.TunerService;
 import com.android.systemui.tuner.TunerService.Tunable;
+import com.android.systemui.util.leak.LeakDetector;
 
 import java.util.ArrayList;
 import java.util.Collections;
@@ -146,7 +147,18 @@
             return get();
         }
 
+        @Override
+        public void clearItem(boolean isDestroyed) {
+            if (isDestroyed && mItem != null) {
+                Dependency.get(LeakDetector.class).trackGarbage(mItem);
+            }
+            mItem = null;
+        }
+
         private void notifyChanged() {
+            if (mItem != null) {
+                Dependency.get(LeakDetector.class).trackGarbage(mItem);
+            }
             mItem = null;
             for (int i = 0; i < mProducers.size(); i++) {
                 final T item = mProducers.get(i).get();
@@ -169,7 +181,7 @@
         }
 
         public void addTunerFactory(TunerFactory<T> factory, String[] keys) {
-            mProducers.add(new TunerItem(factory, factory.keys()));
+            mProducers.add(new TunerItem(factory, keys));
         }
 
         public void addUiMode(int uiMode, Supplier<T> mode) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/AmbientState.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/AmbientState.java
index ba1e7c2..4d8da44 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/AmbientState.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/AmbientState.java
@@ -21,11 +21,15 @@
 
 import com.android.systemui.R;
 import com.android.systemui.statusbar.ActivatableNotificationView;
+import com.android.systemui.statusbar.ExpandableNotificationRow;
+import com.android.systemui.statusbar.ExpandableView;
+import com.android.systemui.statusbar.NotificationData;
 import com.android.systemui.statusbar.NotificationShelf;
 import com.android.systemui.statusbar.StatusBarState;
 import com.android.systemui.statusbar.policy.HeadsUpManager;
 
 import java.util.ArrayList;
+import java.util.Collection;
 
 /**
  * A global state to track all input states for the algorithm.
@@ -59,7 +63,7 @@
     private boolean mPanelTracking;
     private boolean mExpansionChanging;
     private boolean mPanelFullWidth;
-    private boolean mHasPulsingNotifications;
+    private Collection<HeadsUpManager.HeadsUpEntry> mPulsing;
     private boolean mUnlockHintRunning;
     private boolean mQsCustomizerShowing;
     private int mIntrinsicPadding;
@@ -290,11 +294,23 @@
     }
 
     public boolean hasPulsingNotifications() {
-        return mHasPulsingNotifications;
+        return mPulsing != null;
     }
 
-    public void setHasPulsingNotifications(boolean hasPulsing) {
-        mHasPulsingNotifications = hasPulsing;
+    public void setPulsing(Collection<HeadsUpManager.HeadsUpEntry> hasPulsing) {
+        mPulsing = hasPulsing;
+    }
+
+    public boolean isPulsing(NotificationData.Entry entry) {
+        if (mPulsing == null) {
+            return false;
+        }
+        for (HeadsUpManager.HeadsUpEntry e : mPulsing) {
+            if (e.entry == entry) {
+                return true;
+            }
+        }
+        return false;
     }
 
     public boolean isPanelTracking() {
@@ -332,4 +348,34 @@
     public int getIntrinsicPadding() {
         return mIntrinsicPadding;
     }
+
+    /**
+     * Similar to the normal is above shelf logic but doesn't allow it to be above in AOD1.
+     *
+     * @param expandableView the view to check
+     */
+    public boolean isAboveShelf(ExpandableView expandableView) {
+        if (!(expandableView instanceof ExpandableNotificationRow)) {
+            return expandableView.isAboveShelf();
+        }
+        ExpandableNotificationRow row = (ExpandableNotificationRow) expandableView;
+        return row.isAboveShelf() && !isDozingAndNotPulsing(row);
+    }
+
+    /**
+     * @return whether a view is dozing and not pulsing right now
+     */
+    public boolean isDozingAndNotPulsing(ExpandableView view) {
+        if (view instanceof ExpandableNotificationRow) {
+            return isDozingAndNotPulsing((ExpandableNotificationRow) view);
+        }
+        return false;
+    }
+
+    /**
+     * @return whether a row is dozing and not pulsing right now
+     */
+    public boolean isDozingAndNotPulsing(ExpandableNotificationRow row) {
+        return isDark() && !isPulsing(row.getEntry());
+    }
 }
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 74523e2..0097391 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
@@ -805,7 +805,7 @@
      */
     private float getAppearStartPosition() {
         if (mTrackingHeadsUp && mFirstVisibleBackgroundChild != null) {
-            if (mFirstVisibleBackgroundChild.isAboveShelf()) {
+            if (mAmbientState.isAboveShelf(mFirstVisibleBackgroundChild)) {
                 // If we ever expanded beyond the first notification, it's allowed to merge into
                 // the shelf
                 return mFirstVisibleBackgroundChild.getPinnedHeadsUpHeight();
@@ -823,7 +823,8 @@
         int notGoneChildCount = getNotGoneChildCount();
         if (mEmptyShadeView.getVisibility() == GONE && notGoneChildCount != 0) {
             int minNotificationsForShelf = 1;
-            if (mTrackingHeadsUp || mHeadsUpManager.hasPinnedHeadsUp()) {
+            if (mTrackingHeadsUp
+                    || (mHeadsUpManager.hasPinnedHeadsUp() && !mAmbientState.isDark())) {
                 appearPosition = mHeadsUpManager.getTopHeadsUpPinnedHeight();
                 minNotificationsForShelf = 2;
             } else {
@@ -2008,12 +2009,7 @@
     }
 
     private boolean isPulsing(NotificationData.Entry entry) {
-        for (HeadsUpManager.HeadsUpEntry e : mPulsing) {
-            if (e.entry == entry) {
-                return true;
-            }
-        }
-        return false;
+        return mAmbientState.isPulsing(entry);
     }
 
     public boolean hasPulsingNotifications() {
@@ -4148,7 +4144,7 @@
             return;
         }
         mPulsing = pulsing;
-        mAmbientState.setHasPulsingNotifications(hasPulsingNotifications());
+        mAmbientState.setPulsing(pulsing);
         updateNotificationAnimationStates();
         updateContentHeight();
         notifyHeightChangeListener(mShelf);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollAlgorithm.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollAlgorithm.java
index 8235bc7..f4197a3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollAlgorithm.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollAlgorithm.java
@@ -413,7 +413,7 @@
             if (mIsExpanded) {
                 // Ensure that the heads up is always visible even when scrolled off
                 clampHunToTop(ambientState, row, childState);
-                if (i == 0 && row.isAboveShelf()) {
+                if (i == 0 && ambientState.isAboveShelf(row)) {
                     // the first hun can't get off screen.
                     clampHunToMaxTranslation(ambientState, row, childState);
                     childState.hidden = false;
@@ -515,7 +515,7 @@
         ExpandableViewState childViewState = resultState.getViewStateForView(child);
         int zDistanceBetweenElements = ambientState.getZDistanceBetweenElements();
         float baseZ = ambientState.getBaseZHeight();
-        if (child.mustStayOnScreen()
+        if (child.mustStayOnScreen() && !ambientState.isDozingAndNotPulsing(child)
                 && childViewState.yTranslation < ambientState.getTopPadding()
                 + ambientState.getStackTranslation()) {
             if (childrenOnTop != 0.0f) {
@@ -527,7 +527,7 @@
             }
             childViewState.zTranslation = baseZ
                     + childrenOnTop * zDistanceBetweenElements;
-        } else if (i == 0 && child.isAboveShelf()) {
+        } else if (i == 0 && ambientState.isAboveShelf(child)) {
             // In case this is a new view that has never been measured before, we don't want to
             // elevate if we are currently expanded more then the notification
             int shelfHeight = ambientState.getShelf().getIntrinsicHeight();
diff --git a/packages/SystemUI/src/com/android/systemui/util/leak/GarbageMonitor.java b/packages/SystemUI/src/com/android/systemui/util/leak/GarbageMonitor.java
index ba9e60a..021f9c4 100644
--- a/packages/SystemUI/src/com/android/systemui/util/leak/GarbageMonitor.java
+++ b/packages/SystemUI/src/com/android/systemui/util/leak/GarbageMonitor.java
@@ -21,6 +21,7 @@
 import android.os.Handler;
 import android.os.Looper;
 import android.os.SystemProperties;
+import android.provider.Settings;
 import android.support.annotation.VisibleForTesting;
 
 import com.android.systemui.Dependency;
@@ -84,12 +85,15 @@
         // TODO(b/35345376): Turn this back on for debuggable builds after known leak fixed.
         private static final boolean ENABLED = Build.IS_DEBUGGABLE
                 && SystemProperties.getBoolean("debug.enable_leak_reporting", false);
+        private static final String FORCE_ENABLE = "sysui_force_garbage_monitor";
 
         private GarbageMonitor mGarbageMonitor;
 
         @Override
         public void start() {
-            if (!ENABLED) {
+            boolean forceEnable = Settings.Secure.getInt(mContext.getContentResolver(),
+                    FORCE_ENABLE, 0) != 0;
+            if (!ENABLED && !forceEnable) {
                 return;
             }
             mGarbageMonitor = Dependency.get(GarbageMonitor.class);
diff --git a/packages/SystemUI/src/com/android/systemui/volume/Util.java b/packages/SystemUI/src/com/android/systemui/volume/Util.java
index a46a44d..c6d6218 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/Util.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/Util.java
@@ -78,6 +78,7 @@
     }
 
     public static String mediaMetadataToString(MediaMetadata metadata) {
+        if (metadata == null) return null;
         return metadata.getDescription().toString();
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogControllerImpl.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogControllerImpl.java
index eaad2f9..103eb6e 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogControllerImpl.java
@@ -48,8 +48,10 @@
 import com.android.internal.annotations.GuardedBy;
 import com.android.systemui.Dumpable;
 import com.android.systemui.R;
+import com.android.systemui.SysUiServiceProvider;
 import com.android.systemui.plugins.VolumeDialogController;
 import com.android.systemui.qs.tiles.DndTile;
+import com.android.systemui.statusbar.phone.StatusBar;
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
@@ -89,11 +91,12 @@
     private final W mWorker;
     private final Context mContext;
     private AudioManager mAudio;
+    protected StatusBar mStatusBar;
     private final NotificationManager mNoMan;
     private final SettingObserver mObserver;
     private final Receiver mReceiver = new Receiver();
     private final MediaSessions mMediaSessions;
-    private final C mCallbacks = new C();
+    protected C mCallbacks = new C();
     private final State mState = new State();
     private final MediaSessionsCallbacks mMediaSessionsCallbacksW = new MediaSessionsCallbacks();
     private final Vibrator mVibrator;
@@ -123,6 +126,7 @@
         mReceiver.init();
         mVibrator = (Vibrator) mContext.getSystemService(Context.VIBRATOR_SERVICE);
         mHasVibrator = mVibrator != null && mVibrator.hasVibrator();
+        updateStatusBar();
 
         boolean accessibilityVolumeStreamActive = context.getSystemService(
                 AccessibilityManager.class).isAccessibilityVolumeStreamActive();
@@ -326,8 +330,17 @@
         return changed;
     }
 
-    private boolean onVolumeChangedW(int stream, int flags) {
-        final boolean showUI = (flags & AudioManager.FLAG_SHOW_UI) != 0;
+    private void updateStatusBar() {
+        if (mStatusBar == null) {
+            mStatusBar = SysUiServiceProvider.getComponent(mContext, StatusBar.class);
+        }
+    }
+
+    boolean onVolumeChangedW(int stream, int flags) {
+        updateStatusBar();
+
+        final boolean showUI = (mStatusBar != null && mStatusBar.isDeviceInteractive()) &&
+                ((flags & AudioManager.FLAG_SHOW_UI) != 0);
         final boolean fromKey = (flags & AudioManager.FLAG_FROM_KEY) != 0;
         final boolean showVibrateHint = (flags & AudioManager.FLAG_SHOW_VIBRATE_HINT) != 0;
         final boolean showSilentHint = (flags & AudioManager.FLAG_SHOW_SILENT_HINT) != 0;
@@ -638,7 +651,7 @@
         }
     }
 
-    private final class C implements Callbacks {
+    class C implements Callbacks {
         private final HashMap<Callbacks, Handler> mCallbackMap = new HashMap<>();
 
         public void add(Callbacks callback, Handler handler) {
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
index 2527c6b..22fb710 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
@@ -178,7 +178,13 @@
 
     @Override
     public void destroy() {
+        mAccessibility.destroy();
         mController.removeCallback(mControllerCallbackH);
+        if (mZenFooter != null) {
+            mZenFooter.cleanup();
+        }
+        Dependency.get(TunerService.class).removeTunable(this);
+        mHandler.removeCallbacksAndMessages(null);
     }
 
     private void initDialog() {
@@ -1240,16 +1246,14 @@
                 }
             });
             mDialogView.setAccessibilityDelegate(this);
-            mAccessibilityMgr.addAccessibilityStateChangeListener(
-                    new AccessibilityStateChangeListener() {
-                        @Override
-                        public void onAccessibilityStateChanged(boolean enabled) {
-                            updateFeedbackEnabled();
-                        }
-                    });
+            mAccessibilityMgr.addAccessibilityStateChangeListener(mListener);
             updateFeedbackEnabled();
         }
 
+        public void destroy() {
+            mAccessibilityMgr.removeAccessibilityStateChangeListener(mListener);
+        }
+
         @Override
         public boolean onRequestSendAccessibilityEvent(ViewGroup host, View child,
                 AccessibilityEvent event) {
@@ -1272,6 +1276,9 @@
             }
             return false;
         }
+
+        private final AccessibilityStateChangeListener mListener =
+                enabled -> updateFeedbackEnabled();
     }
 
     private static class VolumeRow {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/colorextraction/SysuiColorExtractorTests.java b/packages/SystemUI/tests/src/com/android/systemui/colorextraction/SysuiColorExtractorTests.java
index a81188a..d0f0bfd 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/colorextraction/SysuiColorExtractorTests.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/colorextraction/SysuiColorExtractorTests.java
@@ -48,14 +48,13 @@
 
     @Test
     public void getColors_usesGreyIfWallpaperNotVisible() {
-        ColorExtractor.GradientColors fallbackColors = new ColorExtractor.GradientColors();
-        fallbackColors.setMainColor(ColorExtractor.FALLBACK_COLOR);
-        fallbackColors.setSecondaryColor(ColorExtractor.FALLBACK_COLOR);
-
-        SysuiColorExtractor extractor = new SysuiColorExtractor(getContext(), new Tonal(), false);
+        SysuiColorExtractor extractor = new SysuiColorExtractor(getContext(),
+                new Tonal(getContext()), false);
         simulateEvent(extractor);
         extractor.setWallpaperVisible(false);
 
+        ColorExtractor.GradientColors fallbackColors = extractor.getFallbackColors();
+
         for (int which : sWhich) {
             for (int type : sTypes) {
                 assertEquals("Not using fallback!", extractor.getColors(which, type),
@@ -76,7 +75,6 @@
                     outGradientColorsNormal.set(colors);
                     outGradientColorsDark.set(colors);
                     outGradientColorsExtraDark.set(colors);
-                    return true;
                 }, false);
         simulateEvent(extractor);
         extractor.setWallpaperVisible(true);
@@ -91,7 +89,7 @@
 
     private void simulateEvent(SysuiColorExtractor extractor) {
         // Let's fake a color event
-        extractor.onColorsChanged(new WallpaperColors(Color.valueOf(Color.BLACK), null, null, 0),
+        extractor.onColorsChanged(new WallpaperColors(Color.valueOf(Color.GREEN), null, null, 0),
                 WallpaperManager.FLAG_SYSTEM | WallpaperManager.FLAG_LOCK);
     }
 }
\ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeScreenBrightnessTest.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeScreenBrightnessTest.java
index 514dc8b..fe3221a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeScreenBrightnessTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeScreenBrightnessTest.java
@@ -38,11 +38,13 @@
 import com.android.systemui.utils.hardware.FakeSensorManager;
 
 import org.junit.Before;
+import org.junit.Ignore;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
 @RunWith(AndroidJUnit4.class)
 @SmallTest
+@Ignore
 public class DozeScreenBrightnessTest extends SysuiTestCase {
 
     DozeServiceFake mServiceFake;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/SlashImageViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/SlashImageViewTest.java
index aef584f..9fe3e10 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/SlashImageViewTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/SlashImageViewTest.java
@@ -38,26 +38,39 @@
     private TestableSlashImageView mSlashView;
 
     @Test
-    public void testSetSlashStateCreatesSlashDrawable() {
+    public void testSetNonNullSlashStateCreatesSlashDrawable() {
         SlashState mockState = mock(SlashState.class);
         Drawable mockDrawable = mock(Drawable.class);
         mSlashView = new TestableSlashImageView(mContext);
         assertTrue(mSlashView.getSlashDrawable() == null);
 
-        mSlashView.setImageDrawable(mockDrawable);
-        mSlashView.setState(mockState);
+        mSlashView.setState(mockState, mockDrawable);
 
         assertTrue(mSlashView.getSlashDrawable() != null);
     }
 
     @Test
+    public void testSetNullSlashStateRemovesSlashDrawable() {
+        SlashState mockState = mock(SlashState.class);
+        Drawable mockDrawable = mock(Drawable.class);
+        mSlashView = new TestableSlashImageView(mContext);
+        mSlashView.setState(mockState, mockDrawable);
+
+        assertTrue(mSlashView.getSlashDrawable() != null);
+
+        mSlashView.setState(null, mockDrawable);
+
+        assertTrue(mSlashView.getSlashDrawable() == null);
+    }
+
+    @Test
     public void testSetNullDrawableRemovesSlashDrawable() {
         SlashState mockState = mock(SlashState.class);
         Drawable mockDrawable = mock(Drawable.class);
 
         mSlashView = new TestableSlashImageView(mContext);
         mSlashView.setImageDrawable(mockDrawable);
-        mSlashView.setState(mockState);
+        mSlashView.setState(mockState, mockDrawable);
         mSlashView.setImageDrawable(null);
 
         assertTrue(mSlashView.getSlashDrawable() == null);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
index 8a4983c..c33897e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
@@ -334,6 +334,83 @@
     }
 
     @Test
+    public void testShouldPeek_suppressedScreenOn_dozing() {
+        when(mPowerManager.isScreenOn()).thenReturn(true);
+        when(mHeadsUpManager.isSnoozed(anyString())).thenReturn(false);
+        when(mNotificationData.shouldFilterOut(any())).thenReturn(false);
+        when(mSystemServicesProxy.isDreaming()).thenReturn(false);
+        when(mNotificationData.getImportance(any())).thenReturn(IMPORTANCE_HIGH);
+
+        mStatusBar.mDozing = true;
+        when(mNotificationData.shouldSuppressScreenOn(any())).thenReturn(true);
+        when(mNotificationData.shouldSuppressScreenOff(any())).thenReturn(false);
+
+        Notification n = new Notification.Builder(getContext(), "a").build();
+        StatusBarNotification sbn = new StatusBarNotification("a", "a", 0, "a", 0, 0, n,
+                UserHandle.of(0), null, 0);
+        NotificationData.Entry entry = new NotificationData.Entry(sbn);
+
+        assertTrue(mStatusBar.shouldPeek(entry, sbn));
+    }
+
+    @Test
+    public void testShouldPeek_suppressedScreenOn_noDoze() {
+        when(mPowerManager.isScreenOn()).thenReturn(true);
+        when(mHeadsUpManager.isSnoozed(anyString())).thenReturn(false);
+        when(mNotificationData.shouldFilterOut(any())).thenReturn(false);
+        when(mSystemServicesProxy.isDreaming()).thenReturn(false);
+        when(mNotificationData.getImportance(any())).thenReturn(IMPORTANCE_HIGH);
+
+        mStatusBar.mDozing = false;
+        when(mNotificationData.shouldSuppressScreenOn(any())).thenReturn(true);
+        when(mNotificationData.shouldSuppressScreenOff(any())).thenReturn(false);
+
+        Notification n = new Notification.Builder(getContext(), "a").build();
+        StatusBarNotification sbn = new StatusBarNotification("a", "a", 0, "a", 0, 0, n,
+                UserHandle.of(0), null, 0);
+        NotificationData.Entry entry = new NotificationData.Entry(sbn);
+        assertFalse(mStatusBar.shouldPeek(entry, sbn));
+    }
+    @Test
+    public void testShouldPeek_suppressedScreenOff_dozing() {
+        when(mPowerManager.isScreenOn()).thenReturn(true);
+        when(mHeadsUpManager.isSnoozed(anyString())).thenReturn(false);
+        when(mNotificationData.shouldFilterOut(any())).thenReturn(false);
+        when(mSystemServicesProxy.isDreaming()).thenReturn(false);
+        when(mNotificationData.getImportance(any())).thenReturn(IMPORTANCE_HIGH);
+
+        mStatusBar.mDozing = true;
+        when(mNotificationData.shouldSuppressScreenOn(any())).thenReturn(false);
+        when(mNotificationData.shouldSuppressScreenOff(any())).thenReturn(true);
+
+        Notification n = new Notification.Builder(getContext(), "a").build();
+        StatusBarNotification sbn = new StatusBarNotification("a", "a", 0, "a", 0, 0, n,
+                UserHandle.of(0), null, 0);
+        NotificationData.Entry entry = new NotificationData.Entry(sbn);
+        assertFalse(mStatusBar.shouldPeek(entry, sbn));
+    }
+
+    @Test
+    public void testShouldPeek_suppressedScreenOff_noDoze() {
+        when(mPowerManager.isScreenOn()).thenReturn(true);
+        when(mHeadsUpManager.isSnoozed(anyString())).thenReturn(false);
+        when(mNotificationData.shouldFilterOut(any())).thenReturn(false);
+        when(mSystemServicesProxy.isDreaming()).thenReturn(false);
+        when(mNotificationData.getImportance(any())).thenReturn(IMPORTANCE_HIGH);
+
+        mStatusBar.mDozing = false;
+        when(mNotificationData.shouldSuppressScreenOn(any())).thenReturn(false);
+        when(mNotificationData.shouldSuppressScreenOff(any())).thenReturn(true);
+
+        Notification n = new Notification.Builder(getContext(), "a").build();
+        StatusBarNotification sbn = new StatusBarNotification("a", "a", 0, "a", 0, 0, n,
+                UserHandle.of(0), null, 0);
+        NotificationData.Entry entry = new NotificationData.Entry(sbn);
+        assertTrue(mStatusBar.shouldPeek(entry, sbn));
+    }
+
+
+    @Test
     public void testLogHidden() {
         try {
             mStatusBar.handleVisibleToUserChanged(false);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeExtensionController.java b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeExtensionController.java
index daf7547..586a424 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeExtensionController.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeExtensionController.java
@@ -100,6 +100,11 @@
         }
 
         @Override
+        public void clearItem(boolean isDestroyed) {
+
+        }
+
+        @Override
         public Context getContext() {
             return null;
         }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/volume/UtilTest.java b/packages/SystemUI/tests/src/com/android/systemui/volume/UtilTest.java
new file mode 100644
index 0000000..6b20a1e
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/volume/UtilTest.java
@@ -0,0 +1,39 @@
+/*
+ * 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.systemui.volume;
+
+import android.media.MediaMetadata;
+import android.support.test.filters.SmallTest;
+
+import com.android.systemui.SysuiTestCase;
+
+import junit.framework.Assert;
+
+import org.junit.Test;
+
+@SmallTest
+public class UtilTest extends SysuiTestCase {
+
+    @Test
+    public void testMediaMetadataToString_null() {
+        Assert.assertEquals(null, Util.mediaMetadataToString(null));
+    }
+
+    @Test
+    public void testMediaMetadataToString_notNull() {
+        Assert.assertNotNull(Util.mediaMetadataToString(new MediaMetadata.Builder().build()));
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogControllerImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogControllerImplTest.java
new file mode 100644
index 0000000..8060f5b
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogControllerImplTest.java
@@ -0,0 +1,70 @@
+/*
+ * 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.systemui.volume;
+
+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.content.Context;
+import android.media.AudioManager;
+import android.support.test.filters.SmallTest;
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.statusbar.phone.StatusBar;
+
+import org.junit.Before;
+import org.junit.Test;
+
+@SmallTest
+public class VolumeDialogControllerImplTest extends SysuiTestCase {
+
+    TestableVolumeDialogControllerImpl mVolumeController;
+    VolumeDialogControllerImpl.C mCallback;
+    StatusBar mStatusBar;
+
+    @Before
+    public void setup() throws Exception {
+        mCallback = mock(VolumeDialogControllerImpl.C.class);
+        mStatusBar = mock(StatusBar.class);
+        mVolumeController = new TestableVolumeDialogControllerImpl(mContext, mCallback, mStatusBar);
+    }
+
+    @Test
+    public void testVolumeChangeW_deviceNotInteractiveAOD() {
+        when(mStatusBar.isDeviceInteractive()).thenReturn(false);
+        mVolumeController.onVolumeChangedW(0, AudioManager.FLAG_SHOW_UI);
+        verify(mCallback, never()).onShowRequested(Events.SHOW_REASON_VOLUME_CHANGED);
+    }
+
+    @Test
+    public void testVolumeChangeW_deviceInteractive() {
+        when(mStatusBar.isDeviceInteractive()).thenReturn(true);
+        mVolumeController.onVolumeChangedW(0, AudioManager.FLAG_SHOW_UI);
+        verify(mCallback, times(1)).onShowRequested(Events.SHOW_REASON_VOLUME_CHANGED);
+    }
+
+    static class TestableVolumeDialogControllerImpl extends VolumeDialogControllerImpl {
+        public TestableVolumeDialogControllerImpl(Context context, C callback, StatusBar s) {
+            super(context);
+            mCallbacks = callback;
+            mStatusBar = s;
+        }
+    }
+
+}
diff --git a/proto/src/gnss.proto b/proto/src/gnss.proto
index 33a5584..c54ddad 100644
--- a/proto/src/gnss.proto
+++ b/proto/src/gnss.proto
@@ -33,4 +33,13 @@
 
   // Standard deviation of position accuracy (in meters)
   optional int32 standard_deviation_position_accuracy_meters = 8;
+
+  // Number of top 4 average CN0 processed
+  optional int32 num_top_four_average_cn0_processed = 9;
+
+  // Mean of top 4 average CN0 (dB-Hz)
+  optional double mean_top_four_average_cn0_db_hz = 10;
+
+  // Standard deviation of top 4 average CN0 (dB-Hz)
+  optional double standard_deviation_top_four_average_cn0_db_hz = 11;
 }
\ No newline at end of file
diff --git a/proto/src/ipconnectivity.proto b/proto/src/ipconnectivity.proto
index 76c5418..885896f 100644
--- a/proto/src/ipconnectivity.proto
+++ b/proto/src/ipconnectivity.proto
@@ -23,7 +23,7 @@
 // It is not intended to map one to one to the TRANSPORT_* constants defined in
 // android.net.NetworkCapabilities. Instead it is intended to be used as
 // a dimension field for metrics events and aggregated metrics.
-// Next tag: 7
+// Next tag: 10
 enum LinkLayer {
   // An unknown link layer technology.
   UNKNOWN   = 0;
@@ -32,6 +32,9 @@
   CELLULAR  = 2;
   ETHERNET  = 3;
   WIFI      = 4;
+  WIFI_P2P  = 7;
+  WIFI_NAN  = 8; // Also known as WiFi Aware
+  LOWPAN    = 9;
 
   // Indicates that the link layer dimension is not relevant for the metrics or
   // event considered.
@@ -47,16 +50,18 @@
   optional int32 value = 2;
 };
 
-// Logs changes in the system default network. Changes can be 1) acquiring a
-// default network with no previous default, 2) a switch of the system default
-// network to a new default network, 3) a loss of the system default network.
-// This message is associated to android.net.metrics.DefaultNetworkEvent.
+// An event record when the system default network disconnects or the system
+// switches to a new default network.
+// Next tag: 10.
 message DefaultNetworkEvent {
-  // A value of 0 means this is a loss of the system default network.
-  optional NetworkId network_id = 1;
 
-  // A value of 0 means there was no previous default network.
-  optional NetworkId previous_network_id = 2;
+  // Reason why this network stopped being the default.
+  enum LostReason {
+    UNKNOWN       = 0;
+    OUTSCORED     = 1;
+    INVALIDATION  = 2;
+    DISCONNECT    = 3;
+  };
 
   // Whether the network supports IPv4, IPv6, or both.
   enum IPSupport {
@@ -66,17 +71,52 @@
     DUAL = 3;
   };
 
+  // Duration in milliseconds when this network was the default.
+  // Since version 4
+  optional int64 default_network_duration_ms = 5;
+
+  // Duration in milliseconds without a default network before this network
+  // became the default.
+  // Since version 4
+  optional int64 no_default_network_duration_ms = 6;
+
+  // Network score of this network when it became the default network.
+  // Since version 4
+  optional int64 initial_score = 7;
+
+  // Network score of this network when it stopped being the default network.
+  // Since version 4
+  optional int64 final_score = 8;
+
+  // Best available information about IP support of this default network.
+  // Since version 4
+  optional IPSupport ip_support = 9;
+
+
+  // Deprecated fields
+
+  // A value of 0 means this is a loss of the system default network.
+  // Deprecated since version 3. Replaced by top level network_id.
+  optional NetworkId network_id = 1 [deprecated = true];
+
+  // A value of 0 means there was no previous default network.
+  // Deprecated since version 3. Replaced by previous_default_network_id.
+  optional NetworkId previous_network_id = 2 [deprecated = true];
+
   // Best available information about IP support of the previous network when
   // disconnecting or switching to a new default network.
-  optional IPSupport previous_network_ip_support = 3;
+  // Deprecated since version 3. Replaced by ip_support field.
+  optional IPSupport previous_network_ip_support = 3 [deprecated = true];
 
   // The transport types of the new default network, represented by
   // TRANSPORT_* constants as defined in NetworkCapabilities.
-  repeated int32 transport_types = 4;
+  // Deprecated since version 3. Replaced by top-level transports field.
+  repeated int32 transport_types = 4 [deprecated = true];
 };
 
 // Logs IpReachabilityMonitor probe events and NUD_FAILED events.
 // This message is associated to android.net.metrics.IpReachabilityEvent.
+// Next tag: 3.
 message IpReachabilityEvent {
   // The interface name (wlan, rmnet, lo, ...) on which the probe was sent.
   // Deprecated since version 2, to be replaced by link_layer field.
@@ -91,6 +131,7 @@
 // Logs NetworkMonitor and ConnectivityService events related to the state of
 // a network: connection, evaluation, validation, lingering, and disconnection.
 // This message is associated to android.net.metrics.NetworkEvent.
+// Next tag: 4.
 message NetworkEvent {
   // The id of the network on which this event happened.
   // Deprecated since version 3.
@@ -108,6 +149,7 @@
 // Logs individual captive portal probing events that are performed when
 // evaluating or reevaluating networks for Internet connectivity.
 // This message is associated to android.net.metrics.ValidationProbeEvent.
+// Next tag: 5.
 message ValidationProbeEvent {
   // The id of the network for which the probe was sent.
   // Deprecated since version 3.
@@ -124,26 +166,64 @@
   optional int32 probe_result = 4;
 }
 
-// Logs DNS lookup latencies. Repeated fields must have the same length.
+// Logs DNS lookup latencies.
 // This message is associated to android.net.metrics.DnsEvent.
-// Deprecated since version 2.
+// Next tag: 11
 message DNSLookupBatch {
+
+  // The time it took for successful DNS lookups to complete.
+  // The number of repeated values can be less than getaddrinfo_query_count
+  // + gethostbyname_query_count in case of event rate-limiting.
+  repeated int32 latencies_ms = 4;
+
+  // The total number of getaddrinfo queries.
+  // Since version 4.
+  optional int64 getaddrinfo_query_count = 5;
+
+  // The total number of gethostbyname queries.
+  // Since version 4.
+  optional int64 gethostbyname_query_count = 6;
+
+  // The total number of getaddrinfo errors.
+  // Since version 4.
+  optional int64 getaddrinfo_error_count = 7;
+
+  // The total number of gethostbyname errors.
+  // Since version 4.
+  optional int64 gethostbyname_error_count = 8;
+
+  // Counts of all errors returned by getaddrinfo.
+  // The Pair key field is the getaddrinfo error value.
+  // The value field is the count for that return value.
+  // Since version 4
+  repeated Pair getaddrinfo_errors = 9;
+
+  // Counts of all errors returned by gethostbyname.
+  // The Pair key field is the gethostbyname errno value.
+  // the Pair value field is the count for that errno code.
+  // Since version 4
+  repeated Pair gethostbyname_errors = 10;
+
+
+  // Deprecated fields
+
   // The id of the network on which the DNS lookups took place.
-  optional NetworkId network_id = 1;
+  // Deprecated since version 3.
+  optional NetworkId network_id = 1 [deprecated = true];
 
   // The types of the DNS lookups, as defined in android.net.metrics.DnsEvent.
-  repeated int32 event_types = 2;
+  // Deprecated since version 3.
+  repeated int32 event_types = 2 [deprecated = true];
 
   // The return values of the DNS resolver for each DNS lookups.
-  repeated int32 return_codes = 3;
-
-  // The time it took for each DNS lookups to complete.
-  repeated int32 latencies_ms = 4;
+  // Deprecated since version 3.
+  repeated int32 return_codes = 3 [deprecated = true];
 };
 
 // Represents a collections of DNS lookup latencies and counters for a
 // particular combination of DNS query type and return code.
 // Since version 2.
+// Next tag: 7.
 message DNSLatencies {
   // The type of the DNS lookups, as defined in android.net.metrics.DnsEvent.
   // Acts as a key for a set of DNS query results.
@@ -203,6 +283,7 @@
 // state transition or a response packet parsing error.
 // This message is associated to android.net.metrics.DhcpClientEvent and
 // android.net.metrics.DhcpErrorEvent.
+// Next tag: 5
 message DHCPEvent {
   // The interface name (wlan, rmnet, lo, ...) on which the event happened.
   // Deprecated since version 2, to be replaced by link_layer field.
@@ -255,7 +336,7 @@
 // Represents Router Advertisement listening statistics for an interface with
 // Android Packet Filter enabled.
 // Since version 1.
-// Next tag: 12
+// Next tag: 15
 message ApfStatistics {
   // The time interval in milliseconds these stastistics cover.
   optional int64 duration_ms = 1;
@@ -288,12 +369,28 @@
 
   // The total number of APF program updates triggered when disabling the
   // multicast filter. Since version 3.
+  // Since version 4.
   optional int32 program_updates_allowing_multicast = 11;
+
+  // The total number of packets processed by the APF interpreter.
+  // Since version 4.
+  optional int32 total_packet_processed = 12;
+
+  // The total number of packets dropped by the APF interpreter.
+  // Since version 4.
+  optional int32 total_packet_dropped = 13;
+
+  // List of hardware counters collected by the APF interpreter.
+  // The Pair key is the counter id, defined in android.net.metrics.ApfStats.
+  // The Pair value is the counter value.
+  // Since version 4.
+  repeated Pair hardware_counters = 14;
 }
 
 // Represents the reception of a Router Advertisement packet for an interface
 // with Android Packet Filter enabled.
 // Since version 1.
+// Next tag: 7.
 message RaEvent {
   // All lifetime values are expressed in seconds. The default value for an
   // option lifetime that was not present in the RA option list is -1.
@@ -322,6 +419,7 @@
 // Represents an IP provisioning event in IpManager and how long the
 // provisioning action took.
 // This message is associated to android.net.metrics.IpManagerEvent.
+// Next tag: 4.
 message IpProvisioningEvent {
   // The interface name (wlan, rmnet, lo, ...) on which the probe was sent.
   // Deprecated since version 2, to be replaced by link_layer field.
@@ -335,8 +433,48 @@
   optional int32 latency_ms = 3;
 }
 
+// Represents statistics from a single android Network.
+// Since version 4. Replace NetworkEvent.
+// Next tag: 9.
+message NetworkStats {
+
+  // Duration of this Network lifecycle in milliseconds.
+  optional int64 duration_ms = 1;
+
+  // Information about IP support of this network.
+  optional DefaultNetworkEvent.IPSupport ip_support = 2;
+
+  // True if the network was validated at least once.
+  optional bool ever_validated = 3;
+
+  // True if a captive portal was found at least once on this network.
+  optional bool portal_found = 4;
+
+  // Total number of times no connectivity was reported for this network.
+  optional int32 no_connectivity_reports = 5;
+
+  // Total number of validation attempts.
+  optional int32 validation_attempts = 6;
+
+  // Results from all validation attempts.
+  // The Pair key is the result:
+  //    0 -> unvalidated
+  //    1 -> validated
+  //    2 -> captive portal
+  // The Pair value is the duration of the validation attempts in milliseconds.
+  repeated Pair validation_events = 7;
+
+  // Time series of validation states in time order.
+  // The Pair key is the state:
+  //    0 -> unvalidated
+  //    1 -> validated
+  //    2 -> captive portal,
+  // The Pair value is the duration of that state in milliseconds.
+  repeated Pair validation_states = 8;
+}
+
 // Represents one of the IP connectivity event defined in this file.
-// Next tag: 19
+// Next tag: 20
 message IpConnectivityEvent {
   // Time in ms when the event was recorded.
   optional int64 time_ms = 1;
@@ -370,14 +508,13 @@
   oneof event {
 
     // An event about the system default network.
-    // The link_layer field is not relevant for this event and set to NONE.
     DefaultNetworkEvent default_network_event = 2;
 
     // An IP reachability probe event.
     IpReachabilityEvent ip_reachability_event = 3;
 
     // A network lifecycle event.
-    NetworkEvent network_event = 4;
+    NetworkEvent network_event = 4 [deprecated = true];
 
     // A batch of DNS lookups.
     // Deprecated in the nyc-mr2 release since version 2,and replaced by
@@ -407,10 +544,14 @@
 
     // An RA packet reception event.
     RaEvent ra_event = 11;
+
+    // Network statistics.
+    NetworkStats network_stats = 19;
   };
 };
 
 // The information about IP connectivity events.
+// Next tag: 4.
 message IpConnectivityLog {
   // An array of IP connectivity events.
   repeated IpConnectivityEvent events = 1;
@@ -424,5 +565,6 @@
   //  nyc-mr1: not populated, implicitly 1.
   //  nyc-mr2: 2.
   //  oc:      3.
+  //  oc-dr1:  4. (sailfish, marlin, walleye, taimen)
   optional int32 version = 3;
 };
diff --git a/proto/src/metrics_constants.proto b/proto/src/metrics_constants.proto
index adcc8eb..5319086 100644
--- a/proto/src/metrics_constants.proto
+++ b/proto/src/metrics_constants.proto
@@ -138,6 +138,18 @@
     REASON_TIMEOUT = 19;
   }
 
+  // Subtypes of camera events for ACTION_CAMERA_EVENT
+  enum CameraEvent {
+    // A back-facing camera was used
+    CAMERA_BACK_USED = 0;
+
+    // A front-facing camera was used
+    CAMERA_FRONT_USED = 1;
+
+    // An external camera was used
+    CAMERA_EXTERNAL_USED = 2;
+  }
+
   // Known visual elements: views or controls.
   enum View {
     // Unknown view
@@ -4196,6 +4208,12 @@
     // OS: O DR
     DIALOG_BLUETOOTH_PAIRED_DEVICE_FORGET = 1031;
 
+    // An event from the camera service
+    // CATEGORY: OTHER
+    //  SUBTYPE: CameraEvent
+    // OS: O DR
+    ACTION_CAMERA_EVENT = 1032;
+
     // ---- End O-DR1 Constants, all O-DR1 constants go above this line ----
 
     // ACTION: Settings > Network & Internet > Mobile network > Mobile data
@@ -4213,6 +4231,27 @@
     // OS: O MR
     FIELD_SETTINGS_SEARCH_RESULT_ASYNC_RANKING_STATE = 1083;
 
+    // ACTION: Settings > Connected devices > SMS Mirroring
+    // CATEGORY: SETTINGS
+    // OS: O MR
+    ACTION_SETTINGS_SMS_MIRRORING = 1084;
+
+    // ACTION: Chooser picked a ranked target.
+    // CATEGORY: GLOBAL_SYSTEM_UI
+    // OS: O MR
+    ACTION_TARGET_SELECTED = 1085;
+
+    // FIELD - is category used in Chooser: 1.
+    // Type: int encoded boolean
+    // CATEGORY: GLOBAL_SYSTEM_UI
+    // OS: O MR
+    FIELD_IS_CATEGORY_USED = 1086;
+
+    // FIELD - ranked position of selected target for Chooser.
+    // CATEGORY: GLOBAL_SYSTEM_UI
+    // OS: O MR
+    FIELD_RANKED_POSITION = 1087;
+
     // Add new aosp constants above this line.
     // END OF AOSP CONSTANTS
   }
diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java
index 68b83e6..6d4fb75 100644
--- a/services/autofill/java/com/android/server/autofill/Session.java
+++ b/services/autofill/java/com/android/server/autofill/Session.java
@@ -797,51 +797,48 @@
         }
 
         final AutofillId[] requiredIds = saveInfo.getRequiredIds();
-        if (requiredIds == null || requiredIds.length == 0) {
-            Slog.w(TAG, "showSaveLocked(): no required ids on saveInfo");
-            return true;
-        }
-
         boolean allRequiredAreNotEmpty = true;
         boolean atLeastOneChanged = false;
-        for (int i = 0; i < requiredIds.length; i++) {
-            final AutofillId id = requiredIds[i];
-            if (id == null) {
-                Slog.w(TAG, "null autofill id on " + Arrays.toString(requiredIds));
-                continue;
-            }
-            final ViewState viewState = mViewStates.get(id);
-            if (viewState == null) {
-                Slog.w(TAG, "showSaveLocked(): no ViewState for required " + id);
-                allRequiredAreNotEmpty = false;
-                break;
-            }
-
-            AutofillValue value = viewState.getCurrentValue();
-            if (value == null || value.isEmpty()) {
-                final AutofillValue initialValue = getValueFromContexts(id);
-                if (initialValue != null) {
-                    if (sDebug) {
-                        Slog.d(TAG, "Value of required field " + id + " didn't change; "
-                                + "using initial value (" + initialValue + ") instead");
-                    }
-                    value = initialValue;
-                } else {
-                    if (sDebug) {
-                        Slog.d(TAG, "showSaveLocked(): empty value for required " + id );
-                    }
+        if (requiredIds != null) {
+            for (int i = 0; i < requiredIds.length; i++) {
+                final AutofillId id = requiredIds[i];
+                if (id == null) {
+                    Slog.w(TAG, "null autofill id on " + Arrays.toString(requiredIds));
+                    continue;
+                }
+                final ViewState viewState = mViewStates.get(id);
+                if (viewState == null) {
+                    Slog.w(TAG, "showSaveLocked(): no ViewState for required " + id);
                     allRequiredAreNotEmpty = false;
                     break;
                 }
-            }
-            final AutofillValue filledValue = viewState.getAutofilledValue();
 
-            if (!value.equals(filledValue)) {
-                if (sDebug) {
-                    Slog.d(TAG, "showSaveLocked(): found a change on required " + id + ": "
-                            + filledValue + " => " + value);
+                AutofillValue value = viewState.getCurrentValue();
+                if (value == null || value.isEmpty()) {
+                    final AutofillValue initialValue = getValueFromContexts(id);
+                    if (initialValue != null) {
+                        if (sDebug) {
+                            Slog.d(TAG, "Value of required field " + id + " didn't change; "
+                                    + "using initial value (" + initialValue + ") instead");
+                        }
+                        value = initialValue;
+                    } else {
+                        if (sDebug) {
+                            Slog.d(TAG, "showSaveLocked(): empty value for required " + id );
+                        }
+                        allRequiredAreNotEmpty = false;
+                        break;
+                    }
                 }
-                atLeastOneChanged = true;
+                final AutofillValue filledValue = viewState.getAutofilledValue();
+
+                if (!value.equals(filledValue)) {
+                    if (sDebug) {
+                        Slog.d(TAG, "showSaveLocked(): found a change on required " + id + ": "
+                                + filledValue + " => " + value);
+                    }
+                    atLeastOneChanged = true;
+                }
             }
         }
 
@@ -877,10 +874,20 @@
                 final ValueFinder valueFinder = (id) -> {return getValueAsString(id);};
 
                 final InternalValidator validator = saveInfo.getValidator();
-                if (validator != null && !validator.isValid(valueFinder)) {
-                    // TODO(b/62534917): add CTS test
-                    Slog.i(TAG, "not showing save UI because fields failed validation");
-                    return true;
+                if (validator != null) {
+                    boolean isValid;
+                    try {
+                        isValid = validator.isValid(valueFinder);
+                    } catch (Exception e) {
+                        Slog.e(TAG, "Not showing save UI because of exception during validation "
+                                + e.getClass());
+                        return true;
+                    }
+
+                    if (!isValid) {
+                        Slog.i(TAG, "not showing save UI because fields failed validation");
+                        return true;
+                    }
                 }
 
                 if (sDebug) Slog.d(TAG, "Good news, everyone! All checks passed, show save UI!");
@@ -1354,8 +1361,10 @@
         final SaveInfo saveInfo = response.getSaveInfo();
         if (saveInfo != null) {
             final AutofillId[] requiredIds = saveInfo.getRequiredIds();
-            for (AutofillId id : requiredIds) {
-                createOrUpdateViewStateLocked(id, state, null);
+            if (requiredIds != null) {
+                for (AutofillId id : requiredIds) {
+                    createOrUpdateViewStateLocked(id, state, null);
+                }
             }
             final AutofillId[] optionalIds = saveInfo.getOptionalIds();
             if (optionalIds != null) {
diff --git a/services/autofill/java/com/android/server/autofill/ui/AutoFillUI.java b/services/autofill/java/com/android/server/autofill/ui/AutoFillUI.java
index 8b15d50..67ee185 100644
--- a/services/autofill/java/com/android/server/autofill/ui/AutoFillUI.java
+++ b/services/autofill/java/com/android/server/autofill/ui/AutoFillUI.java
@@ -163,7 +163,8 @@
             @Nullable String filterText, @NonNull String packageName,
             @NonNull AutoFillUiCallback callback) {
         if (sDebug) {
-            Slog.d(TAG, "showFillUi(): id=" + focusedId + ", filter=" + filterText);
+            final int size = filterText == null ? 0 : filterText.length();
+            Slog.d(TAG, "showFillUi(): id=" + focusedId + ", filter=" + size + " chars");
         }
         final LogMaker log = (new LogMaker(MetricsProto.MetricsEvent.AUTOFILL_FILL_UI))
                 .setPackageName(packageName)
diff --git a/services/autofill/java/com/android/server/autofill/ui/FillUi.java b/services/autofill/java/com/android/server/autofill/ui/FillUi.java
index 51a239f..24f3b33 100644
--- a/services/autofill/java/com/android/server/autofill/ui/FillUi.java
+++ b/services/autofill/java/com/android/server/autofill/ui/FillUi.java
@@ -204,7 +204,10 @@
                 return;
             }
             if (count <= 0) {
-                if (sDebug) Slog.d(TAG, "No dataset matches filter: " + mFilterText);
+                if (sDebug) {
+                    final int size = mFilterText == null ? 0 : mFilterText.length();
+                    Slog.d(TAG, "No dataset matches filter with " + size + " chars");
+                }
                 mCallback.requestHideFillUi();
             } else {
                 if (updateContentSize()) {
diff --git a/services/autofill/java/com/android/server/autofill/ui/SaveUi.java b/services/autofill/java/com/android/server/autofill/ui/SaveUi.java
index e8dc3c1..cb19d81 100644
--- a/services/autofill/java/com/android/server/autofill/ui/SaveUi.java
+++ b/services/autofill/java/com/android/server/autofill/ui/SaveUi.java
@@ -28,7 +28,6 @@
 import android.service.autofill.SaveInfo;
 import android.service.autofill.ValueFinder;
 import android.text.Html;
-import android.text.method.LinkMovementMethod;
 import android.util.ArraySet;
 import android.util.Slog;
 import android.view.Gravity;
@@ -144,15 +143,19 @@
         final CustomDescription customDescription = info.getCustomDescription();
 
         if (customDescription != null) {
-            // TODO(b/62534917): add CTS test
             if (sDebug) Slog.d(TAG, "Using custom description");
 
             final RemoteViews presentation = customDescription.getPresentation(valueFinder);
             if (presentation != null) {
-                final View remote = presentation.apply(context, null);
-                final LinearLayout layout = view.findViewById(R.id.autofill_save_custom_subtitle);
-                layout.addView(remote);
-                layout.setVisibility(View.VISIBLE);
+                try {
+                    final View remote = presentation.apply(context, null);
+                    final LinearLayout layout = view.findViewById(
+                            R.id.autofill_save_custom_subtitle);
+                    layout.addView(remote);
+                    layout.setVisibility(View.VISIBLE);
+                } catch (Exception e) {
+                    Slog.e(TAG, "Could not inflate custom description. ", e);
+                }
             } else {
                 Slog.w(TAG, "could not create remote presentation for custom title");
             }
diff --git a/services/backup/java/com/android/server/backup/BackupManagerService.java b/services/backup/java/com/android/server/backup/BackupManagerService.java
index 9486c15..a028028 100644
--- a/services/backup/java/com/android/server/backup/BackupManagerService.java
+++ b/services/backup/java/com/android/server/backup/BackupManagerService.java
@@ -4875,6 +4875,7 @@
                 final int N = mPackages.size();
                 final byte[] buffer = new byte[8192];
                 for (int i = 0; i < N; i++) {
+                    mBackupRunner = null;
                     PackageInfo currentPackage = mPackages.get(i);
                     String packageName = currentPackage.packageName;
                     if (DEBUG) {
@@ -5058,7 +5059,13 @@
                         }
                         EventLog.writeEvent(EventLogTags.FULL_BACKUP_AGENT_FAILURE, packageName,
                                 "transport rejected");
-                        // Do nothing, clean up, and continue looping.
+                        // This failure state can come either a-priori from the transport, or
+                        // from the preflight pass.  If we got as far as preflight, we now need
+                        // to tear down the target process.
+                        if (mBackupRunner != null) {
+                            tearDownAgentAndKill(currentPackage.applicationInfo);
+                        }
+                        // ... and continue looping.
                     } else if (backupPackageStatus == BackupTransport.TRANSPORT_QUOTA_EXCEEDED) {
                         sendBackupOnPackageResult(mBackupObserver, packageName,
                                 BackupManager.ERROR_TRANSPORT_QUOTA_EXCEEDED);
@@ -5067,6 +5074,7 @@
                             EventLog.writeEvent(EventLogTags.FULL_BACKUP_QUOTA_EXCEEDED,
                                     packageName);
                         }
+                        tearDownAgentAndKill(currentPackage.applicationInfo);
                         // Do nothing, clean up, and continue looping.
                     } else if (backupPackageStatus == BackupTransport.AGENT_ERROR) {
                         sendBackupOnPackageResult(mBackupObserver, packageName,
@@ -5090,6 +5098,7 @@
                         EventLog.writeEvent(EventLogTags.FULL_BACKUP_TRANSPORT_FAILURE);
                         // Abort entire backup pass.
                         backupRunStatus = BackupManager.ERROR_TRANSPORT_ABORTED;
+                        tearDownAgentAndKill(currentPackage.applicationInfo);
                         return;
                     } else {
                         // Success!
diff --git a/services/backup/java/com/android/server/backup/BackupPasswordManager.java b/services/backup/java/com/android/server/backup/BackupPasswordManager.java
new file mode 100644
index 0000000..ee7651b
--- /dev/null
+++ b/services/backup/java/com/android/server/backup/BackupPasswordManager.java
@@ -0,0 +1,307 @@
+/*
+ * 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.backup;
+
+import android.content.Context;
+import android.util.Slog;
+
+import com.android.server.backup.utils.DataStreamFileCodec;
+import com.android.server.backup.utils.DataStreamCodec;
+import com.android.server.backup.utils.PasswordUtils;
+
+import java.io.DataInputStream;
+import java.io.DataOutputStream;
+import java.io.File;
+import java.io.IOException;
+import java.security.SecureRandom;
+
+/**
+ * Manages persisting and verifying backup passwords.
+ *
+ * <p>Does not persist the password itself, but persists a PBKDF2 hash with a randomly chosen (also
+ * persisted) salt. Validation is performed by running the challenge text through the same
+ * PBKDF2 cycle with the persisted salt, and checking the hashes match.
+ *
+ * @see PasswordUtils for the hashing algorithm.
+ */
+public final class BackupPasswordManager {
+    private static final String TAG = "BackupPasswordManager";
+    private static final boolean DEBUG = false;
+
+    private static final int BACKUP_PW_FILE_VERSION = 2;
+    private static final int DEFAULT_PW_FILE_VERSION = 1;
+
+    private static final String PASSWORD_VERSION_FILE_NAME = "pwversion";
+    private static final String PASSWORD_HASH_FILE_NAME = "pwhash";
+
+    // See https://android-developers.googleblog.com/2013/12/changes-to-secretkeyfactory-api-in.html
+    public static final String PBKDF_CURRENT = "PBKDF2WithHmacSHA1";
+    public static final String PBKDF_FALLBACK = "PBKDF2WithHmacSHA1And8bit";
+
+    private final SecureRandom mRng;
+    private final Context mContext;
+    private final File mBaseStateDir;
+
+    private String mPasswordHash;
+    private int mPasswordVersion;
+    private byte[] mPasswordSalt;
+
+    /**
+     * Creates an instance enforcing permissions using the {@code context} and persisting password
+     * data within the {@code baseStateDir}.
+     *
+     * @param context The context, for enforcing permissions around setting the password.
+     * @param baseStateDir A directory within which to persist password data.
+     * @param secureRandom Random number generator with which to generate password salts.
+     */
+    BackupPasswordManager(Context context, File baseStateDir, SecureRandom secureRandom) {
+        mContext = context;
+        mRng = secureRandom;
+        mBaseStateDir = baseStateDir;
+        loadStateFromFilesystem();
+    }
+
+    /**
+     * Returns {@code true} if a password for backup is set.
+     *
+     * @throws SecurityException If caller does not have {@link android.Manifest.permission#BACKUP}
+     *   permission.
+     */
+    boolean hasBackupPassword() {
+        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
+                "hasBackupPassword");
+        return mPasswordHash != null && mPasswordHash.length() > 0;
+    }
+
+    /**
+     * Returns {@code true} if {@code password} matches the persisted password.
+     *
+     * @throws SecurityException If caller does not have {@link android.Manifest.permission#BACKUP}
+     *   permission.
+     */
+    boolean backupPasswordMatches(String password) {
+        if (hasBackupPassword() && !passwordMatchesSaved(password)) {
+            if (DEBUG) Slog.w(TAG, "Backup password mismatch; aborting");
+            return false;
+        }
+        return true;
+    }
+
+    /**
+     * Sets the new password, given a correct current password.
+     *
+     * @throws SecurityException If caller does not have {@link android.Manifest.permission#BACKUP}
+     *   permission.
+     * @return {@code true} if has permission to set the password, {@code currentPassword}
+     *   matches the currently persisted password, and is able to persist {@code newPassword}.
+     */
+    boolean setBackupPassword(String currentPassword, String newPassword) {
+        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
+                "setBackupPassword");
+
+        if (!passwordMatchesSaved(currentPassword)) {
+            return false;
+        }
+
+        // Snap up to latest password file version.
+        try {
+            getPasswordVersionFileCodec().serialize(BACKUP_PW_FILE_VERSION);
+            mPasswordVersion = BACKUP_PW_FILE_VERSION;
+        } catch (IOException e) {
+            Slog.e(TAG, "Unable to write backup pw version; password not changed");
+            return false;
+        }
+
+        if (newPassword == null || newPassword.isEmpty()) {
+            return clearPassword();
+        }
+
+        try {
+            byte[] salt = randomSalt();
+            String newPwHash = PasswordUtils.buildPasswordHash(
+                    PBKDF_CURRENT, newPassword, salt, PasswordUtils.PBKDF2_HASH_ROUNDS);
+
+            getPasswordHashFileCodec().serialize(new BackupPasswordHash(newPwHash, salt));
+            mPasswordHash = newPwHash;
+            mPasswordSalt = salt;
+            return true;
+        } catch (IOException e) {
+            Slog.e(TAG, "Unable to set backup password");
+        }
+        return false;
+    }
+
+    /**
+     * Returns {@code true} if should try salting using the older PBKDF algorithm.
+     *
+     * <p>This is {@code true} for v1 files.
+     */
+    private boolean usePbkdf2Fallback() {
+        return mPasswordVersion < BACKUP_PW_FILE_VERSION;
+    }
+
+    /**
+     * Deletes the current backup password.
+     *
+     * @return {@code true} if successful.
+     */
+    private boolean clearPassword() {
+        File passwordHashFile = getPasswordHashFile();
+        if (passwordHashFile.exists() && !passwordHashFile.delete()) {
+            Slog.e(TAG, "Unable to clear backup password");
+            return false;
+        }
+
+        mPasswordHash = null;
+        mPasswordSalt = null;
+        return true;
+    }
+
+    /**
+     * Sets the password hash, salt, and version in the object from what has been persisted to the
+     * filesystem.
+     */
+    private void loadStateFromFilesystem() {
+        try {
+            mPasswordVersion = getPasswordVersionFileCodec().deserialize();
+        } catch (IOException e) {
+            Slog.e(TAG, "Unable to read backup pw version");
+            mPasswordVersion = DEFAULT_PW_FILE_VERSION;
+        }
+
+        try {
+            BackupPasswordHash hash = getPasswordHashFileCodec().deserialize();
+            mPasswordHash = hash.hash;
+            mPasswordSalt = hash.salt;
+        } catch (IOException e) {
+            Slog.e(TAG, "Unable to read saved backup pw hash");
+        }
+    }
+
+    /**
+     * Whether the candidate password matches the current password. If the persisted password is an
+     * older version, attempts hashing using the older algorithm.
+     *
+     * @param candidatePassword The password to try.
+     * @return {@code true} if the passwords match.
+     */
+    private boolean passwordMatchesSaved(String candidatePassword) {
+        return passwordMatchesSaved(PBKDF_CURRENT, candidatePassword)
+                || (usePbkdf2Fallback() && passwordMatchesSaved(PBKDF_FALLBACK, candidatePassword));
+    }
+
+    /**
+     * Returns {@code true} if the candidate password is correct.
+     *
+     * @param algorithm The algorithm used to hash passwords.
+     * @param candidatePassword The candidate password to compare to the current password.
+     * @return {@code true} if the candidate password matched the saved password.
+     */
+    private boolean passwordMatchesSaved(String algorithm, String candidatePassword) {
+        if (mPasswordHash == null) {
+            return candidatePassword == null || candidatePassword.equals("");
+        } else if (candidatePassword == null || candidatePassword.length() == 0) {
+            // The current password is not zero-length, but the candidate password is.
+            return false;
+        } else {
+            String candidatePasswordHash = PasswordUtils.buildPasswordHash(
+                    algorithm, candidatePassword, mPasswordSalt, PasswordUtils.PBKDF2_HASH_ROUNDS);
+            return mPasswordHash.equalsIgnoreCase(candidatePasswordHash);
+        }
+    }
+
+    private byte[] randomSalt() {
+        int bitsPerByte = 8;
+        byte[] array = new byte[PasswordUtils.PBKDF2_SALT_SIZE / bitsPerByte];
+        mRng.nextBytes(array);
+        return array;
+    }
+
+    private DataStreamFileCodec<Integer> getPasswordVersionFileCodec() {
+        return new DataStreamFileCodec<>(
+                new File(mBaseStateDir, PASSWORD_VERSION_FILE_NAME),
+                new PasswordVersionFileCodec());
+    }
+
+    private DataStreamFileCodec<BackupPasswordHash> getPasswordHashFileCodec() {
+        return new DataStreamFileCodec<>(getPasswordHashFile(), new PasswordHashFileCodec());
+    }
+
+    private File getPasswordHashFile() {
+        return new File(mBaseStateDir, PASSWORD_HASH_FILE_NAME);
+    }
+
+    /**
+     * Container class for a PBKDF hash and the salt used to create the hash.
+     */
+    private static final class BackupPasswordHash {
+        public String hash;
+        public byte[] salt;
+
+        BackupPasswordHash(String hash, byte[] salt) {
+            this.hash = hash;
+            this.salt = salt;
+        }
+    }
+
+    /**
+     * The password version file contains a single 32-bit integer.
+     */
+    private static final class PasswordVersionFileCodec implements
+            DataStreamCodec<Integer> {
+        @Override
+        public void serialize(Integer integer, DataOutputStream dataOutputStream)
+                throws IOException {
+            dataOutputStream.write(integer);
+        }
+
+        @Override
+        public Integer deserialize(DataInputStream dataInputStream) throws IOException {
+            return dataInputStream.readInt();
+        }
+    }
+
+    /**
+     * The passwords hash file contains
+     *
+     * <ul>
+     *     <li>A 32-bit integer representing the number of bytes in the salt;
+     *     <li>The salt bytes;
+     *     <li>A UTF-8 string of the hash.
+     * </ul>
+     */
+    private static final class PasswordHashFileCodec implements
+            DataStreamCodec<BackupPasswordHash> {
+        @Override
+        public void serialize(BackupPasswordHash backupPasswordHash,
+                DataOutputStream dataOutputStream) throws IOException {
+            dataOutputStream.writeInt(backupPasswordHash.salt.length);
+            dataOutputStream.write(backupPasswordHash.salt);
+            dataOutputStream.writeUTF(backupPasswordHash.hash);
+        }
+
+        @Override
+        public BackupPasswordHash deserialize(
+                DataInputStream dataInputStream) throws IOException {
+            int saltLen = dataInputStream.readInt();
+            byte[] salt = new byte[saltLen];
+            dataInputStream.readFully(salt);
+            String hash = dataInputStream.readUTF();
+            return new BackupPasswordHash(hash, salt);
+        }
+    }
+}
diff --git a/services/backup/java/com/android/server/backup/DataChangedJournal.java b/services/backup/java/com/android/server/backup/DataChangedJournal.java
new file mode 100644
index 0000000..9360c85
--- /dev/null
+++ b/services/backup/java/com/android/server/backup/DataChangedJournal.java
@@ -0,0 +1,142 @@
+/*
+ * 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.backup;
+
+import android.annotation.Nullable;
+
+import java.io.BufferedInputStream;
+import java.io.DataInputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.RandomAccessFile;
+import java.util.ArrayList;
+
+/**
+ * A journal of packages that have indicated that their data has changed (and therefore should be
+ * backed up in the next scheduled K/V backup pass).
+ *
+ * <p>This information is persisted to the filesystem so that it is not lost in the event of a
+ * reboot.
+ */
+public final class DataChangedJournal {
+    private static final String FILE_NAME_PREFIX = "journal";
+
+    /**
+     * Journals tend to be on the order of a few kilobytes, hence setting the buffer size to 8kb.
+     */
+    private static final int BUFFER_SIZE_BYTES = 8 * 1024;
+
+    private final File mFile;
+
+    /**
+     * Constructs an instance that reads from and writes to the given file.
+     */
+    DataChangedJournal(File file) {
+        mFile = file;
+    }
+
+    /**
+     * Adds the given package to the journal.
+     *
+     * @param packageName The name of the package whose data has changed.
+     * @throws IOException if there is an IO error writing to the journal file.
+     */
+    public void addPackage(String packageName) throws IOException {
+        try (RandomAccessFile out = new RandomAccessFile(mFile, "rws")) {
+            out.seek(out.length());
+            out.writeUTF(packageName);
+        }
+    }
+
+    /**
+     * Invokes {@link Consumer#accept(String)} with every package name in the journal file.
+     *
+     * @param consumer The callback.
+     * @throws IOException If there is an IO error reading from the file.
+     */
+    public void forEach(Consumer consumer) throws IOException {
+        try (
+            BufferedInputStream bufferedInputStream = new BufferedInputStream(
+                    new FileInputStream(mFile), BUFFER_SIZE_BYTES);
+            DataInputStream dataInputStream = new DataInputStream(bufferedInputStream)
+        ) {
+            while (dataInputStream.available() > 0) {
+                String packageName = dataInputStream.readUTF();
+                consumer.accept(packageName);
+            }
+        }
+    }
+
+    /**
+     * Deletes the journal from the filesystem.
+     *
+     * @return {@code true} if successfully deleted journal.
+     */
+    public boolean delete() {
+        return mFile.delete();
+    }
+
+    @Override
+    public boolean equals(@Nullable Object object) {
+        if (object instanceof DataChangedJournal) {
+            DataChangedJournal that = (DataChangedJournal) object;
+            try {
+                return this.mFile.getCanonicalPath().equals(that.mFile.getCanonicalPath());
+            } catch (IOException exception) {
+                return false;
+            }
+        }
+        return false;
+    }
+
+    @Override
+    public String toString() {
+        return mFile.toString();
+    }
+
+    /**
+     * Consumer for iterating over package names in the journal.
+     */
+    @FunctionalInterface
+    public interface Consumer {
+        void accept(String packageName);
+    }
+
+    /**
+     * Creates a new journal with a random file name in the given journal directory.
+     *
+     * @param journalDirectory The directory where journals are kept.
+     * @return The journal.
+     * @throws IOException if there is an IO error creating the file.
+     */
+    static DataChangedJournal newJournal(File journalDirectory) throws IOException {
+        return new DataChangedJournal(
+                File.createTempFile(FILE_NAME_PREFIX, null, journalDirectory));
+    }
+
+    /**
+     * Returns a list of journals in the given journal directory.
+     */
+    static ArrayList<DataChangedJournal> listJournals(File journalDirectory) {
+        ArrayList<DataChangedJournal> journals = new ArrayList<>();
+        for (File file : journalDirectory.listFiles()) {
+            journals.add(new DataChangedJournal(file));
+        }
+        return journals;
+    }
+}
diff --git a/services/backup/java/com/android/server/backup/RefactoredBackupManagerService.java b/services/backup/java/com/android/server/backup/RefactoredBackupManagerService.java
index 7e28f61..d118917 100644
--- a/services/backup/java/com/android/server/backup/RefactoredBackupManagerService.java
+++ b/services/backup/java/com/android/server/backup/RefactoredBackupManagerService.java
@@ -32,6 +32,7 @@
 import static com.android.server.backup.internal.BackupHandler.MSG_RUN_RESTORE;
 import static com.android.server.backup.internal.BackupHandler.MSG_SCHEDULE_BACKUP_PACKAGE;
 
+import android.annotation.Nullable;
 import android.app.ActivityManager;
 import android.app.AlarmManager;
 import android.app.AppGlobals;
@@ -117,13 +118,12 @@
 import com.android.server.backup.utils.AppBackupUtils;
 import com.android.server.backup.utils.BackupManagerMonitorUtils;
 import com.android.server.backup.utils.BackupObserverUtils;
-import com.android.server.backup.utils.PasswordUtils;
+import com.android.server.backup.utils.SparseArrayUtils;
 import com.android.server.power.BatterySaverPolicy.ServiceType;
 
-import libcore.io.IoUtils;
+import com.google.android.collect.Sets;
 
 import java.io.BufferedInputStream;
-import java.io.BufferedOutputStream;
 import java.io.ByteArrayOutputStream;
 import java.io.DataInputStream;
 import java.io.DataOutputStream;
@@ -135,7 +135,6 @@
 import java.io.FileOutputStream;
 import java.io.IOException;
 import java.io.InputStream;
-import java.io.OutputStream;
 import java.io.PrintWriter;
 import java.io.RandomAccessFile;
 import java.security.SecureRandom;
@@ -169,10 +168,6 @@
     // with U+FF00 or higher for system use).
     public static final String KEY_WIDGET_STATE = "\uffed\uffedwidget";
 
-    // Historical and current algorithm names
-    public static final String PBKDF_CURRENT = "PBKDF2WithHmacSHA1";
-    public static final String PBKDF_FALLBACK = "PBKDF2WithHmacSHA1And8bit";
-
     // Name and current contents version of the full-backup manifest file
     //
     // Manifest version history:
@@ -190,7 +185,6 @@
     // 5 : added support for key-value packages
     public static final int BACKUP_FILE_VERSION = 5;
     public static final String BACKUP_FILE_HEADER_MAGIC = "ANDROID BACKUP\n";
-    private static final int BACKUP_PW_FILE_VERSION = 2;
     public static final String BACKUP_METADATA_FILENAME = "_meta";
     public static final int BACKUP_METADATA_VERSION = 1;
     public static final int BACKUP_WIDGET_METADATA_TOKEN = 0x01FFED01;
@@ -283,6 +277,8 @@
     private final Object mClearDataLock = new Object();
     private volatile boolean mClearingData;
 
+    private final BackupPasswordManager mBackupPasswordManager;
+
     @GuardedBy("mPendingRestores")
     private boolean mIsRestoreInProgress;
     @GuardedBy("mPendingRestores")
@@ -482,11 +478,11 @@
         mDataDir = dataDir;
     }
 
-    public File getJournal() {
+    public DataChangedJournal getJournal() {
         return mJournal;
     }
 
-    public void setJournal(File journal) {
+    public void setJournal(@Nullable DataChangedJournal journal) {
         mJournal = journal;
     }
 
@@ -630,20 +626,9 @@
     private File mBaseStateDir;
     private File mDataDir;
     private File mJournalDir;
-    private File mJournal;
+    @Nullable private DataChangedJournal mJournal;
 
-    // Backup password, if any, and the file where it's saved.  What is stored is not the
-    // password text itself; it's the result of a PBKDF2 hash with a randomly chosen (but
-    // persisted) salt.  Validation is performed by running the challenge text through the
-    // same PBKDF2 cycle with the persisted salt; if the resulting derived key string matches
-    // the saved hash string, then the challenge text matches the originally supplied
-    // password text.
     private final SecureRandom mRng = new SecureRandom();
-    private String mPasswordHash;
-    private File mPasswordHashFile;
-    private int mPasswordVersion;
-    private File mPasswordVersionFile;
-    private byte[] mPasswordSalt;
 
     // Keep a log of all the apps we've ever backed up, and what the
     // dataset tokens are for both the current backup dataset and
@@ -745,52 +730,7 @@
         // This dir on /cache is managed directly in init.rc
         mDataDir = new File(Environment.getDownloadCacheDirectory(), "backup_stage");
 
-        mPasswordVersion = 1;       // unless we hear otherwise
-        mPasswordVersionFile = new File(mBaseStateDir, "pwversion");
-        if (mPasswordVersionFile.exists()) {
-            FileInputStream fin = null;
-            DataInputStream in = null;
-            try {
-                fin = new FileInputStream(mPasswordVersionFile);
-                in = new DataInputStream(fin);
-                mPasswordVersion = in.readInt();
-            } catch (IOException e) {
-                Slog.e(TAG, "Unable to read backup pw version");
-            } finally {
-                try {
-                    if (in != null) in.close();
-                    if (fin != null) fin.close();
-                } catch (IOException e) {
-                    Slog.w(TAG, "Error closing pw version files");
-                }
-            }
-        }
-
-        mPasswordHashFile = new File(mBaseStateDir, "pwhash");
-        if (mPasswordHashFile.exists()) {
-            FileInputStream fin = null;
-            DataInputStream in = null;
-            try {
-                fin = new FileInputStream(mPasswordHashFile);
-                in = new DataInputStream(new BufferedInputStream(fin));
-                // integer length of the salt array, followed by the salt,
-                // then the hex pw hash string
-                int saltLen = in.readInt();
-                byte[] salt = new byte[saltLen];
-                in.readFully(salt);
-                mPasswordHash = in.readUTF();
-                mPasswordSalt = salt;
-            } catch (IOException e) {
-                Slog.e(TAG, "Unable to read saved backup pw hash");
-            } finally {
-                try {
-                    if (in != null) in.close();
-                    if (fin != null) fin.close();
-                } catch (IOException e) {
-                    Slog.w(TAG, "Unable to close streams");
-                }
-            }
-        }
+        mBackupPasswordManager = new BackupPasswordManager(mContext, mBaseStateDir, mRng);
 
         // Alarm receivers for scheduled backups & initialization operations
         mRunBackupReceiver = new RunBackupReceiver(this);
@@ -859,8 +799,7 @@
 
         // Remember our ancestral dataset
         mTokenFile = new File(mBaseStateDir, "ancestral");
-        try {
-            RandomAccessFile tf = new RandomAccessFile(mTokenFile, "r");
+        try (RandomAccessFile tf = new RandomAccessFile(mTokenFile, "r")) {
             int version = tf.readInt();
             if (version == CURRENT_ANCESTRAL_RECORD_VERSION) {
                 mAncestralToken = tf.readLong();
@@ -875,7 +814,6 @@
                     }
                 }
             }
-            tf.close();
         } catch (FileNotFoundException fnf) {
             // Probably innocuous
             Slog.v(TAG, "No ancestral data");
@@ -899,13 +837,8 @@
         // If there are previous contents, parse them out then start a new
         // file to continue the recordkeeping.
         if (mEverStored.exists()) {
-            RandomAccessFile temp = null;
-            RandomAccessFile in = null;
-
-            try {
-                temp = new RandomAccessFile(tempProcessedFile, "rws");
-                in = new RandomAccessFile(mEverStored, "r");
-
+            try (RandomAccessFile temp = new RandomAccessFile(tempProcessedFile, "rws");
+                 RandomAccessFile in = new RandomAccessFile(mEverStored, "r")) {
                 // Loop until we hit EOF
                 while (true) {
                     String pkg = in.readUTF();
@@ -929,15 +862,6 @@
                 }
             } catch (IOException e) {
                 Slog.e(TAG, "Error in processed file", e);
-            } finally {
-                try {
-                    if (temp != null) temp.close();
-                } catch (IOException e) {
-                }
-                try {
-                    if (in != null) in.close();
-                } catch (IOException e) {
-                }
             }
         }
 
@@ -968,14 +892,9 @@
                 PackageManagerBackupAgent.getStorableApplications(mPackageManager);
 
         if (mFullBackupScheduleFile.exists()) {
-            FileInputStream fstream = null;
-            BufferedInputStream bufStream = null;
-            DataInputStream in = null;
-            try {
-                fstream = new FileInputStream(mFullBackupScheduleFile);
-                bufStream = new BufferedInputStream(fstream);
-                in = new DataInputStream(bufStream);
-
+            try (FileInputStream fstream = new FileInputStream(mFullBackupScheduleFile);
+                 BufferedInputStream bufStream = new BufferedInputStream(fstream);
+                 DataInputStream in = new DataInputStream(bufStream)) {
                 int version = in.readInt();
                 if (version != SCHEDULE_FILE_VERSION) {
                     Slog.e(TAG, "Unknown backup schedule version " + version);
@@ -1036,10 +955,6 @@
                 Slog.e(TAG, "Unable to read backup schedule", e);
                 mFullBackupScheduleFile.delete();
                 schedule = null;
-            } finally {
-                IoUtils.closeQuietly(in);
-                IoUtils.closeQuietly(bufStream);
-                IoUtils.closeQuietly(fstream);
             }
         }
 
@@ -1105,35 +1020,17 @@
     }
 
     private void parseLeftoverJournals() {
-        for (File f : mJournalDir.listFiles()) {
-            if (mJournal == null || f.compareTo(mJournal) != 0) {
-                // This isn't the current journal, so it must be a leftover.  Read
-                // out the package names mentioned there and schedule them for
-                // backup.
-                DataInputStream in = null;
+        ArrayList<DataChangedJournal> journals = DataChangedJournal.listJournals(mJournalDir);
+        for (DataChangedJournal journal : journals) {
+            if (!journal.equals(mJournal)) {
                 try {
-                    Slog.i(TAG, "Found stale backup journal, scheduling");
-                    // Journals will tend to be on the order of a few kilobytes(around 4k), hence,
-                    // setting the buffer size to 8192.
-                    InputStream bufferedInputStream = new BufferedInputStream(
-                            new FileInputStream(f), 8192);
-                    in = new DataInputStream(bufferedInputStream);
-                    while (true) {
-                        String packageName = in.readUTF();
+                    journal.forEach(packageName -> {
+                        Slog.i(TAG, "Found stale backup journal, scheduling");
                         if (MORE_DEBUG) Slog.i(TAG, "  " + packageName);
                         dataChangedImpl(packageName);
-                    }
-                } catch (EOFException e) {
-                    // no more data; we're done
-                } catch (Exception e) {
-                    Slog.e(TAG, "Can't read " + f, e);
-                } finally {
-                    // close/delete the file
-                    try {
-                        if (in != null) in.close();
-                    } catch (IOException e) {
-                    }
-                    f.delete();
+                    });
+                } catch (IOException e) {
+                    Slog.e(TAG, "Can't read " + journal, e);
                 }
             }
         }
@@ -1146,128 +1043,18 @@
         return array;
     }
 
-    private boolean passwordMatchesSaved(String algorithm, String candidatePw, int rounds) {
-        if (mPasswordHash == null) {
-            // no current password case -- require that 'currentPw' be null or empty
-            if (candidatePw == null || "".equals(candidatePw)) {
-                return true;
-            } // else the non-empty candidate does not match the empty stored pw
-        } else {
-            // hash the stated current pw and compare to the stored one
-            if (candidatePw != null && candidatePw.length() > 0) {
-                String currentPwHash = PasswordUtils.buildPasswordHash(algorithm, candidatePw,
-                        mPasswordSalt,
-                        rounds);
-                if (mPasswordHash.equalsIgnoreCase(currentPwHash)) {
-                    // candidate hash matches the stored hash -- the password matches
-                    return true;
-                }
-            } // else the stored pw is nonempty but the candidate is empty; no match
-        }
-        return false;
-    }
-
     @Override
     public boolean setBackupPassword(String currentPw, String newPw) {
-        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
-                "setBackupPassword");
-
-        // When processing v1 passwords we may need to try two different PBKDF2 checksum regimes
-        final boolean pbkdf2Fallback = (mPasswordVersion < BACKUP_PW_FILE_VERSION);
-
-        // If the supplied pw doesn't hash to the the saved one, fail.  The password
-        // might be caught in the legacy crypto mismatch; verify that too.
-        if (!passwordMatchesSaved(PBKDF_CURRENT, currentPw, PasswordUtils.PBKDF2_HASH_ROUNDS)
-                && !(pbkdf2Fallback && passwordMatchesSaved(PBKDF_FALLBACK,
-                currentPw, PasswordUtils.PBKDF2_HASH_ROUNDS))) {
-            return false;
-        }
-
-        // Snap up to current on the pw file version
-        mPasswordVersion = BACKUP_PW_FILE_VERSION;
-        FileOutputStream pwFout = null;
-        DataOutputStream pwOut = null;
-        try {
-            pwFout = new FileOutputStream(mPasswordVersionFile);
-            pwOut = new DataOutputStream(pwFout);
-            pwOut.writeInt(mPasswordVersion);
-        } catch (IOException e) {
-            Slog.e(TAG, "Unable to write backup pw version; password not changed");
-            return false;
-        } finally {
-            try {
-                if (pwOut != null) pwOut.close();
-                if (pwFout != null) pwFout.close();
-            } catch (IOException e) {
-                Slog.w(TAG, "Unable to close pw version record");
-            }
-        }
-
-        // Clearing the password is okay
-        if (newPw == null || newPw.isEmpty()) {
-            if (mPasswordHashFile.exists()) {
-                if (!mPasswordHashFile.delete()) {
-                    // Unable to delete the old pw file, so fail
-                    Slog.e(TAG, "Unable to clear backup password");
-                    return false;
-                }
-            }
-            mPasswordHash = null;
-            mPasswordSalt = null;
-            return true;
-        }
-
-        try {
-            // Okay, build the hash of the new backup password
-            byte[] salt = randomBytes(PasswordUtils.PBKDF2_SALT_SIZE);
-            String newPwHash = PasswordUtils.buildPasswordHash(PBKDF_CURRENT, newPw, salt,
-                    PasswordUtils.PBKDF2_HASH_ROUNDS);
-
-            OutputStream pwf = null, buffer = null;
-            DataOutputStream out = null;
-            try {
-                pwf = new FileOutputStream(mPasswordHashFile);
-                buffer = new BufferedOutputStream(pwf);
-                out = new DataOutputStream(buffer);
-                // integer length of the salt array, followed by the salt,
-                // then the hex pw hash string
-                out.writeInt(salt.length);
-                out.write(salt);
-                out.writeUTF(newPwHash);
-                out.flush();
-                mPasswordHash = newPwHash;
-                mPasswordSalt = salt;
-                return true;
-            } finally {
-                if (out != null) out.close();
-                if (buffer != null) buffer.close();
-                if (pwf != null) pwf.close();
-            }
-        } catch (IOException e) {
-            Slog.e(TAG, "Unable to set backup password");
-        }
-        return false;
+        return mBackupPasswordManager.setBackupPassword(currentPw, newPw);
     }
 
     @Override
     public boolean hasBackupPassword() {
-        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
-                "hasBackupPassword");
-
-        return mPasswordHash != null && mPasswordHash.length() > 0;
+        return mBackupPasswordManager.hasBackupPassword();
     }
 
     public boolean backupPasswordMatches(String currentPw) {
-        if (hasBackupPassword()) {
-            final boolean pbkdf2Fallback = (mPasswordVersion < BACKUP_PW_FILE_VERSION);
-            if (!passwordMatchesSaved(PBKDF_CURRENT, currentPw, PasswordUtils.PBKDF2_HASH_ROUNDS)
-                    && !(pbkdf2Fallback && passwordMatchesSaved(PBKDF_FALLBACK,
-                    currentPw, PasswordUtils.PBKDF2_HASH_ROUNDS))) {
-                if (DEBUG) Slog.w(TAG, "Backup password mismatch; aborting");
-                return false;
-            }
-        }
-        return true;
+        return mBackupPasswordManager.backupPasswordMatches(currentPw);
     }
 
     // Maintain persistent state around whether need to do an initialize operation.
@@ -1628,18 +1415,11 @@
         synchronized (mEverStoredApps) {
             if (!mEverStoredApps.add(packageName)) return;
 
-            RandomAccessFile out = null;
-            try {
-                out = new RandomAccessFile(mEverStored, "rws");
+            try (RandomAccessFile out = new RandomAccessFile(mEverStored, "rws")) {
                 out.seek(out.length());
                 out.writeUTF(packageName);
             } catch (IOException e) {
                 Slog.e(TAG, "Can't log backup of " + packageName + " to " + mEverStored);
-            } finally {
-                try {
-                    if (out != null) out.close();
-                } catch (IOException e) {
-                }
             }
         }
     }
@@ -1654,16 +1434,13 @@
             // we'll recognize on initialization time that the package no longer
             // exists and fix it up then.
             File tempKnownFile = new File(mBaseStateDir, "processed.new");
-            RandomAccessFile known = null;
-            try {
-                known = new RandomAccessFile(tempKnownFile, "rws");
+            try (RandomAccessFile known = new RandomAccessFile(tempKnownFile, "rws")) {
                 mEverStoredApps.remove(packageName);
                 for (String s : mEverStoredApps) {
                     known.writeUTF(s);
                     if (MORE_DEBUG) Slog.v(TAG, "    " + s);
                 }
                 known.close();
-                known = null;
                 if (!tempKnownFile.renameTo(mEverStored)) {
                     throw new IOException("Can't rename " + tempKnownFile + " to " + mEverStored);
                 }
@@ -1676,11 +1453,6 @@
                 mEverStoredApps.clear();
                 tempKnownFile.delete();
                 mEverStored.delete();
-            } finally {
-                try {
-                    if (known != null) known.close();
-                } catch (IOException e) {
-                }
             }
         }
     }
@@ -1689,9 +1461,7 @@
     // as the set of packages with data [supposedly] available in the
     // ancestral dataset.
     public void writeRestoreTokens() {
-        try {
-            RandomAccessFile af = new RandomAccessFile(mTokenFile, "rwd");
-
+        try (RandomAccessFile af = new RandomAccessFile(mTokenFile, "rwd")) {
             // First, the version number of this record, for futureproofing
             af.writeInt(CURRENT_ANCESTRAL_RECORD_VERSION);
 
@@ -1710,7 +1480,6 @@
                     if (MORE_DEBUG) Slog.v(TAG, "   " + pkgName);
                 }
             }
-            af.close();
         } catch (IOException e) {
             Slog.w(TAG, "Unable to write token file:", e);
         }
@@ -2489,38 +2258,22 @@
         }
 
         // a caller with full permission can ask to back up any participating app
-        HashSet<String> targets = new HashSet<>();
         if (PACKAGE_MANAGER_SENTINEL.equals(packageName)) {
-            targets.add(PACKAGE_MANAGER_SENTINEL);
+            return Sets.newHashSet(PACKAGE_MANAGER_SENTINEL);
         } else {
             synchronized (mBackupParticipants) {
-                int N = mBackupParticipants.size();
-                for (int i = 0; i < N; i++) {
-                    HashSet<String> s = mBackupParticipants.valueAt(i);
-                    if (s != null) {
-                        targets.addAll(s);
-                    }
-                }
+                return SparseArrayUtils.union(mBackupParticipants);
             }
         }
-        return targets;
     }
 
     private void writeToJournalLocked(String str) {
-        RandomAccessFile out = null;
         try {
-            if (mJournal == null) mJournal = File.createTempFile("journal", null, mJournalDir);
-            out = new RandomAccessFile(mJournal, "rws");
-            out.seek(out.length());
-            out.writeUTF(str);
+            if (mJournal == null) mJournal = DataChangedJournal.newJournal(mJournalDir);
+            mJournal.addPackage(str);
         } catch (IOException e) {
             Slog.e(TAG, "Can't write " + str + " to backup journal", e);
             mJournal = null;
-        } finally {
-            try {
-                if (out != null) out.close();
-            } catch (IOException e) {
-            }
         }
     }
 
@@ -2595,14 +2348,7 @@
             // a caller with full permission can ask to back up any participating app
             // !!! TODO: allow data-clear of ANY app?
             if (MORE_DEBUG) Slog.v(TAG, "Privileged caller, allowing clear of other apps");
-            apps = new HashSet<>();
-            int N = mBackupParticipants.size();
-            for (int i = 0; i < N; i++) {
-                HashSet<String> s = mBackupParticipants.valueAt(i);
-                if (s != null) {
-                    apps.addAll(s);
-                }
-            }
+            apps = SparseArrayUtils.union(mBackupParticipants);
         }
 
         // Is the given app an available participant?
@@ -2978,9 +2724,7 @@
         File base = new File(Environment.getDataDirectory(), "backup");
         File enableFile = new File(base, BACKUP_ENABLE_FILE);
         File stage = new File(base, BACKUP_ENABLE_FILE + "-stage");
-        FileOutputStream fout = null;
-        try {
-            fout = new FileOutputStream(stage);
+        try (FileOutputStream fout = new FileOutputStream(stage)) {
             fout.write(enable ? 1 : 0);
             fout.close();
             stage.renameTo(enableFile);
@@ -2996,8 +2740,6 @@
                     Settings.Secure.BACKUP_ENABLED, null, userId);
             enableFile.delete();
             stage.delete();
-        } finally {
-            IoUtils.closeQuietly(fout);
         }
     }
 
diff --git a/services/backup/java/com/android/server/backup/fullbackup/PerformAdbBackupTask.java b/services/backup/java/com/android/server/backup/fullbackup/PerformAdbBackupTask.java
index 007d930..804e92c 100644
--- a/services/backup/java/com/android/server/backup/fullbackup/PerformAdbBackupTask.java
+++ b/services/backup/java/com/android/server/backup/fullbackup/PerformAdbBackupTask.java
@@ -16,11 +16,11 @@
 
 package com.android.server.backup.fullbackup;
 
+import static com.android.server.backup.BackupPasswordManager.PBKDF_CURRENT;
 import static com.android.server.backup.RefactoredBackupManagerService.BACKUP_FILE_HEADER_MAGIC;
 import static com.android.server.backup.RefactoredBackupManagerService.BACKUP_FILE_VERSION;
 import static com.android.server.backup.RefactoredBackupManagerService.DEBUG;
 import static com.android.server.backup.RefactoredBackupManagerService.MORE_DEBUG;
-import static com.android.server.backup.RefactoredBackupManagerService.PBKDF_CURRENT;
 import static com.android.server.backup.RefactoredBackupManagerService.SHARED_BACKUP_AGENT_PACKAGE;
 import static com.android.server.backup.RefactoredBackupManagerService.TAG;
 
diff --git a/services/backup/java/com/android/server/backup/internal/BackupHandler.java b/services/backup/java/com/android/server/backup/internal/BackupHandler.java
index edd3894..8f82300 100644
--- a/services/backup/java/com/android/server/backup/internal/BackupHandler.java
+++ b/services/backup/java/com/android/server/backup/internal/BackupHandler.java
@@ -36,6 +36,7 @@
 import com.android.internal.backup.IBackupTransport;
 import com.android.server.EventLogTags;
 import com.android.server.backup.BackupRestoreTask;
+import com.android.server.backup.DataChangedJournal;
 import com.android.server.backup.RefactoredBackupManagerService;
 import com.android.server.backup.fullbackup.PerformAdbBackupTask;
 import com.android.server.backup.fullbackup.PerformFullTransportBackupTask;
@@ -107,7 +108,7 @@
 
                 // snapshot the pending-backup set and work on that
                 ArrayList<BackupRequest> queue = new ArrayList<>();
-                File oldJournal = backupManagerService.getJournal();
+                DataChangedJournal oldJournal = backupManagerService.getJournal();
                 synchronized (backupManagerService.getQueueLock()) {
                     // Do we have any work to do?  Construct the work queue
                     // then release the synchronization lock to actually run
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 a996e2d..5d4fcf4 100644
--- a/services/backup/java/com/android/server/backup/internal/PerformBackupTask.java
+++ b/services/backup/java/com/android/server/backup/internal/PerformBackupTask.java
@@ -28,6 +28,7 @@
 import static com.android.server.backup.internal.BackupHandler.MSG_BACKUP_OPERATION_TIMEOUT;
 import static com.android.server.backup.internal.BackupHandler.MSG_BACKUP_RESTORE_STEP;
 
+import android.annotation.Nullable;
 import android.app.ApplicationThreadConstants;
 import android.app.IBackupAgent;
 import android.app.backup.BackupDataInput;
@@ -57,6 +58,7 @@
 import com.android.server.AppWidgetBackupBridge;
 import com.android.server.EventLogTags;
 import com.android.server.backup.BackupRestoreTask;
+import com.android.server.backup.DataChangedJournal;
 import com.android.server.backup.KeyValueBackupJob;
 import com.android.server.backup.PackageManagerBackupAgent;
 import com.android.server.backup.RefactoredBackupManagerService;
@@ -114,7 +116,7 @@
     ArrayList<BackupRequest> mQueue;
     ArrayList<BackupRequest> mOriginalQueue;
     File mStateDir;
-    File mJournal;
+    @Nullable DataChangedJournal mJournal;
     BackupState mCurrentState;
     List<String> mPendingFullBackups;
     IBackupObserver mObserver;
@@ -142,9 +144,9 @@
 
     public PerformBackupTask(RefactoredBackupManagerService backupManagerService,
             IBackupTransport transport, String dirName,
-            ArrayList<BackupRequest> queue, File journal, IBackupObserver observer,
-            IBackupManagerMonitor monitor, List<String> pendingFullBackups,
-            boolean userInitiated, boolean nonIncremental) {
+            ArrayList<BackupRequest> queue, @Nullable DataChangedJournal journal,
+            IBackupObserver observer, IBackupManagerMonitor monitor,
+            List<String> pendingFullBackups, boolean userInitiated, boolean nonIncremental) {
         this.backupManagerService = backupManagerService;
         mTransport = transport;
         mOriginalQueue = queue;
diff --git a/services/backup/java/com/android/server/backup/restore/PerformAdbRestoreTask.java b/services/backup/java/com/android/server/backup/restore/PerformAdbRestoreTask.java
index b1d6afc..62ae065 100644
--- a/services/backup/java/com/android/server/backup/restore/PerformAdbRestoreTask.java
+++ b/services/backup/java/com/android/server/backup/restore/PerformAdbRestoreTask.java
@@ -16,6 +16,8 @@
 
 package com.android.server.backup.restore;
 
+import static com.android.server.backup.BackupPasswordManager.PBKDF_CURRENT;
+import static com.android.server.backup.BackupPasswordManager.PBKDF_FALLBACK;
 import static com.android.server.backup.RefactoredBackupManagerService.BACKUP_FILE_HEADER_MAGIC;
 import static com.android.server.backup.RefactoredBackupManagerService.BACKUP_FILE_VERSION;
 import static com.android.server.backup.RefactoredBackupManagerService.BACKUP_MANIFEST_FILENAME;
@@ -23,8 +25,6 @@
 import static com.android.server.backup.RefactoredBackupManagerService.DEBUG;
 import static com.android.server.backup.RefactoredBackupManagerService.MORE_DEBUG;
 import static com.android.server.backup.RefactoredBackupManagerService.OP_TYPE_RESTORE_WAIT;
-import static com.android.server.backup.RefactoredBackupManagerService.PBKDF_CURRENT;
-import static com.android.server.backup.RefactoredBackupManagerService.PBKDF_FALLBACK;
 import static com.android.server.backup.RefactoredBackupManagerService.SETTINGS_PACKAGE;
 import static com.android.server.backup.RefactoredBackupManagerService.SHARED_BACKUP_AGENT_PACKAGE;
 import static com.android.server.backup.RefactoredBackupManagerService.TAG;
diff --git a/services/backup/java/com/android/server/backup/utils/DataStreamCodec.java b/services/backup/java/com/android/server/backup/utils/DataStreamCodec.java
new file mode 100644
index 0000000..b1e226d
--- /dev/null
+++ b/services/backup/java/com/android/server/backup/utils/DataStreamCodec.java
@@ -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
+ */
+
+package com.android.server.backup.utils;
+
+import java.io.DataInputStream;
+import java.io.DataOutputStream;
+import java.io.IOException;
+
+/**
+ * Implements how to serialize a {@code T} to a {@link DataOutputStream} and how to deserialize a
+ * {@code T} from a {@link DataInputStream}.
+ *
+ * @param <T> Type of object to be serialized / deserialized.
+ */
+public interface DataStreamCodec<T> {
+    /**
+     * Serializes {@code t} to {@code dataOutputStream}.
+     */
+    void serialize(T t, DataOutputStream dataOutputStream) throws IOException;
+
+    /**
+     * Deserializes {@code t} from {@code dataInputStream}.
+     */
+    T deserialize(DataInputStream dataInputStream) throws IOException;
+}
+
diff --git a/services/backup/java/com/android/server/backup/utils/DataStreamFileCodec.java b/services/backup/java/com/android/server/backup/utils/DataStreamFileCodec.java
new file mode 100644
index 0000000..7753b03
--- /dev/null
+++ b/services/backup/java/com/android/server/backup/utils/DataStreamFileCodec.java
@@ -0,0 +1,78 @@
+/*
+ * 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.backup.utils;
+
+import java.io.BufferedOutputStream;
+import java.io.DataInputStream;
+import java.io.DataOutputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+
+/**
+ * Provides an interface for serializing an object to a file and deserializing it back again.
+ *
+ * <p>Serialization logic is implemented as a {@link DataStreamCodec}.
+ *
+ * @param <T> The type of object to serialize / deserialize.
+ */
+public final class DataStreamFileCodec<T> {
+    private final File mFile;
+    private final DataStreamCodec<T> mCodec;
+
+    /**
+     * Constructs an instance to serialize to or deserialize from the given file, with the given
+     * serialization / deserialization strategy.
+     */
+    public DataStreamFileCodec(File file, DataStreamCodec<T> codec) {
+        mFile = file;
+        mCodec = codec;
+    }
+
+    /**
+     * Deserializes a {@code T} from the file, automatically closing input streams.
+     *
+     * @return The deserialized object.
+     * @throws IOException if an IO error occurred.
+     */
+    public T deserialize() throws IOException {
+        try (
+            FileInputStream fileInputStream = new FileInputStream(mFile);
+            DataInputStream dataInputStream = new DataInputStream(fileInputStream)
+        ) {
+            return mCodec.deserialize(dataInputStream);
+        }
+    }
+
+    /**
+     * Serializes {@code t} to the file, automatically flushing and closing output streams.
+     *
+     * @param t The object to serialize.
+     * @throws IOException if an IO error occurs.
+     */
+    public void serialize(T t) throws IOException {
+        try (
+            FileOutputStream fileOutputStream = new FileOutputStream(mFile);
+            BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(fileOutputStream);
+            DataOutputStream dataOutputStream = new DataOutputStream(bufferedOutputStream)
+        ) {
+            mCodec.serialize(t, dataOutputStream);
+            dataOutputStream.flush();
+        }
+    }
+}
diff --git a/services/backup/java/com/android/server/backup/utils/PasswordUtils.java b/services/backup/java/com/android/server/backup/utils/PasswordUtils.java
index 12fc927..9c5e283 100644
--- a/services/backup/java/com/android/server/backup/utils/PasswordUtils.java
+++ b/services/backup/java/com/android/server/backup/utils/PasswordUtils.java
@@ -123,8 +123,7 @@
             int rounds) {
         try {
             SecretKeyFactory keyFactory = SecretKeyFactory.getInstance(algorithm);
-            KeySpec
-                    ks = new PBEKeySpec(pwArray, salt, rounds, PBKDF2_KEY_SIZE);
+            KeySpec ks = new PBEKeySpec(pwArray, salt, rounds, PBKDF2_KEY_SIZE);
             return keyFactory.generateSecret(ks);
         } catch (InvalidKeySpecException e) {
             Slog.e(TAG, "Invalid key spec for PBKDF2!");
diff --git a/services/backup/java/com/android/server/backup/utils/SparseArrayUtils.java b/services/backup/java/com/android/server/backup/utils/SparseArrayUtils.java
new file mode 100644
index 0000000..954d714
--- /dev/null
+++ b/services/backup/java/com/android/server/backup/utils/SparseArrayUtils.java
@@ -0,0 +1,49 @@
+/*
+ * 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.backup.utils;
+
+import android.util.SparseArray;
+
+import java.util.HashSet;
+
+/**
+ * Helper functions for manipulating instances of {@link SparseArray}.
+ */
+public final class SparseArrayUtils {
+    // Statics only
+    private SparseArrayUtils() {}
+
+    /**
+     * Given a {@link SparseArray<HashSet>}, returns a new {@link HashSet} containing every element
+     * from every set in the array.
+     *
+     * @param sets The array of sets from which to take the union.
+     * @param <V> The type of element contained in the set.
+     * @return The complete set.
+     */
+    public static<V> HashSet<V> union(SparseArray<HashSet<V>> sets) {
+        HashSet<V> unionSet = new HashSet<>();
+        int n = sets.size();
+        for (int i = 0; i < n; i++) {
+            HashSet<V> ithSet = sets.valueAt(i);
+            if (ithSet != null) {
+                unionSet.addAll(ithSet);
+            }
+        }
+        return unionSet;
+    }
+}
diff --git a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
index f47b0d3..f2f01cf 100644
--- a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
+++ b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
@@ -57,6 +57,7 @@
 import android.provider.Settings;
 import android.provider.SettingsStringUtil.ComponentNameSet;
 import android.text.BidiFormatter;
+import android.util.ArraySet;
 import android.util.AtomicFile;
 import android.util.ExceptionUtils;
 import android.util.Log;
@@ -83,6 +84,7 @@
 import java.util.ArrayList;
 import java.util.List;
 import java.util.Objects;
+import java.util.Set;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.ConcurrentMap;
 import java.util.function.Function;
@@ -247,9 +249,9 @@
                 throws RemoteException {
             checkCallerIsSystemOr(callingPackage, userId);
             checkUsesFeature(callingPackage, getCallingUserId());
-            return CollectionUtils.map(
+            return new ArrayList<>(CollectionUtils.map(
                     readAllAssociations(userId, callingPackage),
-                    a -> a.deviceAddress);
+                    a -> a.deviceAddress));
         }
 
         //TODO also revoke notification access
@@ -495,20 +497,20 @@
                 new Association(userId, deviceAddress, priviledgedPackage)));
     }
 
-    private void updateAssociations(Function<List<Association>, List<Association>> update) {
+    private void updateAssociations(Function<Set<Association>, Set<Association>> update) {
         updateAssociations(update, getCallingUserId());
     }
 
-    private void updateAssociations(Function<List<Association>, List<Association>> update,
+    private void updateAssociations(Function<Set<Association>, Set<Association>> update,
             int userId) {
         final AtomicFile file = getStorageFileForUser(userId);
         synchronized (file) {
-            List<Association> associations = readAllAssociations(userId);
-            final List<Association> old = CollectionUtils.copyOf(associations);
+            Set<Association> associations = readAllAssociations(userId);
+            final Set<Association> old = CollectionUtils.copyOf(associations);
             associations = update.apply(associations);
             if (size(old) == size(associations)) return;
 
-            List<Association> finalAssociations = associations;
+            Set<Association> finalAssociations = associations;
             file.write((out) -> {
                 XmlSerializer xml = Xml.newSerializer();
                 try {
@@ -517,13 +519,12 @@
                     xml.startDocument(null, true);
                     xml.startTag(null, XML_TAG_ASSOCIATIONS);
 
-                    for (int i = 0; i < size(finalAssociations); i++) {
-                        Association association = finalAssociations.get(i);
+                    CollectionUtils.forEach(finalAssociations, association -> {
                         xml.startTag(null, XML_TAG_ASSOCIATION)
-                            .attribute(null, XML_ATTR_PACKAGE, association.companionAppPackage)
-                            .attribute(null, XML_ATTR_DEVICE, association.deviceAddress)
-                            .endTag(null, XML_TAG_ASSOCIATION);
-                    }
+                                .attribute(null, XML_ATTR_PACKAGE, association.companionAppPackage)
+                                .attribute(null, XML_ATTR_DEVICE, association.deviceAddress)
+                                .endTag(null, XML_TAG_ASSOCIATION);
+                    });
 
                     xml.endTag(null, XML_TAG_ASSOCIATIONS);
                     xml.endDocument();
@@ -545,17 +546,17 @@
     }
 
     @Nullable
-    private ArrayList<Association> readAllAssociations(int userId) {
+    private Set<Association> readAllAssociations(int userId) {
         return readAllAssociations(userId, null);
     }
 
     @Nullable
-    private ArrayList<Association> readAllAssociations(int userId, @Nullable String packageFilter) {
+    private Set<Association> readAllAssociations(int userId, @Nullable String packageFilter) {
         final AtomicFile file = getStorageFileForUser(userId);
 
         if (!file.getBaseFile().exists()) return null;
 
-        ArrayList<Association> result = null;
+        ArraySet<Association> result = null;
         final XmlPullParser parser = Xml.newPullParser();
         synchronized (file) {
             try (FileInputStream in = file.openRead()) {
@@ -627,12 +628,10 @@
         public int onCommand(String cmd) {
             switch (cmd) {
                 case "list": {
-                    ArrayList<Association> associations = readAllAssociations(getNextArgInt());
-                    for (int i = 0; i < size(associations); i++) {
-                        Association a = associations.get(i);
-                        getOutPrintWriter()
-                                .println(a.companionAppPackage + " " + a.deviceAddress);
-                    }
+                    CollectionUtils.forEach(
+                            readAllAssociations(getNextArgInt()),
+                            a -> getOutPrintWriter()
+                                    .println(a.companionAppPackage + " " + a.deviceAddress));
                 } break;
 
                 case "associate": {
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index a2e74b6..71c423c 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -40,7 +40,6 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
-import android.content.pm.PackageManager;
 import android.content.res.Configuration;
 import android.database.ContentObserver;
 import android.net.ConnectivityManager;
@@ -52,10 +51,10 @@
 import android.net.INetworkStatsService;
 import android.net.LinkProperties;
 import android.net.LinkProperties.CompareResult;
+import android.net.MatchAllNetworkSpecifier;
 import android.net.Network;
 import android.net.NetworkAgent;
 import android.net.NetworkCapabilities;
-import android.net.MatchAllNetworkSpecifier;
 import android.net.NetworkConfig;
 import android.net.NetworkInfo;
 import android.net.NetworkInfo.DetailedState;
@@ -124,13 +123,12 @@
 import com.android.internal.util.MessageUtils;
 import com.android.internal.util.WakeupMessage;
 import com.android.internal.util.XmlUtils;
-import com.android.server.LocalServices;
 import com.android.server.am.BatteryStatsService;
 import com.android.server.connectivity.DataConnectionStats;
 import com.android.server.connectivity.KeepaliveTracker;
+import com.android.server.connectivity.LingerMonitor;
 import com.android.server.connectivity.MockableSystemProperties;
 import com.android.server.connectivity.Nat464Xlat;
-import com.android.server.connectivity.LingerMonitor;
 import com.android.server.connectivity.NetworkAgentInfo;
 import com.android.server.connectivity.NetworkDiagnostics;
 import com.android.server.connectivity.NetworkMonitor;
@@ -139,8 +137,8 @@
 import com.android.server.connectivity.PacManager;
 import com.android.server.connectivity.PermissionMonitor;
 import com.android.server.connectivity.Tethering;
-import com.android.server.connectivity.tethering.TetheringDependencies;
 import com.android.server.connectivity.Vpn;
+import com.android.server.connectivity.tethering.TetheringDependencies;
 import com.android.server.net.BaseNetworkObserver;
 import com.android.server.net.LockdownVpnTracker;
 import com.android.server.net.NetworkPolicyManagerInternal;
@@ -1036,8 +1034,7 @@
     /**
      * Apply any relevant filters to {@link NetworkState} for the given UID. For
      * example, this may mark the network as {@link DetailedState#BLOCKED} based
-     * on {@link #isNetworkWithLinkPropertiesBlocked}, or
-     * {@link NetworkInfo#isMetered()} based on network policies.
+     * on {@link #isNetworkWithLinkPropertiesBlocked}.
      */
     private void filterNetworkStateForUid(NetworkState state, int uid, boolean ignoreBlocked) {
         if (state == null || state.networkInfo == null || state.linkProperties == null) return;
@@ -1048,15 +1045,6 @@
         if (mLockdownTracker != null) {
             mLockdownTracker.augmentNetworkInfo(state.networkInfo);
         }
-
-        // TODO: apply metered state closer to NetworkAgentInfo
-        final long token = Binder.clearCallingIdentity();
-        try {
-            state.networkInfo.setMetered(mPolicyManager.isNetworkMetered(state));
-        } catch (RemoteException e) {
-        } finally {
-            Binder.restoreCallingIdentity(token);
-        }
     }
 
     /**
@@ -1326,30 +1314,24 @@
     }
 
     @Override
+    @Deprecated
     public NetworkQuotaInfo getActiveNetworkQuotaInfo() {
-        enforceAccessPermission();
-        final int uid = Binder.getCallingUid();
-        final long token = Binder.clearCallingIdentity();
-        try {
-            final NetworkState state = getUnfilteredActiveNetworkState(uid);
-            if (state.networkInfo != null) {
-                try {
-                    return mPolicyManager.getNetworkQuotaInfo(state);
-                } catch (RemoteException e) {
-                }
-            }
-            return null;
-        } finally {
-            Binder.restoreCallingIdentity(token);
-        }
+        Log.w(TAG, "Shame on UID " + Binder.getCallingUid()
+                + " for calling the hidden API getNetworkQuotaInfo(). Shame!");
+        return new NetworkQuotaInfo();
     }
 
     @Override
     public boolean isActiveNetworkMetered() {
         enforceAccessPermission();
 
-        final NetworkInfo info = getActiveNetworkInfo();
-        return (info != null) ? info.isMetered() : false;
+        final NetworkCapabilities caps = getNetworkCapabilities(getActiveNetwork());
+        if (caps != null) {
+            return !caps.hasCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED);
+        } else {
+            // Always return the most conservative value
+            return true;
+        }
     }
 
     private INetworkManagementEventObserver mDataActivityObserver = new BaseNetworkObserver() {
@@ -1510,6 +1492,12 @@
         ConnectivityManager.enforceChangePermission(mContext);
     }
 
+    private void enforceSettingsPermission() {
+        mContext.enforceCallingOrSelfPermission(
+                android.Manifest.permission.NETWORK_SETTINGS,
+                "ConnectivityService");
+    }
+
     private void enforceTetherAccessPermission() {
         mContext.enforceCallingOrSelfPermission(
                 android.Manifest.permission.ACCESS_NETWORK_STATE,
@@ -2759,7 +2747,8 @@
         enforceAccessPermission();
 
         NetworkAgentInfo nai = getNetworkAgentInfoForNetwork(network);
-        if (nai != null && !nai.networkInfo.isMetered()) {
+        if (nai != null && nai.networkCapabilities
+                .hasCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED)) {
             return ConnectivityManager.MULTIPATH_PREFERENCE_UNMETERED;
         }
 
@@ -3640,6 +3629,21 @@
     }
 
     @Override
+    public boolean isAlwaysOnVpnPackageSupported(int userId, String packageName) {
+        enforceSettingsPermission();
+        enforceCrossUserPermission(userId);
+
+        synchronized (mVpns) {
+            Vpn vpn = mVpns.get(userId);
+            if (vpn == null) {
+                Slog.w(TAG, "User " + userId + " has no Vpn configuration");
+                return false;
+            }
+            return vpn.isAlwaysOnPackageSupported(packageName);
+        }
+    }
+
+    @Override
     public boolean setAlwaysOnVpnPackage(int userId, String packageName, boolean lockdown) {
         enforceConnectivityInternalPermission();
         enforceCrossUserPermission(userId);
diff --git a/services/core/java/com/android/server/IpSecService.java b/services/core/java/com/android/server/IpSecService.java
index b88bbc1..ac5da93 100644
--- a/services/core/java/com/android/server/IpSecService.java
+++ b/services/core/java/com/android/server/IpSecService.java
@@ -206,7 +206,11 @@
 
         T get(int key) {
             T val = mArray.get(key);
-            val.checkOwnerOrSystemAndThrow();
+            // The value should never be null unless the resource doesn't exist
+            // (since we do not allow null resources to be added).
+            if (val != null) {
+                val.checkOwnerOrSystemAndThrow();
+            }
             return val;
         }
 
@@ -402,17 +406,14 @@
 
     private void connectNativeNetdService() {
         // Avoid blocking the system server to do this
-        Thread t =
-                new Thread(
-                        new Runnable() {
-                            @Override
-                            public void run() {
-                                synchronized (IpSecService.this) {
-                                    NetdService.get(NETD_FETCH_TIMEOUT);
-                                }
-                            }
-                        });
-        t.run();
+        new Thread() {
+            @Override
+            public void run() {
+                synchronized (IpSecService.this) {
+                    NetdService.get(NETD_FETCH_TIMEOUT);
+                }
+            }
+        }.start();
     }
 
     INetd getNetdInstance() throws RemoteException {
diff --git a/services/core/java/com/android/server/NetworkManagementService.java b/services/core/java/com/android/server/NetworkManagementService.java
index 8ea334d..7959e39 100644
--- a/services/core/java/com/android/server/NetworkManagementService.java
+++ b/services/core/java/com/android/server/NetworkManagementService.java
@@ -18,6 +18,7 @@
 
 import static android.Manifest.permission.CONNECTIVITY_INTERNAL;
 import static android.Manifest.permission.DUMP;
+import static android.Manifest.permission.NETWORK_STACK;
 import static android.Manifest.permission.SHUTDOWN;
 import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_DOZABLE;
 import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_NAME_DOZABLE;
@@ -55,6 +56,7 @@
 import android.net.ConnectivityManager;
 import android.net.INetd;
 import android.net.INetworkManagementEventObserver;
+import android.net.ITetheringStatsProvider;
 import android.net.InterfaceConfiguration;
 import android.net.IpPrefix;
 import android.net.LinkAddress;
@@ -225,6 +227,10 @@
 
     private final NetworkStatsFactory mStatsFactory = new NetworkStatsFactory();
 
+    @GuardedBy("mTetheringStatsProviders")
+    private final HashMap<ITetheringStatsProvider, String>
+            mTetheringStatsProviders = Maps.newHashMap();
+
     /**
      * If both locks need to be held, then they should be obtained in the order:
      * first {@link #mQuotaLock} and then {@link #mRulesLock}.
@@ -331,6 +337,10 @@
         Watchdog.getInstance().addMonitor(this);
 
         LocalServices.addService(NetworkManagementInternal.class, new LocalService());
+
+        synchronized (mTetheringStatsProviders) {
+            mTetheringStatsProviders.put(new NetdTetheringStatsProvider(), "netd");
+        }
     }
 
     @VisibleForTesting
@@ -520,6 +530,23 @@
         }
     }
 
+    @Override
+    public void registerTetheringStatsProvider(ITetheringStatsProvider provider, String name) {
+        mContext.enforceCallingOrSelfPermission(NETWORK_STACK, TAG);
+        Preconditions.checkNotNull(provider);
+        synchronized(mTetheringStatsProviders) {
+            mTetheringStatsProviders.put(provider, name);
+        }
+    }
+
+    @Override
+    public void unregisterTetheringStatsProvider(ITetheringStatsProvider provider) {
+        mContext.enforceCallingOrSelfPermission(NETWORK_STACK, TAG);
+        synchronized(mTetheringStatsProviders) {
+            mTetheringStatsProviders.remove(provider);
+        }
+    }
+
     // Sync the state of the given chain with the native daemon.
     private void syncFirewallChainLocked(int chain, String name) {
         SparseIntArray rules;
@@ -1789,14 +1816,16 @@
         }
     }
 
-    @Override
-    public NetworkStats getNetworkStatsTethering() {
-        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
-
-        final NetworkStats stats = new NetworkStats(SystemClock.elapsedRealtime(), 1);
-        try {
-            final NativeDaemonEvent[] events = mConnector.executeForList(
-                    "bandwidth", "gettetherstats");
+    private class NetdTetheringStatsProvider extends ITetheringStatsProvider.Stub {
+        @Override
+        public NetworkStats getTetherStats() {
+            final NativeDaemonEvent[] events;
+            try {
+                events = mConnector.executeForList("bandwidth", "gettetherstats");
+            } catch (NativeDaemonConnectorException e) {
+                throw e.rethrowAsParcelableException();
+            }
+            final NetworkStats stats = new NetworkStats(SystemClock.elapsedRealtime(), 1);
             for (NativeDaemonEvent event : events) {
                 if (event.getCode() != TetheringStatsListResult) continue;
 
@@ -1822,8 +1851,24 @@
                     throw new IllegalStateException("problem parsing tethering stats: " + event);
                 }
             }
-        } catch (NativeDaemonConnectorException e) {
-            throw e.rethrowAsParcelableException();
+            return stats;
+        }
+    }
+
+    @Override
+    public NetworkStats getNetworkStatsTethering() {
+        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
+
+        final NetworkStats stats = new NetworkStats(SystemClock.elapsedRealtime(), 1);
+        synchronized (mTetheringStatsProviders) {
+            for (ITetheringStatsProvider provider: mTetheringStatsProviders.keySet()) {
+                try {
+                    stats.combineAllValues(provider.getTetherStats());
+                } catch (RemoteException e) {
+                    Log.e(TAG, "Problem reading tethering stats from " +
+                            mTetheringStatsProviders.get(provider) + ": " + e);
+                }
+            }
         }
         return stats;
     }
diff --git a/services/core/java/com/android/server/SystemServiceManager.java b/services/core/java/com/android/server/SystemServiceManager.java
index 72ff606..581914d 100644
--- a/services/core/java/com/android/server/SystemServiceManager.java
+++ b/services/core/java/com/android/server/SystemServiceManager.java
@@ -18,6 +18,7 @@
 
 import android.annotation.NonNull;
 import android.content.Context;
+import android.os.SystemClock;
 import android.os.Trace;
 import android.util.Slog;
 
@@ -118,14 +119,14 @@
         // Register it.
         mServices.add(service);
         // Start it.
-        long time = System.currentTimeMillis();
+        long time = SystemClock.elapsedRealtime();
         try {
             service.onStart();
         } catch (RuntimeException ex) {
             throw new RuntimeException("Failed to start service " + service.getClass().getName()
                     + ": onStart threw an exception", ex);
         }
-        warnIfTooLong(System.currentTimeMillis() - time, service, "onStart");
+        warnIfTooLong(SystemClock.elapsedRealtime() - time, service, "onStart");
     }
 
     /**
@@ -146,7 +147,7 @@
             final int serviceLen = mServices.size();
             for (int i = 0; i < serviceLen; i++) {
                 final SystemService service = mServices.get(i);
-                long time = System.currentTimeMillis();
+                long time = SystemClock.elapsedRealtime();
                 Trace.traceBegin(Trace.TRACE_TAG_SYSTEM_SERVER, service.getClass().getName());
                 try {
                     service.onBootPhase(mCurrentPhase);
@@ -156,7 +157,7 @@
                             + ": onBootPhase threw an exception during phase "
                             + mCurrentPhase, ex);
                 }
-                warnIfTooLong(System.currentTimeMillis() - time, service, "onBootPhase");
+                warnIfTooLong(SystemClock.elapsedRealtime() - time, service, "onBootPhase");
                 Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER);
             }
         } finally {
@@ -178,14 +179,14 @@
             final SystemService service = mServices.get(i);
             Trace.traceBegin(Trace.TRACE_TAG_SYSTEM_SERVER, "onStartUser "
                     + service.getClass().getName());
-            long time = System.currentTimeMillis();
+            long time = SystemClock.elapsedRealtime();
             try {
                 service.onStartUser(userHandle);
             } catch (Exception ex) {
                 Slog.wtf(TAG, "Failure reporting start of user " + userHandle
                         + " to service " + service.getClass().getName(), ex);
             }
-            warnIfTooLong(System.currentTimeMillis() - time, service, "onStartUser ");
+            warnIfTooLong(SystemClock.elapsedRealtime() - time, service, "onStartUser ");
             Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER);
         }
     }
@@ -197,14 +198,14 @@
             final SystemService service = mServices.get(i);
             Trace.traceBegin(Trace.TRACE_TAG_SYSTEM_SERVER, "onUnlockUser "
                     + service.getClass().getName());
-            long time = System.currentTimeMillis();
+            long time = SystemClock.elapsedRealtime();
             try {
                 service.onUnlockUser(userHandle);
             } catch (Exception ex) {
                 Slog.wtf(TAG, "Failure reporting unlock of user " + userHandle
                         + " to service " + service.getClass().getName(), ex);
             }
-            warnIfTooLong(System.currentTimeMillis() - time, service, "onUnlockUser ");
+            warnIfTooLong(SystemClock.elapsedRealtime() - time, service, "onUnlockUser ");
             Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER);
         }
     }
@@ -216,14 +217,14 @@
             final SystemService service = mServices.get(i);
             Trace.traceBegin(Trace.TRACE_TAG_SYSTEM_SERVER, "onSwitchUser "
                     + service.getClass().getName());
-            long time = System.currentTimeMillis();
+            long time = SystemClock.elapsedRealtime();
             try {
                 service.onSwitchUser(userHandle);
             } catch (Exception ex) {
                 Slog.wtf(TAG, "Failure reporting switch of user " + userHandle
                         + " to service " + service.getClass().getName(), ex);
             }
-            warnIfTooLong(System.currentTimeMillis() - time, service, "onSwitchUser");
+            warnIfTooLong(SystemClock.elapsedRealtime() - time, service, "onSwitchUser");
             Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER);
         }
     }
@@ -235,14 +236,14 @@
             final SystemService service = mServices.get(i);
             Trace.traceBegin(Trace.TRACE_TAG_SYSTEM_SERVER, "onStopUser "
                     + service.getClass().getName());
-            long time = System.currentTimeMillis();
+            long time = SystemClock.elapsedRealtime();
             try {
                 service.onStopUser(userHandle);
             } catch (Exception ex) {
                 Slog.wtf(TAG, "Failure reporting stop of user " + userHandle
                         + " to service " + service.getClass().getName(), ex);
             }
-            warnIfTooLong(System.currentTimeMillis() - time, service, "onStopUser");
+            warnIfTooLong(SystemClock.elapsedRealtime() - time, service, "onStopUser");
             Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER);
         }
     }
@@ -254,14 +255,14 @@
             final SystemService service = mServices.get(i);
             Trace.traceBegin(Trace.TRACE_TAG_SYSTEM_SERVER, "onCleanupUser "
                     + service.getClass().getName());
-            long time = System.currentTimeMillis();
+            long time = SystemClock.elapsedRealtime();
             try {
                 service.onCleanupUser(userHandle);
             } catch (Exception ex) {
                 Slog.wtf(TAG, "Failure reporting cleanup of user " + userHandle
                         + " to service " + service.getClass().getName(), ex);
             }
-            warnIfTooLong(System.currentTimeMillis() - time, service, "onCleanupUser");
+            warnIfTooLong(SystemClock.elapsedRealtime() - time, service, "onCleanupUser");
             Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER);
         }
     }
diff --git a/services/core/java/com/android/server/TextServicesManagerService.java b/services/core/java/com/android/server/TextServicesManagerService.java
index 6d9d874..80d39f5 100644
--- a/services/core/java/com/android/server/TextServicesManagerService.java
+++ b/services/core/java/com/android/server/TextServicesManagerService.java
@@ -857,7 +857,7 @@
             Slog.e(TAG, "Remove the spell checker bind unexpectedly.");
             synchronized (mLock) {
                 final int size = mListeners.getRegisteredCallbackCount();
-                for (int i = 0; i < size; ++i) {
+                for (int i = size - 1; i >= 0; --i) {
                     mListeners.unregister(mListeners.getRegisteredCallbackItem(i));
                 }
                 mPendingSessionRequests.clear();
diff --git a/services/core/java/com/android/server/VibratorService.java b/services/core/java/com/android/server/VibratorService.java
index c68e5d6..4733840 100644
--- a/services/core/java/com/android/server/VibratorService.java
+++ b/services/core/java/com/android/server/VibratorService.java
@@ -48,6 +48,7 @@
 import android.os.WorkSource;
 import android.provider.Settings;
 import android.provider.Settings.SettingNotFoundException;
+import android.util.DebugUtils;
 import android.util.Slog;
 import android.view.InputDevice;
 import android.media.AudioAttributes;
@@ -60,10 +61,7 @@
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
 import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Iterator;
 import java.util.LinkedList;
-import java.util.ListIterator;
 
 public class VibratorService extends IVibratorService.Stub
         implements InputManager.InputDeviceListener {
@@ -948,6 +946,21 @@
         }
 
         private int runVibrate() {
+            try {
+                final int zenMode = Settings.Global.getInt(mContext.getContentResolver(),
+                        Settings.Global.ZEN_MODE);
+                if (zenMode != Settings.Global.ZEN_MODE_OFF) {
+                    try (PrintWriter pw = getOutPrintWriter();) {
+                        pw.print("Ignoring because device is on DND mode ");
+                        pw.println(DebugUtils.flagsToString(Settings.Global.class, "ZEN_MODE_",
+                                zenMode));
+                        return 0;
+                    }
+                }
+            } catch (SettingNotFoundException e) {
+                // ignore
+            }
+
             final long duration = Long.parseLong(getNextArgRequired());
             if (duration > MAX_VIBRATION_MS) {
                 throw new IllegalArgumentException("maximum duration is " + MAX_VIBRATION_MS);
@@ -972,7 +985,8 @@
                 pw.println("    Prints this help text.");
                 pw.println("");
                 pw.println("  vibrate duration [description]");
-                pw.println("    Vibrates for duration milliseconds.");
+                pw.println("    Vibrates for duration milliseconds; ignored when device is on DND ");
+                pw.println("    (Do Not Disturb) mode.");
                 pw.println("");
             }
         }
diff --git a/services/core/java/com/android/server/Watchdog.java b/services/core/java/com/android/server/Watchdog.java
index b18fa32..aceedf1 100644
--- a/services/core/java/com/android/server/Watchdog.java
+++ b/services/core/java/com/android/server/Watchdog.java
@@ -85,8 +85,9 @@
         "android.hardware.bluetooth@1.0::IBluetoothHci",
         "android.hardware.camera.provider@2.4::ICameraProvider",
         "android.hardware.graphics.composer@2.1::IComposer",
-        "android.hardware.vr@1.0::IVr",
-        "android.hardware.media.omx@1.0::IOmx"
+        "android.hardware.media.omx@1.0::IOmx",
+        "android.hardware.sensors@1.0::ISensors",
+        "android.hardware.vr@1.0::IVr"
     );
 
     static Watchdog sWatchdog;
diff --git a/services/core/java/com/android/server/accounts/AccountManagerService.java b/services/core/java/com/android/server/accounts/AccountManagerService.java
index a54fe72..c6f2fc0 100644
--- a/services/core/java/com/android/server/accounts/AccountManagerService.java
+++ b/services/core/java/com/android/server/accounts/AccountManagerService.java
@@ -5180,6 +5180,28 @@
 
             fout.println();
             mAuthenticatorCache.dump(fd, fout, args, userAccounts.userId);
+
+            boolean isUserUnlocked;
+            synchronized (mUsers) {
+                isUserUnlocked = isLocalUnlockedUser(userAccounts.userId);
+            }
+            // Following logs are printed only when user is unlocked.
+            if (!isUserUnlocked) {
+                return;
+            }
+            fout.println();
+            synchronized (userAccounts.dbLock) {
+                Map<Account, Map<String, Integer>> allVisibilityValues =
+                        userAccounts.accountsDb.findAllVisibilityValues();
+                fout.println("Account visibility:");
+                for (Account account : allVisibilityValues.keySet()) {
+                    fout.println("  " + account.name);
+                    Map<String, Integer> visibilities = allVisibilityValues.get(account);
+                    for (Entry<String, Integer> entry : visibilities.entrySet()) {
+                        fout.println("    " + entry.getKey() + ", " + entry.getValue());
+                    }
+                }
+            }
         }
     }
 
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 04df104..ec0e595 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -6715,7 +6715,6 @@
             mActiveUids.put(proc.uid, uidRec);
             EventLogTags.writeAmUidRunning(uidRec.uid);
             noteUidProcessState(uidRec.uid, uidRec.curProcState);
-            enqueueUidChangeLocked(uidRec, -1, UidRecord.CHANGE_ACTIVE);
         }
         proc.uidRecord = uidRec;
 
@@ -12499,10 +12498,10 @@
         switch (mWakefulness) {
             case PowerManagerInternal.WAKEFULNESS_AWAKE:
             case PowerManagerInternal.WAKEFULNESS_DREAMING:
-            case PowerManagerInternal.WAKEFULNESS_DOZING:
                 // Pause applications whenever the lock screen is shown or any sleep
                 // tokens have been acquired.
                 return mKeyguardController.isKeyguardShowing() || !mSleepTokens.isEmpty();
+            case PowerManagerInternal.WAKEFULNESS_DOZING:
             case PowerManagerInternal.WAKEFULNESS_ASLEEP:
             default:
                 // If we're asleep then pause applications unconditionally.
@@ -17368,23 +17367,41 @@
             ArrayList<MemItem> catMems = new ArrayList<MemItem>();
 
             catMems.add(new MemItem("Native", "Native", nativePss, nativeSwapPss, -1));
-            final MemItem dalvikItem =
-                    new MemItem("Dalvik", "Dalvik", dalvikPss, dalvikSwapPss, -2);
-            if (dalvikSubitemPss.length > 0) {
-                dalvikItem.subitems = new ArrayList<MemItem>();
-                for (int j=0; j<dalvikSubitemPss.length; j++) {
-                    final String name = Debug.MemoryInfo.getOtherLabel(
-                            Debug.MemoryInfo.NUM_OTHER_STATS + j);
-                    dalvikItem.subitems.add(new MemItem(name, name, dalvikSubitemPss[j],
-                                    dalvikSubitemSwapPss[j], j));
-                }
-            }
-            catMems.add(dalvikItem);
+            final int dalvikId = -2;
+            catMems.add(new MemItem("Dalvik", "Dalvik", dalvikPss, dalvikSwapPss, dalvikId));
             catMems.add(new MemItem("Unknown", "Unknown", otherPss, otherSwapPss, -3));
             for (int j=0; j<Debug.MemoryInfo.NUM_OTHER_STATS; j++) {
                 String label = Debug.MemoryInfo.getOtherLabel(j);
                 catMems.add(new MemItem(label, label, miscPss[j], miscSwapPss[j], j));
             }
+            if (dalvikSubitemPss.length > 0) {
+                // Add dalvik subitems.
+                for (MemItem memItem : catMems) {
+                    int memItemStart = 0, memItemEnd = 0;
+                    if (memItem.id == dalvikId) {
+                        memItemStart = Debug.MemoryInfo.OTHER_DVK_STAT_DALVIK_START;
+                        memItemEnd = Debug.MemoryInfo.OTHER_DVK_STAT_DALVIK_END;
+                    } else if (memItem.id == Debug.MemoryInfo.OTHER_DALVIK_OTHER) {
+                        memItemStart = Debug.MemoryInfo.OTHER_DVK_STAT_DALVIK_OTHER_START;
+                        memItemEnd = Debug.MemoryInfo.OTHER_DVK_STAT_DALVIK_OTHER_END;
+                    } else if (memItem.id == Debug.MemoryInfo.OTHER_DEX) {
+                        memItemStart = Debug.MemoryInfo.OTHER_DVK_STAT_DEX_START;
+                        memItemEnd = Debug.MemoryInfo.OTHER_DVK_STAT_DEX_END;
+                    } else if (memItem.id == Debug.MemoryInfo.OTHER_ART) {
+                        memItemStart = Debug.MemoryInfo.OTHER_DVK_STAT_ART_START;
+                        memItemEnd = Debug.MemoryInfo.OTHER_DVK_STAT_ART_END;
+                    } else {
+                        continue;  // No subitems, continue.
+                    }
+                    memItem.subitems = new ArrayList<MemItem>();
+                    for (int j=memItemStart; j<=memItemEnd; j++) {
+                        final String name = Debug.MemoryInfo.getOtherLabel(
+                                Debug.MemoryInfo.NUM_OTHER_STATS + j);
+                        memItem.subitems.add(new MemItem(name, name, dalvikSubitemPss[j],
+                                dalvikSubitemSwapPss[j], j));
+                    }
+                }
+            }
 
             ArrayList<MemItem> oomMems = new ArrayList<MemItem>();
             for (int j=0; j<oomPss.length; j++) {
@@ -21631,7 +21648,8 @@
                                         if (DEBUG_PSS) Slog.d(TAG_PSS,
                                                 "Requesting dump heap from "
                                                 + myProc + " to " + heapdumpFile);
-                                        thread.dumpHeap(/* managed=*/ true, /* runGc= */ false,
+                                        thread.dumpHeap(/* managed= */ true,
+                                                /* mallocInfo= */ false, /* runGc= */ false,
                                                 heapdumpFile.toString(), fd);
                                     } catch (RemoteException e) {
                                     }
@@ -22807,8 +22825,9 @@
         for (int i=mActiveUids.size()-1; i>=0; i--) {
             final UidRecord uidRec = mActiveUids.valueAt(i);
             int uidChange = UidRecord.CHANGE_PROCSTATE;
-            if (uidRec.setProcState != uidRec.curProcState
-                    || uidRec.setWhitelist != uidRec.curWhitelist) {
+            if (uidRec.curProcState != ActivityManager.PROCESS_STATE_NONEXISTENT
+                    && (uidRec.setProcState != uidRec.curProcState
+                           || uidRec.setWhitelist != uidRec.curWhitelist)) {
                 if (DEBUG_UID_OBSERVERS) Slog.i(TAG_UID_OBSERVERS,
                         "Changes in " + uidRec + ": proc state from " + uidRec.setProcState
                         + " to " + uidRec.curProcState + ", whitelist from " + uidRec.setWhitelist
@@ -22829,7 +22848,7 @@
                                     mConstants.BACKGROUND_SETTLE_TIME);
                         }
                     }
-                    if (!uidRec.setIdle) {
+                    if (uidRec.idle && !uidRec.setIdle) {
                         uidChange = UidRecord.CHANGE_IDLE;
                     }
                 } else {
@@ -23394,8 +23413,8 @@
         return proc;
     }
 
-    public boolean dumpHeap(String process, int userId, boolean managed, boolean runGc,
-            String path, ParcelFileDescriptor fd) throws RemoteException {
+    public boolean dumpHeap(String process, int userId, boolean managed, boolean mallocInfo,
+            boolean runGc, String path, ParcelFileDescriptor fd) throws RemoteException {
 
         try {
             synchronized (this) {
@@ -23423,7 +23442,7 @@
                     }
                 }
 
-                proc.thread.dumpHeap(managed, runGc, path, fd);
+                proc.thread.dumpHeap(managed, mallocInfo, runGc, path, fd);
                 fd = null;
                 return true;
             }
@@ -23492,6 +23511,9 @@
             }
             if (DEBUG_PSS) Slog.d(TAG_PSS, "Dump heap finished for " + path);
             mHandler.sendEmptyMessage(POST_DUMP_HEAP_NOTIFICATION_MSG);
+
+            // Forced gc to clean up the remnant hprof fd.
+            Runtime.getRuntime().gc();
         }
     }
 
diff --git a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
index 5d24296..9273b3c 100644
--- a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
+++ b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
@@ -23,6 +23,7 @@
 import android.app.IActivityController;
 import android.app.IActivityManager;
 import android.app.IStopUserCallback;
+import android.app.IUidObserver;
 import android.app.ProfilerInfo;
 import android.app.WaitResult;
 import android.app.usage.ConfigurationStats;
@@ -184,6 +185,8 @@
                     return runMakeIdle(pw);
                 case "monitor":
                     return runMonitor(pw);
+                case "watch-uids":
+                    return runWatchUids(pw);
                 case "hang":
                     return runHang(pw);
                 case "restart":
@@ -784,6 +787,7 @@
     int runDumpHeap(PrintWriter pw) throws RemoteException {
         final PrintWriter err = getErrPrintWriter();
         boolean managed = true;
+        boolean mallocInfo = false;
         int userId = UserHandle.USER_CURRENT;
         boolean runGc = false;
 
@@ -799,6 +803,9 @@
                 managed = false;
             } else if (opt.equals("-g")) {
                 runGc = true;
+            } else if (opt.equals("-m")) {
+                managed = false;
+                mallocInfo = true;
             } else {
                 err.println("Error: Unknown option: " + opt);
                 return -1;
@@ -814,7 +821,7 @@
             return -1;
         }
 
-        if (!mInterface.dumpHeap(process, userId, managed, runGc, heapFile, fd)) {
+        if (!mInterface.dumpHeap(process, userId, managed, mallocInfo, runGc, heapFile, fd)) {
             err.println("HEAP DUMP FAILED on process " + process);
             return -1;
         }
@@ -1290,6 +1297,141 @@
         return 0;
     }
 
+    static final class MyUidObserver extends IUidObserver.Stub {
+        final IActivityManager mInterface;
+        final PrintWriter mPw;
+        final InputStream mInput;
+
+        static final int STATE_NORMAL = 0;
+
+        int mState;
+
+        MyUidObserver(IActivityManager iam, PrintWriter pw, InputStream input) {
+            mInterface = iam;
+            mPw = pw;
+            mInput = input;
+        }
+
+        @Override
+        public void onUidStateChanged(int uid, int procState, long procStateSeq) throws RemoteException {
+            synchronized (this) {
+                mPw.print(uid);
+                mPw.print(" procstate ");
+                mPw.print(ProcessList.makeProcStateString(procState));
+                mPw.print(" seq ");
+                mPw.println(procStateSeq);
+                mPw.flush();
+            }
+        }
+
+        @Override
+        public void onUidGone(int uid, boolean disabled) throws RemoteException {
+            synchronized (this) {
+                mPw.print(uid);
+                mPw.print(" gone");
+                if (disabled) {
+                    mPw.print(" disabled");
+                }
+                mPw.println();
+                mPw.flush();
+            }
+        }
+
+        @Override
+        public void onUidActive(int uid) throws RemoteException {
+            synchronized (this) {
+                mPw.print(uid);
+                mPw.println(" active");
+                mPw.flush();
+            }
+        }
+
+        @Override
+        public void onUidIdle(int uid, boolean disabled) throws RemoteException {
+            synchronized (this) {
+                mPw.print(uid);
+                mPw.print(" idle");
+                if (disabled) {
+                    mPw.print(" disabled");
+                }
+                mPw.println();
+                mPw.flush();
+            }
+        }
+
+        @Override
+        public void onUidCachedChanged(int uid, boolean cached) throws RemoteException {
+            synchronized (this) {
+                mPw.print(uid);
+                mPw.println(cached ? " cached" : " uncached");
+                mPw.flush();
+            }
+        }
+
+        void printMessageForState() {
+            switch (mState) {
+                case STATE_NORMAL:
+                    mPw.println("Watching uid states...  available commands:");
+                    break;
+            }
+            mPw.println("(q)uit: finish watching");
+        }
+
+        void run() throws RemoteException {
+            try {
+                printMessageForState();
+                mPw.flush();
+
+                mInterface.registerUidObserver(this, ActivityManager.UID_OBSERVER_ACTIVE
+                        | ActivityManager.UID_OBSERVER_GONE | ActivityManager.UID_OBSERVER_PROCSTATE
+                        | ActivityManager.UID_OBSERVER_IDLE | ActivityManager.UID_OBSERVER_CACHED,
+                        ActivityManager.PROCESS_STATE_UNKNOWN, null);
+                mState = STATE_NORMAL;
+
+                InputStreamReader converter = new InputStreamReader(mInput);
+                BufferedReader in = new BufferedReader(converter);
+                String line;
+
+                while ((line = in.readLine()) != null) {
+                    boolean addNewline = true;
+                    if (line.length() <= 0) {
+                        addNewline = false;
+                    } else if ("q".equals(line) || "quit".equals(line)) {
+                        break;
+                    } else {
+                        mPw.println("Invalid command: " + line);
+                    }
+
+                    synchronized (this) {
+                        if (addNewline) {
+                            mPw.println("");
+                        }
+                        printMessageForState();
+                        mPw.flush();
+                    }
+                }
+
+            } catch (IOException e) {
+                e.printStackTrace(mPw);
+                mPw.flush();
+            } finally {
+                mInterface.unregisterUidObserver(this);
+            }
+        }
+    }
+
+    int runWatchUids(PrintWriter pw) throws RemoteException {
+        String opt;
+        while ((opt=getNextOption()) != null) {
+            getErrPrintWriter().println("Error: Unknown option: " + opt);
+            return -1;
+        }
+
+        MyUidObserver controller = new MyUidObserver(mInterface, pw, getRawInputStream());
+        controller.run();
+        return 0;
+    }
+
     int runHang(PrintWriter pw) throws RemoteException {
         String opt;
         boolean allowRestart = false;
@@ -2595,6 +2737,8 @@
             pw.println("  monitor [--gdb <port>]");
             pw.println("      Start monitoring for crashes or ANRs.");
             pw.println("      --gdb: start gdbserv on the given port at crash/ANR");
+            pw.println("  watch-uids [--gdb <port>]");
+            pw.println("      Start watching for and reporting uid state changes.");
             pw.println("  hang [--allow-restart]");
             pw.println("      Hang the system.");
             pw.println("      --allow-restart: allow watchdog to perform normal system restart");
diff --git a/services/core/java/com/android/server/am/ActivityRecord.java b/services/core/java/com/android/server/am/ActivityRecord.java
index 4a57369..6f68c7a 100644
--- a/services/core/java/com/android/server/am/ActivityRecord.java
+++ b/services/core/java/com/android/server/am/ActivityRecord.java
@@ -42,6 +42,7 @@
 import static android.content.Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS;
 import static android.content.Intent.FLAG_ACTIVITY_NO_HISTORY;
 import static android.content.pm.ActivityInfo.CONFIG_ORIENTATION;
+import static android.content.pm.ActivityInfo.CONFIG_ROTATION;
 import static android.content.pm.ActivityInfo.CONFIG_SCREEN_LAYOUT;
 import static android.content.pm.ActivityInfo.CONFIG_SCREEN_SIZE;
 import static android.content.pm.ActivityInfo.CONFIG_SMALLEST_SCREEN_SIZE;
@@ -76,6 +77,7 @@
 import static android.os.Build.VERSION_CODES.O;
 import static android.os.Process.SYSTEM_UID;
 import static android.os.Trace.TRACE_TAG_ACTIVITY_MANAGER;
+import static android.view.WindowManagerPolicy.NAV_BAR_LEFT;
 
 import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_CONFIGURATION;
 import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_SAVED_STATE;
@@ -155,6 +157,7 @@
 import android.view.IApplicationToken;
 import android.view.WindowManager.LayoutParams;
 
+import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.app.ResolverActivity;
 import com.android.internal.content.ReferrerIntent;
 import com.android.internal.util.XmlUtils;
@@ -2303,10 +2306,12 @@
         outBounds.setEmpty();
         final float maxAspectRatio = info.maxAspectRatio;
         final ActivityStack stack = getStack();
-        if (task == null || stack == null || !task.mFullscreen || maxAspectRatio == 0) {
+        if (task == null || stack == null || !task.mFullscreen || maxAspectRatio == 0
+                || isInVrUiMode(getConfiguration())) {
             // We don't set override configuration if that activity task isn't fullscreen. I.e. the
             // activity is in multi-window mode. Or, there isn't a max aspect ratio specified for
-            // the activity. This is indicated by an empty {@link outBounds}.
+            // the activity. This is indicated by an empty {@link outBounds}. We also don't set it
+            // if we are in VR mode.
             return;
         }
 
@@ -2342,6 +2347,17 @@
 
         // Compute configuration based on max supported width and height.
         outBounds.set(0, 0, maxActivityWidth, maxActivityHeight);
+        // Position the activity frame on the opposite side of the nav bar.
+        final int navBarPosition = service.mWindowManager.getNavBarPosition();
+        final int left = navBarPosition == NAV_BAR_LEFT
+                ? configuration.appBounds.right - outBounds.width() : 0;
+        outBounds.offsetTo(left, 0 /* top */);
+    }
+
+    /** Get bounds of the activity. */
+    @VisibleForTesting
+    Rect getBounds() {
+        return new Rect(mBounds);
     }
 
     /**
@@ -2565,6 +2581,10 @@
                 changes &= ~CONFIG_SMALLEST_SCREEN_SIZE;
             }
         }
+        // We don't want rotation to cause relaunches.
+        if ((changes & CONFIG_ROTATION) != 0) {
+            changes &= ~CONFIG_ROTATION;
+        }
         return changes;
     }
 
diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java
index 070e028..ab76529 100644
--- a/services/core/java/com/android/server/am/ActivityStack.java
+++ b/services/core/java/com/android/server/am/ActivityStack.java
@@ -731,6 +731,13 @@
         return null;
     }
 
+    final TaskRecord bottomTask() {
+        if (mTaskHistory.isEmpty()) {
+            return null;
+        }
+        return mTaskHistory.get(0);
+    }
+
     TaskRecord taskForIdLocked(int id) {
         for (int taskNdx = mTaskHistory.size() - 1; taskNdx >= 0; --taskNdx) {
             final TaskRecord task = mTaskHistory.get(taskNdx);
@@ -1867,7 +1874,14 @@
                         // the recents activity from an app.
                         behindFullscreenActivity = true;
                     }
-
+                } else if (mStackId == FULLSCREEN_WORKSPACE_STACK_ID) {
+                    if (DEBUG_VISIBILITY) Slog.v(TAG_VISIBILITY, "Skipping after task=" + task
+                            + " returning to non-application type=" + task.getTaskToReturnTo());
+                    // Once we reach a fullscreen task that should return to another task, then no
+                    // other activities behind that one should be visible.
+                    if (task.getTaskToReturnTo() != APPLICATION_ACTIVITY_TYPE) {
+                        behindFullscreenActivity = true;
+                    }
                 }
             }
 
@@ -2853,10 +2867,13 @@
                         transit = TRANSIT_TASK_OPEN_BEHIND;
                     } else {
                         // If a new task is being launched, then mark the existing top activity as
-                        // supporting picture-in-picture while pausing
+                        // supporting picture-in-picture while pausing only if the starting activity
+                        // would not be considered an overlay on top of the current activity
+                        // (eg. not fullscreen, or the assistant)
                         if (focusedTopActivity != null
                                 && focusedTopActivity.getStackId() != PINNED_STACK_ID
-                                && r.getStackId() != ASSISTANT_STACK_ID) {
+                                && r.getStackId() != ASSISTANT_STACK_ID
+                                && r.fullscreen) {
                             focusedTopActivity.supportsPictureInPictureWhilePausing = true;
                         }
                         transit = TRANSIT_TASK_OPEN;
@@ -3338,6 +3355,16 @@
      * @param allowFocusSelf Is the focus allowed to remain on the same stack.
      */
     private boolean adjustFocusToNextFocusableStackLocked(String reason, boolean allowFocusSelf) {
+        if (isAssistantStack() && bottomTask() != null &&
+                bottomTask().getTaskToReturnTo() == HOME_ACTIVITY_TYPE) {
+            // If the current stack is the assistant stack, then use the return-to type to determine
+            // whether to return to the home screen. This is needed to workaround an issue where
+            // launching a fullscreen task (and subequently returning from that task) will cause
+            // the fullscreen stack to be found as the next focusable stack below, even if the
+            // assistant was launched over home.
+            return mStackSupervisor.moveHomeStackTaskToTop(reason);
+        }
+
         final ActivityStack stack = mStackSupervisor.getNextFocusableStackLocked(
                 allowFocusSelf ? null : this);
         final String myReason = reason + " adjustFocusToNextFocusableStack";
@@ -4467,9 +4494,10 @@
             updateTransitLocked(TRANSIT_TASK_TO_FRONT, options);
         }
         // If a new task is moved to the front, then mark the existing top activity as supporting
-        // picture-in-picture while paused
+        // picture-in-picture while paused only if the task would not be considered an oerlay on top
+        // of the current activity (eg. not fullscreen, or the assistant)
         if (topActivity != null && topActivity.getStackId() != PINNED_STACK_ID
-                && tr.getStackId() != ASSISTANT_STACK_ID) {
+                && tr.getStackId() != ASSISTANT_STACK_ID && tr.containsOnlyFullscreenActivities()) {
             topActivity.supportsPictureInPictureWhilePausing = true;
         }
 
diff --git a/services/core/java/com/android/server/am/ActivityStarter.java b/services/core/java/com/android/server/am/ActivityStarter.java
index a31c33e..4f04066 100644
--- a/services/core/java/com/android/server/am/ActivityStarter.java
+++ b/services/core/java/com/android/server/am/ActivityStarter.java
@@ -1640,6 +1640,16 @@
                                 REPARENT_MOVE_STACK_TO_FRONT, ANIMATE, DEFER_RESUME,
                                 "reparentToDisplay");
                         mMovedToFront = true;
+                    } else if (launchStack.getStackId() == StackId.HOME_STACK_ID
+                        && mTargetStack.getStackId() != StackId.HOME_STACK_ID) {
+                        // It is possible for the home activity to be in another stack initially.
+                        // For example, the activity may have been initially started with an intent
+                        // which placed it in the fullscreen stack. To ensure the proper handling of
+                        // the activity based on home stack assumptions, we must move it over.
+                        intentActivity.getTask().reparent(launchStack.mStackId, ON_TOP,
+                                REPARENT_MOVE_STACK_TO_FRONT, ANIMATE, DEFER_RESUME,
+                                "reparentingHome");
+                        mMovedToFront = true;
                     }
                     mOptions = null;
 
diff --git a/services/core/java/com/android/server/am/KeyguardController.java b/services/core/java/com/android/server/am/KeyguardController.java
index 372d80d..58e71df 100644
--- a/services/core/java/com/android/server/am/KeyguardController.java
+++ b/services/core/java/com/android/server/am/KeyguardController.java
@@ -143,6 +143,7 @@
             failCallback(callback);
             return;
         }
+        Slog.i(TAG, "Activity requesting to dismiss Keyguard: " + activityRecord);
 
         // If the client has requested to dismiss the keyguard and the Activity has the flag to
         // turn the screen on, wakeup the screen if it's the top Activity.
diff --git a/services/core/java/com/android/server/am/TaskRecord.java b/services/core/java/com/android/server/am/TaskRecord.java
index 4f211e3..96d8573 100644
--- a/services/core/java/com/android/server/am/TaskRecord.java
+++ b/services/core/java/com/android/server/am/TaskRecord.java
@@ -1112,6 +1112,19 @@
         return intent != null ? intent : affinityIntent;
     }
 
+    /**
+     * @return Whether there are only fullscreen activities in this task.
+     */
+    boolean containsOnlyFullscreenActivities() {
+        for (int i = 0; i < mActivities.size(); i++) {
+            final ActivityRecord r = mActivities.get(i);
+            if (!r.finishing && !r.fullscreen) {
+                return false;
+            }
+        }
+        return true;
+    }
+
     /** Returns the first non-finishing activity from the root. */
     ActivityRecord getRootActivity() {
         for (int i = 0; i < mActivities.size(); i++) {
diff --git a/services/core/java/com/android/server/camera/CameraServiceProxy.java b/services/core/java/com/android/server/camera/CameraServiceProxy.java
index d155825..3133a51 100644
--- a/services/core/java/com/android/server/camera/CameraServiceProxy.java
+++ b/services/core/java/com/android/server/camera/CameraServiceProxy.java
@@ -21,6 +21,7 @@
 import android.content.IntentFilter;
 import android.hardware.ICameraService;
 import android.hardware.ICameraServiceProxy;
+import android.metrics.LogMaker;
 import android.nfc.INfcAdapter;
 import android.os.Binder;
 import android.os.Handler;
@@ -28,15 +29,23 @@
 import android.os.Message;
 import android.os.Process;
 import android.os.RemoteException;
+import android.os.SystemClock;
 import android.os.SystemProperties;
 import android.os.UserManager;
+import android.util.ArrayMap;
 import android.util.ArraySet;
 import android.util.Slog;
 
+import com.android.internal.logging.MetricsLogger;
+import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
+import com.android.server.LocalServices;
 import com.android.server.ServiceThread;
 import com.android.server.SystemService;
 
+import java.util.ArrayList;
 import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
 import java.util.Set;
 
 /**
@@ -56,12 +65,6 @@
 
     public static final String CAMERA_SERVICE_PROXY_BINDER_NAME = "media.camera.proxy";
 
-    // State arguments to use with the notifyCameraState call from camera service:
-    public static final int CAMERA_STATE_OPEN = 0;
-    public static final int CAMERA_STATE_ACTIVE = 1;
-    public static final int CAMERA_STATE_IDLE = 2;
-    public static final int CAMERA_STATE_CLOSED = 3;
-
     // Flags arguments to NFC adapter to enable/disable NFC
     public static final int DISABLE_POLLING_FLAGS = 0x1000;
     public static final int ENABLE_POLLING_FLAGS = 0x0000;
@@ -71,6 +74,9 @@
 
     private static final int RETRY_DELAY_TIME = 20; //ms
 
+    // Maximum entries to keep in usage history before dumping out
+    private static final int MAX_USAGE_HISTORY = 100;
+
     private final Context mContext;
     private final ServiceThread mHandlerThread;
     private final Handler mHandler;
@@ -82,14 +88,52 @@
 
     private ICameraService mCameraServiceRaw;
 
-    private final ArraySet<String> mActiveCameraIds = new ArraySet<>();
-
+    private final ArrayMap<String, CameraUsageEvent> mActiveCameraUsage = new ArrayMap<>();
+    private final List<CameraUsageEvent> mCameraUsageHistory = new ArrayList<>();
+    private final MetricsLogger mLogger = new MetricsLogger();
     private static final String NFC_NOTIFICATION_PROP = "ro.camera.notify_nfc";
     private static final String NFC_SERVICE_BINDER_NAME = "nfc";
     private static final IBinder nfcInterfaceToken = new Binder();
 
     private final boolean mNotifyNfc;
-    private int mActiveCameraCount = 0;
+
+    /**
+     * Structure to track camera usage
+     */
+    private static class CameraUsageEvent {
+        public final int mCameraFacing;
+        public final String mClientName;
+
+        private boolean mCompleted;
+        private long mDurationOrStartTimeMs;  // Either start time, or duration once completed
+
+        public CameraUsageEvent(int facing, String clientName) {
+            mCameraFacing = facing;
+            mClientName = clientName;
+            mDurationOrStartTimeMs = SystemClock.elapsedRealtime();
+            mCompleted = false;
+        }
+
+        public void markCompleted() {
+            if (mCompleted) {
+                return;
+            }
+            mCompleted = true;
+            mDurationOrStartTimeMs = SystemClock.elapsedRealtime() - mDurationOrStartTimeMs;
+            if (CameraServiceProxy.DEBUG) {
+                Slog.v(TAG, "A camera facing " + cameraFacingToString(mCameraFacing) +
+                        " was in use by " + mClientName + " for " +
+                        mDurationOrStartTimeMs + " ms");
+            }
+        }
+
+        /**
+         * Return duration of camera usage event, or 0 if the event is not done
+         */
+        public long getDuration() {
+            return mCompleted ? mDurationOrStartTimeMs : 0;
+        }
+    }
 
     private final BroadcastReceiver mIntentReceiver = new BroadcastReceiver() {
         @Override
@@ -123,11 +167,14 @@
         }
 
         @Override
-        public void notifyCameraState(String cameraId, int newCameraState) {
+        public void notifyCameraState(String cameraId, int newCameraState, int facing,
+                String clientName) {
             String state = cameraStateToString(newCameraState);
-            if (DEBUG) Slog.v(TAG, "Camera " + cameraId + " state now " + state);
+            String facingStr = cameraFacingToString(facing);
+            if (DEBUG) Slog.v(TAG, "Camera " + cameraId + " facing " + facingStr + " state now " +
+                    state + " for client " + clientName);
 
-            updateActivityCount(cameraId, newCameraState);
+            updateActivityCount(cameraId, newCameraState, facing, clientName);
         }
     };
 
@@ -173,6 +220,9 @@
         mContext.registerReceiver(mIntentReceiver, filter);
 
         publishBinderService(CAMERA_SERVICE_PROXY_BINDER_NAME, mCameraServiceProxy);
+        publishLocalService(CameraServiceProxy.class, this);
+
+        CameraStatsJobService.schedule(mContext);
     }
 
     @Override
@@ -202,8 +252,8 @@
             mCameraServiceRaw = null;
 
             // All cameras reset to idle on camera service death
-            boolean wasEmpty = mActiveCameraIds.isEmpty();
-            mActiveCameraIds.clear();
+            boolean wasEmpty = mActiveCameraUsage.isEmpty();
+            mActiveCameraUsage.clear();
 
             if ( mNotifyNfc && !wasEmpty ) {
                 notifyNfcService(/*enablePolling*/ true);
@@ -211,6 +261,46 @@
         }
     }
 
+    /**
+     * Dump camera usage events to log.
+     * Package-private
+     */
+    void dumpUsageEvents() {
+        synchronized(mLock) {
+            // Randomize order of events so that it's not meaningful
+            Collections.shuffle(mCameraUsageHistory);
+            for (CameraUsageEvent e : mCameraUsageHistory) {
+                if (DEBUG) {
+                    Slog.v(TAG, "Camera: " + e.mClientName + " used a camera facing " +
+                            cameraFacingToString(e.mCameraFacing) + " for " +
+                            e.getDuration() + " ms");
+                }
+                int subtype = 0;
+                switch(e.mCameraFacing) {
+                    case ICameraServiceProxy.CAMERA_FACING_BACK:
+                        subtype = MetricsEvent.CAMERA_BACK_USED;
+                        break;
+                    case ICameraServiceProxy.CAMERA_FACING_FRONT:
+                        subtype = MetricsEvent.CAMERA_FRONT_USED;
+                        break;
+                    case ICameraServiceProxy.CAMERA_FACING_EXTERNAL:
+                        subtype = MetricsEvent.CAMERA_EXTERNAL_USED;
+                        break;
+                    default:
+                        continue;
+                }
+                LogMaker l = new LogMaker(MetricsEvent.ACTION_CAMERA_EVENT)
+                        .setType(MetricsEvent.TYPE_ACTION)
+                        .setSubtype(subtype)
+                        .setLatency(e.getDuration())
+                        .setPackageName(e.mClientName);
+                mLogger.write(l);
+            }
+            mCameraUsageHistory.clear();
+        }
+        CameraStatsJobService.schedule(mContext);
+    }
+
     private void switchUserLocked(int userHandle) {
         Set<Integer> currentUserHandles = getEnabledUserHandles(userHandle);
         mLastUser = userHandle;
@@ -278,21 +368,35 @@
         return true;
     }
 
-    private void updateActivityCount(String cameraId, int newCameraState) {
+    private void updateActivityCount(String cameraId, int newCameraState, int facing, String clientName) {
         synchronized(mLock) {
-            boolean wasEmpty = mActiveCameraIds.isEmpty();
+            // Update active camera list and notify NFC if necessary
+            boolean wasEmpty = mActiveCameraUsage.isEmpty();
             switch (newCameraState) {
-                case CAMERA_STATE_OPEN:
+                case ICameraServiceProxy.CAMERA_STATE_OPEN:
                     break;
-                case CAMERA_STATE_ACTIVE:
-                    mActiveCameraIds.add(cameraId);
+                case ICameraServiceProxy.CAMERA_STATE_ACTIVE:
+                    CameraUsageEvent newEvent = new CameraUsageEvent(facing, clientName);
+                    CameraUsageEvent oldEvent = mActiveCameraUsage.put(cameraId, newEvent);
+                    if (oldEvent != null) {
+                        Slog.w(TAG, "Camera " + cameraId + " was already marked as active");
+                        oldEvent.markCompleted();
+                        mCameraUsageHistory.add(oldEvent);
+                    }
                     break;
-                case CAMERA_STATE_IDLE:
-                case CAMERA_STATE_CLOSED:
-                    mActiveCameraIds.remove(cameraId);
+                case ICameraServiceProxy.CAMERA_STATE_IDLE:
+                case ICameraServiceProxy.CAMERA_STATE_CLOSED:
+                    CameraUsageEvent doneEvent = mActiveCameraUsage.remove(cameraId);
+                    if (doneEvent != null) {
+                        doneEvent.markCompleted();
+                        mCameraUsageHistory.add(doneEvent);
+                        if (mCameraUsageHistory.size() > MAX_USAGE_HISTORY) {
+                            dumpUsageEvents();
+                        }
+                    }
                     break;
             }
-            boolean isEmpty = mActiveCameraIds.isEmpty();
+            boolean isEmpty = mActiveCameraUsage.isEmpty();
             if ( mNotifyNfc && (wasEmpty != isEmpty) ) {
                 notifyNfcService(isEmpty);
             }
@@ -328,12 +432,23 @@
 
     private static String cameraStateToString(int newCameraState) {
         switch (newCameraState) {
-            case CAMERA_STATE_OPEN: return "CAMERA_STATE_OPEN";
-            case CAMERA_STATE_ACTIVE: return "CAMERA_STATE_ACTIVE";
-            case CAMERA_STATE_IDLE: return "CAMERA_STATE_IDLE";
-            case CAMERA_STATE_CLOSED: return "CAMERA_STATE_CLOSED";
+            case ICameraServiceProxy.CAMERA_STATE_OPEN: return "CAMERA_STATE_OPEN";
+            case ICameraServiceProxy.CAMERA_STATE_ACTIVE: return "CAMERA_STATE_ACTIVE";
+            case ICameraServiceProxy.CAMERA_STATE_IDLE: return "CAMERA_STATE_IDLE";
+            case ICameraServiceProxy.CAMERA_STATE_CLOSED: return "CAMERA_STATE_CLOSED";
             default: break;
         }
         return "CAMERA_STATE_UNKNOWN";
     }
+
+    private static String cameraFacingToString(int cameraFacing) {
+        switch (cameraFacing) {
+            case ICameraServiceProxy.CAMERA_FACING_BACK: return "CAMERA_FACING_BACK";
+            case ICameraServiceProxy.CAMERA_FACING_FRONT: return "CAMERA_FACING_FRONT";
+            case ICameraServiceProxy.CAMERA_FACING_EXTERNAL: return "CAMERA_FACING_EXTERNAL";
+            default: break;
+        }
+        return "CAMERA_FACING_UNKNOWN";
+    }
+
 }
diff --git a/services/core/java/com/android/server/camera/CameraStatsJobService.java b/services/core/java/com/android/server/camera/CameraStatsJobService.java
new file mode 100644
index 0000000..b8a6846
--- /dev/null
+++ b/services/core/java/com/android/server/camera/CameraStatsJobService.java
@@ -0,0 +1,77 @@
+/*
+ * 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/LICENSE2.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.camera;
+
+import android.app.job.JobInfo;
+import android.app.job.JobParameters;
+import android.app.job.JobScheduler;
+import android.app.job.JobService;
+import android.content.ComponentName;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.util.Slog;
+
+import java.util.concurrent.TimeUnit;
+
+import com.android.server.LocalServices;
+
+/**
+ * A JobService to periodically collect camera usage stats.
+ */
+public class CameraStatsJobService extends JobService {
+    private static final String TAG = "CameraStatsJobService";
+
+    // Must be unique within UID (system service)
+    private static final int CAMERA_REPORTING_JOB_ID = 0xCA3E7A;
+
+    private static ComponentName sCameraStatsJobServiceName = new ComponentName(
+            "android",
+            CameraStatsJobService.class.getName());
+
+    @Override
+    public boolean onStartJob(JobParameters params) {
+        CameraServiceProxy serviceProxy = LocalServices.getService(CameraServiceProxy.class);
+        if (serviceProxy == null) {
+            Slog.w(TAG, "Can't collect camera usage stats - no camera service proxy found");
+            return false;
+        }
+
+        serviceProxy.dumpUsageEvents();
+        return false;
+    }
+
+    @Override
+    public boolean onStopJob(JobParameters params) {
+        // All work is done in onStartJob, so nothing to stop here
+        return false;
+    }
+
+    public static void schedule(Context context) {
+
+        JobScheduler js = (JobScheduler) context.getSystemService(Context.JOB_SCHEDULER_SERVICE);
+        if (js == null) {
+            Slog.e(TAG, "Can't collect camera usage stats - no Job Scheduler");
+            return;
+        }
+        js.schedule(new JobInfo.Builder(CAMERA_REPORTING_JOB_ID, sCameraStatsJobServiceName)
+                .setMinimumLatency(TimeUnit.DAYS.toMillis(1))
+                .setRequiresDeviceIdle(true)
+                .build());
+
+    }
+
+}
diff --git a/services/core/java/com/android/server/connectivity/IpConnectivityEventBuilder.java b/services/core/java/com/android/server/connectivity/IpConnectivityEventBuilder.java
index 5dee91d..ee38219 100644
--- a/services/core/java/com/android/server/connectivity/IpConnectivityEventBuilder.java
+++ b/services/core/java/com/android/server/connectivity/IpConnectivityEventBuilder.java
@@ -20,6 +20,7 @@
 import static android.net.NetworkCapabilities.TRANSPORT_BLUETOOTH;
 import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
 import static android.net.NetworkCapabilities.TRANSPORT_ETHERNET;
+import static android.net.NetworkCapabilities.TRANSPORT_LOWPAN;
 import static android.net.NetworkCapabilities.TRANSPORT_VPN;
 import static android.net.NetworkCapabilities.TRANSPORT_WIFI;
 import static android.net.NetworkCapabilities.TRANSPORT_WIFI_AWARE;
@@ -362,29 +363,46 @@
         TRANSPORT_LINKLAYER_MAP[TRANSPORT_BLUETOOTH]  = IpConnectivityLogClass.BLUETOOTH;
         TRANSPORT_LINKLAYER_MAP[TRANSPORT_ETHERNET]   = IpConnectivityLogClass.ETHERNET;
         TRANSPORT_LINKLAYER_MAP[TRANSPORT_VPN]        = IpConnectivityLogClass.UNKNOWN;
-        // TODO: change mapping TRANSPORT_WIFI_AWARE -> WIFI_AWARE
-        TRANSPORT_LINKLAYER_MAP[TRANSPORT_WIFI_AWARE] = IpConnectivityLogClass.UNKNOWN;
+        TRANSPORT_LINKLAYER_MAP[TRANSPORT_WIFI_AWARE] = IpConnectivityLogClass.WIFI_NAN;
+        TRANSPORT_LINKLAYER_MAP[TRANSPORT_LOWPAN]     = IpConnectivityLogClass.LOWPAN;
     };
 
     private static int ifnameToLinkLayer(String ifname) {
         // Do not try to catch all interface names with regexes, instead only catch patterns that
         // are cheap to check, and otherwise fallback on postprocessing in aggregation layer.
-        for (int i = 0; i < IFNAME_LINKLAYER_MAP.size(); i++) {
-            String pattern = IFNAME_LINKLAYER_MAP.valueAt(i);
+        for (int i = 0; i < KNOWN_PREFIX; i++) {
+            String pattern = IFNAME_PREFIXES[i];
             if (ifname.startsWith(pattern)) {
-                return IFNAME_LINKLAYER_MAP.keyAt(i);
+                return IFNAME_LINKLAYERS[i];
             }
         }
         return IpConnectivityLogClass.UNKNOWN;
     }
 
-    private static final SparseArray<String> IFNAME_LINKLAYER_MAP = new SparseArray<String>();
+    private static final int KNOWN_PREFIX = 7;
+    private static final String[] IFNAME_PREFIXES = new String[KNOWN_PREFIX];
+    private static final int[] IFNAME_LINKLAYERS = new int[KNOWN_PREFIX];
     static {
-        IFNAME_LINKLAYER_MAP.put(IpConnectivityLogClass.CELLULAR, "rmnet");
-        IFNAME_LINKLAYER_MAP.put(IpConnectivityLogClass.WIFI, "wlan");
-        IFNAME_LINKLAYER_MAP.put(IpConnectivityLogClass.BLUETOOTH, "bt-pan");
-        // TODO: rekey to USB
-        IFNAME_LINKLAYER_MAP.put(IpConnectivityLogClass.ETHERNET, "usb");
-        // TODO: add mappings for nan -> WIFI_AWARE and p2p -> WIFI_P2P
+        // Ordered from most likely link layer to least likely.
+        IFNAME_PREFIXES[0] = "rmnet";
+        IFNAME_LINKLAYERS[0] = IpConnectivityLogClass.CELLULAR;
+
+        IFNAME_PREFIXES[1] = "wlan";
+        IFNAME_LINKLAYERS[1] = IpConnectivityLogClass.WIFI;
+
+        IFNAME_PREFIXES[2] = "bt-pan";
+        IFNAME_LINKLAYERS[2] = IpConnectivityLogClass.BLUETOOTH;
+
+        IFNAME_PREFIXES[3] = "p2p";
+        IFNAME_LINKLAYERS[3] = IpConnectivityLogClass.WIFI_P2P;
+
+        IFNAME_PREFIXES[4] = "aware";
+        IFNAME_LINKLAYERS[4] = IpConnectivityLogClass.WIFI_NAN;
+
+        IFNAME_PREFIXES[5] = "eth";
+        IFNAME_LINKLAYERS[5] = IpConnectivityLogClass.ETHERNET;
+
+        IFNAME_PREFIXES[6] = "wpan";
+        IFNAME_LINKLAYERS[6] = IpConnectivityLogClass.LOWPAN;
     }
 }
diff --git a/services/core/java/com/android/server/connectivity/Tethering.java b/services/core/java/com/android/server/connectivity/Tethering.java
index 0a9dba7..b0be8f7 100644
--- a/services/core/java/com/android/server/connectivity/Tethering.java
+++ b/services/core/java/com/android/server/connectivity/Tethering.java
@@ -57,6 +57,7 @@
 import android.net.NetworkState;
 import android.net.NetworkUtils;
 import android.net.RouteInfo;
+import android.net.util.PrefixUtils;
 import android.net.util.SharedLog;
 import android.net.wifi.WifiManager;
 import android.os.Binder;
@@ -213,13 +214,13 @@
         final Handler smHandler = mTetherMasterSM.getHandler();
         mOffloadController = new OffloadController(smHandler,
                 deps.getOffloadHardwareInterface(smHandler, mLog),
-                mContext.getContentResolver(),
+                mContext.getContentResolver(), mNMService,
                 mLog);
         mUpstreamNetworkMonitor = new UpstreamNetworkMonitor(
-                mContext, mTetherMasterSM, mLog, TetherMasterSM.EVENT_UPSTREAM_CALLBACK );
+                mContext, mTetherMasterSM, mLog, TetherMasterSM.EVENT_UPSTREAM_CALLBACK);
         mForwardedDownstreams = new HashSet<>();
         mSimChange = new SimChangeListener(
-                mContext, mTetherMasterSM.getHandler(), () -> reevaluateSimCardProvisioning());
+                mContext, smHandler, () -> reevaluateSimCardProvisioning());
 
         mStateReceiver = new StateReceiver();
         IntentFilter filter = new IntentFilter();
@@ -227,13 +228,13 @@
         filter.addAction(ConnectivityManager.CONNECTIVITY_ACTION);
         filter.addAction(WifiManager.WIFI_AP_STATE_CHANGED_ACTION);
         filter.addAction(Intent.ACTION_CONFIGURATION_CHANGED);
-        mContext.registerReceiver(mStateReceiver, filter, null, mTetherMasterSM.getHandler());
+        mContext.registerReceiver(mStateReceiver, filter, null, smHandler);
 
         filter = new IntentFilter();
         filter.addAction(Intent.ACTION_MEDIA_SHARED);
         filter.addAction(Intent.ACTION_MEDIA_UNSHARED);
         filter.addDataScheme("file");
-        mContext.registerReceiver(mStateReceiver, filter, null, mTetherMasterSM.getHandler());
+        mContext.registerReceiver(mStateReceiver, filter, null, smHandler);
 
         // load device config info
         updateConfiguration();
@@ -1142,12 +1143,6 @@
         }
     }
 
-    private void startOffloadController() {
-        mOffloadController.start();
-        mOffloadController.updateExemptPrefixes(
-                mUpstreamNetworkMonitor.getOffloadExemptPrefixes());
-    }
-
     class TetherMasterSM extends StateMachine {
         private static final int BASE_MASTER                    = Protocol.BASE_TETHERING;
         // an interface SM has requested Tethering/Local Hotspot
@@ -1165,14 +1160,14 @@
         static final int CMD_CLEAR_ERROR                        = BASE_MASTER + 6;
         static final int EVENT_IFACE_UPDATE_LINKPROPERTIES      = BASE_MASTER + 7;
 
-        private State mInitialState;
-        private State mTetherModeAliveState;
+        private final State mInitialState;
+        private final State mTetherModeAliveState;
 
-        private State mSetIpForwardingEnabledErrorState;
-        private State mSetIpForwardingDisabledErrorState;
-        private State mStartTetheringErrorState;
-        private State mStopTetheringErrorState;
-        private State mSetDnsForwardersErrorState;
+        private final State mSetIpForwardingEnabledErrorState;
+        private final State mSetIpForwardingDisabledErrorState;
+        private final State mStartTetheringErrorState;
+        private final State mStopTetheringErrorState;
+        private final State mSetDnsForwardersErrorState;
 
         // This list is a little subtle.  It contains all the interfaces that currently are
         // requesting tethering, regardless of whether these interfaces are still members of
@@ -1212,22 +1207,46 @@
 
             mNotifyList = new ArrayList<>();
             mIPv6TetheringCoordinator = new IPv6TetheringCoordinator(mNotifyList, mLog);
+
             setInitialState(mInitialState);
         }
 
+        private void startOffloadController() {
+            mOffloadController.start();
+            sendOffloadExemptPrefixes();
+        }
+
+        private void sendOffloadExemptPrefixes() {
+            sendOffloadExemptPrefixes(mUpstreamNetworkMonitor.getLocalPrefixes());
+        }
+
+        private void sendOffloadExemptPrefixes(Set<IpPrefix> localPrefixes) {
+            // Add in well-known minimum set.
+            PrefixUtils.addNonForwardablePrefixes(localPrefixes);
+            // Add tragically hardcoded prefixes.
+            localPrefixes.add(PrefixUtils.DEFAULT_WIFI_P2P_PREFIX);
+
+            // Add prefixes for all downstreams, regardless of IP serving mode.
+            for (TetherInterfaceStateMachine tism : mNotifyList) {
+                localPrefixes.addAll(PrefixUtils.localPrefixesFrom(tism.linkProperties()));
+            }
+
+            mOffloadController.setLocalPrefixes(localPrefixes);
+        }
+
         class InitialState extends State {
             @Override
             public boolean processMessage(Message message) {
                 logMessage(this, message.what);
                 switch (message.what) {
                     case EVENT_IFACE_SERVING_STATE_ACTIVE:
-                        TetherInterfaceStateMachine who = (TetherInterfaceStateMachine)message.obj;
+                        TetherInterfaceStateMachine who = (TetherInterfaceStateMachine) message.obj;
                         if (VDBG) Log.d(TAG, "Tether Mode requested by " + who);
                         handleInterfaceServingStateActive(message.arg1, who);
                         transitionTo(mTetherModeAliveState);
                         break;
                     case EVENT_IFACE_SERVING_STATE_INACTIVE:
-                        who = (TetherInterfaceStateMachine)message.obj;
+                        who = (TetherInterfaceStateMachine) message.obj;
                         if (VDBG) Log.d(TAG, "Tether Mode unrequested by " + who);
                         handleInterfaceServingStateInactive(who);
                         break;
@@ -1422,8 +1441,8 @@
         }
 
         private void handleUpstreamNetworkMonitorCallback(int arg1, Object o) {
-            if (arg1 == UpstreamNetworkMonitor.NOTIFY_EXEMPT_PREFIXES) {
-                mOffloadController.updateExemptPrefixes((Set<IpPrefix>) o);
+            if (arg1 == UpstreamNetworkMonitor.NOTIFY_LOCAL_PREFIXES) {
+                sendOffloadExemptPrefixes((Set<IpPrefix>) o);
                 return;
             }
 
@@ -1527,7 +1546,7 @@
                 boolean retValue = true;
                 switch (message.what) {
                     case EVENT_IFACE_SERVING_STATE_ACTIVE: {
-                        TetherInterfaceStateMachine who = (TetherInterfaceStateMachine)message.obj;
+                        TetherInterfaceStateMachine who = (TetherInterfaceStateMachine) message.obj;
                         if (VDBG) Log.d(TAG, "Tether Mode requested by " + who);
                         handleInterfaceServingStateActive(message.arg1, who);
                         who.sendMessage(TetherInterfaceStateMachine.CMD_TETHER_CONNECTION_CHANGED,
@@ -1541,7 +1560,7 @@
                         break;
                     }
                     case EVENT_IFACE_SERVING_STATE_INACTIVE: {
-                        TetherInterfaceStateMachine who = (TetherInterfaceStateMachine)message.obj;
+                        TetherInterfaceStateMachine who = (TetherInterfaceStateMachine) message.obj;
                         if (VDBG) Log.d(TAG, "Tether Mode unrequested by " + who);
                         handleInterfaceServingStateInactive(who);
 
@@ -1573,6 +1592,9 @@
                             mOffloadController.notifyDownstreamLinkProperties(newLp);
                         } else {
                             mOffloadController.removeDownstreamInterface(newLp.getInterfaceName());
+                            // Another interface might be in local-only hotspot mode;
+                            // resend all local prefixes to the OffloadController.
+                            sendOffloadExemptPrefixes();
                         }
                         break;
                     }
@@ -1614,7 +1636,7 @@
                 boolean retValue = true;
                 switch (message.what) {
                     case EVENT_IFACE_SERVING_STATE_ACTIVE:
-                        TetherInterfaceStateMachine who = (TetherInterfaceStateMachine)message.obj;
+                        TetherInterfaceStateMachine who = (TetherInterfaceStateMachine) message.obj;
                         who.sendMessage(mErrorNotification);
                         break;
                     case CMD_CLEAR_ERROR:
@@ -1737,6 +1759,11 @@
             pw.decreaseIndent();
         }
 
+        pw.println("Hardware offload:");
+        pw.increaseIndent();
+        mOffloadController.dump(pw);
+        pw.decreaseIndent();
+
         pw.println("Log:");
         pw.increaseIndent();
         if (argsContain(args, SHORT_ARG)) {
diff --git a/services/core/java/com/android/server/connectivity/Vpn.java b/services/core/java/com/android/server/connectivity/Vpn.java
index 27968a9..e82eabf 100644
--- a/services/core/java/com/android/server/connectivity/Vpn.java
+++ b/services/core/java/com/android/server/connectivity/Vpn.java
@@ -36,6 +36,7 @@
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.content.ServiceConnection;
+import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageManager;
 import android.content.pm.PackageManager.NameNotFoundException;
 import android.content.pm.ResolveInfo;
@@ -56,7 +57,10 @@
 import android.net.RouteInfo;
 import android.net.UidRange;
 import android.net.Uri;
+import android.net.VpnService;
 import android.os.Binder;
+import android.os.Build.VERSION_CODES;
+import android.os.Bundle;
 import android.os.FileUtils;
 import android.os.IBinder;
 import android.os.INetworkManagementService;
@@ -296,6 +300,56 @@
     }
 
     /**
+     * Checks if a VPN app supports always-on mode.
+     *
+     * In order to support the always-on feature, an app has to
+     * <ul>
+     *     <li>target {@link VERSION_CODES#N API 24} or above, and
+     *     <li>not opt out through the {@link VpnService#METADATA_SUPPORTS_ALWAYS_ON} meta-data
+     *         field.
+     * </ul>
+     *
+     * @param packageName the canonical package name of the VPN app
+     * @return {@code true} if and only if the VPN app exists and supports always-on mode
+     */
+    public boolean isAlwaysOnPackageSupported(String packageName) {
+        enforceSettingsPermission();
+
+        if (packageName == null) {
+            return false;
+        }
+
+        PackageManager pm = mContext.getPackageManager();
+        ApplicationInfo appInfo = null;
+        try {
+            appInfo = pm.getApplicationInfoAsUser(packageName, 0 /*flags*/, mUserHandle);
+        } catch (NameNotFoundException unused) {
+            Log.w(TAG, "Can't find \"" + packageName + "\" when checking always-on support");
+        }
+        if (appInfo == null || appInfo.targetSdkVersion < VERSION_CODES.N) {
+            return false;
+        }
+
+        final Intent intent = new Intent(VpnConfig.SERVICE_INTERFACE);
+        intent.setPackage(packageName);
+        List<ResolveInfo> services =
+                pm.queryIntentServicesAsUser(intent, PackageManager.GET_META_DATA, mUserHandle);
+        if (services == null || services.size() == 0) {
+            return false;
+        }
+
+        for (ResolveInfo rInfo : services) {
+            final Bundle metaData = rInfo.serviceInfo.metaData;
+            if (metaData != null
+                    && !metaData.getBoolean(VpnService.METADATA_SUPPORTS_ALWAYS_ON, true)) {
+                return false;
+            }
+        }
+
+        return true;
+    }
+
+    /**
      * Configures an always-on VPN connection through a specific application.
      * This connection is automatically granted and persisted after a reboot.
      *
@@ -303,6 +357,10 @@
      *    manifest guarded by {@link android.Manifest.permission.BIND_VPN_SERVICE},
      *    otherwise the call will fail.
      *
+     * <p>Note that this method does not check if the VPN app supports always-on mode. The check is
+     *    delayed to {@link #startAlwaysOnVpn()}, which is always called immediately after this
+     *    method in {@link android.net.IConnectivityManager#setAlwaysOnVpnPackage}.
+     *
      * @param packageName the package to designate as always-on VPN supplier.
      * @param lockdown whether to prevent traffic outside of a VPN, for example while connecting.
      * @return {@code true} if the package has been set as always-on, {@code false} otherwise.
@@ -443,6 +501,11 @@
             if (alwaysOnPackage == null) {
                 return true;
             }
+            // Remove always-on VPN if it's not supported.
+            if (!isAlwaysOnPackageSupported(alwaysOnPackage)) {
+                setAlwaysOnPackage(null, false);
+                return false;
+            }
             // Skip if the service is already established. This isn't bulletproof: it's not bound
             // until after establish(), so if it's mid-setup onStartCommand will be sent twice,
             // which may restart the connection.
@@ -1219,6 +1282,11 @@
                 "Unauthorized Caller");
     }
 
+    private void enforceSettingsPermission() {
+        mContext.enforceCallingOrSelfPermission(Manifest.permission.NETWORK_SETTINGS,
+                "Unauthorized Caller");
+    }
+
     private class Connection implements ServiceConnection {
         private IBinder mService;
 
diff --git a/services/core/java/com/android/server/connectivity/tethering/OffloadController.java b/services/core/java/com/android/server/connectivity/tethering/OffloadController.java
index 2b0ded9..1a5ff77 100644
--- a/services/core/java/com/android/server/connectivity/tethering/OffloadController.java
+++ b/services/core/java/com/android/server/connectivity/tethering/OffloadController.java
@@ -16,20 +16,38 @@
 
 package com.android.server.connectivity.tethering;
 
+import static android.net.NetworkStats.SET_DEFAULT;
+import static android.net.NetworkStats.TAG_NONE;
+import static android.net.TrafficStats.UID_TETHERING;
 import static android.provider.Settings.Global.TETHER_OFFLOAD_DISABLED;
 
 import android.content.ContentResolver;
+import android.net.ITetheringStatsProvider;
 import android.net.IpPrefix;
+import android.net.LinkAddress;
 import android.net.LinkProperties;
+import android.net.NetworkStats;
 import android.net.RouteInfo;
 import android.net.util.SharedLog;
 import android.os.Handler;
+import android.os.INetworkManagementService;
+import android.os.RemoteException;
+import android.os.SystemClock;
 import android.provider.Settings;
+import android.text.TextUtils;
+
+import com.android.internal.util.IndentingPrintWriter;
 
 import java.net.Inet4Address;
+import java.net.Inet6Address;
 import java.net.InetAddress;
 import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Objects;
 import java.util.Set;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
 
 /**
  * A class to encapsulate the business logic of programming the tethering
@@ -40,6 +58,8 @@
 public class OffloadController {
     private static final String TAG = OffloadController.class.getSimpleName();
 
+    private static final int STATS_FETCH_TIMEOUT_MS = 1000;
+
     private final Handler mHandler;
     private final OffloadHardwareInterface mHwInterface;
     private final ContentResolver mContentResolver;
@@ -47,14 +67,33 @@
     private boolean mConfigInitialized;
     private boolean mControlInitialized;
     private LinkProperties mUpstreamLinkProperties;
+    // The complete set of offload-exempt prefixes passed in via Tethering from
+    // all upstream and downstream sources.
     private Set<IpPrefix> mExemptPrefixes;
+    // A strictly "smaller" set of prefixes, wherein offload-approved prefixes
+    // (e.g. downstream on-link prefixes) have been removed and replaced with
+    // prefixes representing only the locally-assigned IP addresses.
+    private Set<String> mLastLocalPrefixStrs;
+
+    // Maps upstream interface names to offloaded traffic statistics.
+    private HashMap<String, OffloadHardwareInterface.ForwardedStats>
+            mForwardedStats = new HashMap<>();
 
     public OffloadController(Handler h, OffloadHardwareInterface hwi,
-            ContentResolver contentResolver, SharedLog log) {
+            ContentResolver contentResolver, INetworkManagementService nms, SharedLog log) {
         mHandler = h;
         mHwInterface = hwi;
         mContentResolver = contentResolver;
         mLog = log.forSubComponent(TAG);
+        mExemptPrefixes = new HashSet<>();
+        mLastLocalPrefixStrs = new HashSet<>();
+
+        try {
+            nms.registerTetheringStatsProvider(
+                    new OffloadTetheringStatsProvider(), getClass().getSimpleName());
+        } catch (RemoteException e) {
+            mLog.e("Cannot register offload stats provider: " + e);
+        }
     }
 
     public void start() {
@@ -126,6 +165,7 @@
 
     public void stop() {
         final boolean wasStarted = started();
+        updateStatsForCurrentUpstream();
         mUpstreamLinkProperties = null;
         mHwInterface.stopOffloadControl();
         mControlInitialized = false;
@@ -133,26 +173,83 @@
         if (wasStarted) mLog.log("tethering offload stopped");
     }
 
+    private class OffloadTetheringStatsProvider extends ITetheringStatsProvider.Stub {
+        @Override
+        public NetworkStats getTetherStats() {
+            NetworkStats stats = new NetworkStats(SystemClock.elapsedRealtime(), 0);
+            CountDownLatch latch = new CountDownLatch(1);
+
+            mHandler.post(() -> {
+                try {
+                    NetworkStats.Entry entry = new NetworkStats.Entry();
+                    entry.set = SET_DEFAULT;
+                    entry.tag = TAG_NONE;
+                    entry.uid = UID_TETHERING;
+
+                    updateStatsForCurrentUpstream();
+
+                    for (String iface : mForwardedStats.keySet()) {
+                        entry.iface = iface;
+                        entry.rxBytes = mForwardedStats.get(iface).rxBytes;
+                        entry.txBytes = mForwardedStats.get(iface).txBytes;
+                        stats.addValues(entry);
+                    }
+                } finally {
+                    latch.countDown();
+                }
+            });
+
+            try {
+                latch.await(STATS_FETCH_TIMEOUT_MS, TimeUnit.MILLISECONDS);
+            } catch (InterruptedException e) {
+                mLog.e("Tethering stats fetch timed out after " + STATS_FETCH_TIMEOUT_MS + "ms");
+            }
+
+            return stats;
+        }
+    }
+
+    private void maybeUpdateStats(String iface) {
+        if (TextUtils.isEmpty(iface)) {
+            return;
+        }
+
+        if (!mForwardedStats.containsKey(iface)) {
+            mForwardedStats.put(iface, new OffloadHardwareInterface.ForwardedStats());
+        }
+        mForwardedStats.get(iface).add(mHwInterface.getForwardedStats(iface));
+    }
+
+    private void updateStatsForCurrentUpstream() {
+        if (mUpstreamLinkProperties != null) {
+            maybeUpdateStats(mUpstreamLinkProperties.getInterfaceName());
+        }
+    }
+
     public void setUpstreamLinkProperties(LinkProperties lp) {
-        if (!started()) return;
+        if (!started() || Objects.equals(mUpstreamLinkProperties, lp)) return;
+
+        String prevUpstream = (mUpstreamLinkProperties != null) ?
+                mUpstreamLinkProperties.getInterfaceName() : null;
 
         mUpstreamLinkProperties = (lp != null) ? new LinkProperties(lp) : null;
+
         // TODO: examine return code and decide what to do if programming
         // upstream parameters fails (probably just wait for a subsequent
         // onOffloadEvent() callback to tell us offload is available again and
         // then reapply all state).
+        computeAndPushLocalPrefixes();
         pushUpstreamParameters();
+
+        // Update stats after we've told the hardware to change routing so we don't miss packets.
+        maybeUpdateStats(prevUpstream);
     }
 
-    public void updateExemptPrefixes(Set<IpPrefix> exemptPrefixes) {
+    public void setLocalPrefixes(Set<IpPrefix> localPrefixes) {
         if (!started()) return;
 
-        mExemptPrefixes = exemptPrefixes;
-        // TODO:
-        //     - add IP addresses from all downstream link properties
-        //     - add routes from all non-tethering downstream link properties
-        //     - remove any 64share prefixes
-        //     - push this to the HAL
+        mExemptPrefixes = localPrefixes;
+        computeAndPushLocalPrefixes();
     }
 
     public void notifyDownstreamLinkProperties(LinkProperties lp) {
@@ -215,4 +312,54 @@
         return mHwInterface.setUpstreamParameters(
                 iface, v4addr, v4gateway, (v6gateways.isEmpty() ? null : v6gateways));
     }
+
+    private boolean computeAndPushLocalPrefixes() {
+        final Set<String> localPrefixStrs = computeLocalPrefixStrings(
+                mExemptPrefixes, mUpstreamLinkProperties);
+        if (mLastLocalPrefixStrs.equals(localPrefixStrs)) return true;
+
+        mLastLocalPrefixStrs = localPrefixStrs;
+        return mHwInterface.setLocalPrefixes(new ArrayList<>(localPrefixStrs));
+    }
+
+    // TODO: Factor in downstream LinkProperties once that information is available.
+    private static Set<String> computeLocalPrefixStrings(
+            Set<IpPrefix> localPrefixes, LinkProperties upstreamLinkProperties) {
+        // Create an editable copy.
+        final Set<IpPrefix> prefixSet = new HashSet<>(localPrefixes);
+
+        // TODO: If a downstream interface (not currently passed in) is reusing
+        // the /64 of the upstream (64share) then:
+        //
+        //     [a] remove that /64 from the local prefixes
+        //     [b] add in /128s for IP addresses on the downstream interface
+        //     [c] add in /128s for IP addresses on the upstream interface
+        //
+        // Until downstream information is available here, simply add /128s from
+        // the upstream network; they'll just be redundant with their /64.
+        if (upstreamLinkProperties != null) {
+            for (LinkAddress linkAddr : upstreamLinkProperties.getLinkAddresses()) {
+                if (!linkAddr.isGlobalPreferred()) continue;
+                final InetAddress ip = linkAddr.getAddress();
+                if (!(ip instanceof Inet6Address)) continue;
+                prefixSet.add(new IpPrefix(ip, 128));
+            }
+        }
+
+        final HashSet<String> localPrefixStrs = new HashSet<>();
+        for (IpPrefix pfx : prefixSet) localPrefixStrs.add(pfx.toString());
+        return localPrefixStrs;
+    }
+
+    public void dump(IndentingPrintWriter pw) {
+        if (isOffloadDisabled()) {
+            pw.println("Offload disabled");
+            return;
+        }
+        pw.println("Offload HALs " + (started() ? "started" : "not started"));
+        LinkProperties lp = mUpstreamLinkProperties;
+        String upstream = (lp != null) ? lp.getInterfaceName() : null;
+        pw.println("Current upstream: " + upstream);
+        pw.println("Exempt prefixes: " + mLastLocalPrefixStrs);
+    }
 }
diff --git a/services/core/java/com/android/server/connectivity/tethering/OffloadHardwareInterface.java b/services/core/java/com/android/server/connectivity/tethering/OffloadHardwareInterface.java
index b648f51..4df566f 100644
--- a/services/core/java/com/android/server/connectivity/tethering/OffloadHardwareInterface.java
+++ b/services/core/java/com/android/server/connectivity/tethering/OffloadHardwareInterface.java
@@ -168,6 +168,26 @@
         return stats;
     }
 
+    public boolean setLocalPrefixes(ArrayList<String> localPrefixes) {
+        final String logmsg = String.format("setLocalPrefixes([%s])",
+                String.join(",", localPrefixes));
+
+        final CbResults results = new CbResults();
+        try {
+            mOffloadControl.setLocalPrefixes(localPrefixes,
+                    (boolean success, String errMsg) -> {
+                        results.success = success;
+                        results.errMsg = errMsg;
+                    });
+        } catch (RemoteException e) {
+            record(logmsg, e);
+            return false;
+        }
+
+        record(logmsg, results);
+        return results.success;
+    }
+
     public boolean setUpstreamParameters(
             String iface, String v4addr, String v4gateway, ArrayList<String> v6gws) {
         iface = (iface != null) ? iface : NO_INTERFACE_NAME;
diff --git a/services/core/java/com/android/server/connectivity/tethering/TetherInterfaceStateMachine.java b/services/core/java/com/android/server/connectivity/tethering/TetherInterfaceStateMachine.java
index 4bac69c..69678df 100644
--- a/services/core/java/com/android/server/connectivity/tethering/TetherInterfaceStateMachine.java
+++ b/services/core/java/com/android/server/connectivity/tethering/TetherInterfaceStateMachine.java
@@ -161,6 +161,8 @@
 
     public int lastError() { return mLastError; }
 
+    public LinkProperties linkProperties() { return new LinkProperties(mLinkProperties); }
+
     public void stop() { sendMessage(CMD_INTERFACE_DOWN); }
 
     public void unwanted() { sendMessage(CMD_TETHER_UNREQUESTED); }
diff --git a/services/core/java/com/android/server/connectivity/tethering/UpstreamNetworkMonitor.java b/services/core/java/com/android/server/connectivity/tethering/UpstreamNetworkMonitor.java
index eb66767..c5f7528 100644
--- a/services/core/java/com/android/server/connectivity/tethering/UpstreamNetworkMonitor.java
+++ b/services/core/java/com/android/server/connectivity/tethering/UpstreamNetworkMonitor.java
@@ -34,6 +34,7 @@
 import android.net.NetworkRequest;
 import android.net.NetworkState;
 import android.net.util.NetworkConstants;
+import android.net.util.PrefixUtils;
 import android.net.util.SharedLog;
 import android.util.Log;
 
@@ -72,16 +73,11 @@
     private static final boolean DBG = false;
     private static final boolean VDBG = false;
 
-    private static final IpPrefix[] MINIMUM_LOCAL_PREFIXES_SET = {
-            prefix("127.0.0.0/8"), prefix("169.254.0.0/16"),
-            prefix("::/3"), prefix("fe80::/64"), prefix("fc00::/7"), prefix("ff00::/8"),
-    };
-
     public static final int EVENT_ON_AVAILABLE      = 1;
     public static final int EVENT_ON_CAPABILITIES   = 2;
     public static final int EVENT_ON_LINKPROPERTIES = 3;
     public static final int EVENT_ON_LOST           = 4;
-    public static final int NOTIFY_EXEMPT_PREFIXES  = 10;
+    public static final int NOTIFY_LOCAL_PREFIXES   = 10;
 
     private static final int CALLBACK_LISTEN_ALL = 1;
     private static final int CALLBACK_TRACK_DEFAULT = 2;
@@ -93,7 +89,7 @@
     private final Handler mHandler;
     private final int mWhat;
     private final HashMap<Network, NetworkState> mNetworkMap = new HashMap<>();
-    private HashSet<IpPrefix> mOffloadExemptPrefixes;
+    private HashSet<IpPrefix> mLocalPrefixes;
     private ConnectivityManager mCM;
     private NetworkCallback mListenAllCallback;
     private NetworkCallback mDefaultNetworkCallback;
@@ -107,7 +103,7 @@
         mHandler = mTarget.getHandler();
         mLog = log.forSubComponent(TAG);
         mWhat = what;
-        mOffloadExemptPrefixes = allOffloadExemptPrefixes(mNetworkMap.values());
+        mLocalPrefixes = new HashSet<>();
     }
 
     @VisibleForTesting
@@ -223,8 +219,8 @@
         return typeStatePair.ns;
     }
 
-    public Set<IpPrefix> getOffloadExemptPrefixes() {
-        return (Set<IpPrefix>) mOffloadExemptPrefixes.clone();
+    public Set<IpPrefix> getLocalPrefixes() {
+        return (Set<IpPrefix>) mLocalPrefixes.clone();
     }
 
     private void handleAvailable(int callbackType, Network network) {
@@ -360,11 +356,11 @@
         notifyTarget(EVENT_ON_LOST, mNetworkMap.remove(network));
     }
 
-    private void recomputeOffloadExemptPrefixes() {
-        final HashSet<IpPrefix> exemptPrefixes = allOffloadExemptPrefixes(mNetworkMap.values());
-        if (!mOffloadExemptPrefixes.equals(exemptPrefixes)) {
-            mOffloadExemptPrefixes = exemptPrefixes;
-            notifyTarget(NOTIFY_EXEMPT_PREFIXES, exemptPrefixes.clone());
+    private void recomputeLocalPrefixes() {
+        final HashSet<IpPrefix> localPrefixes = allLocalPrefixes(mNetworkMap.values());
+        if (!mLocalPrefixes.equals(localPrefixes)) {
+            mLocalPrefixes = localPrefixes;
+            notifyTarget(NOTIFY_LOCAL_PREFIXES, localPrefixes.clone());
         }
     }
 
@@ -402,7 +398,7 @@
         @Override
         public void onLinkPropertiesChanged(Network network, LinkProperties newLp) {
             handleLinkProp(network, newLp);
-            recomputeOffloadExemptPrefixes();
+            recomputeLocalPrefixes();
         }
 
         // TODO: Handle onNetworkSuspended();
@@ -411,7 +407,7 @@
         @Override
         public void onLost(Network network) {
             handleLost(mCallbackType, network);
-            recomputeOffloadExemptPrefixes();
+            recomputeLocalPrefixes();
         }
     }
 
@@ -460,35 +456,15 @@
         return result;
     }
 
-    private static HashSet<IpPrefix> allOffloadExemptPrefixes(Iterable<NetworkState> netStates) {
+    private static HashSet<IpPrefix> allLocalPrefixes(Iterable<NetworkState> netStates) {
         final HashSet<IpPrefix> prefixSet = new HashSet<>();
 
-        addDefaultLocalPrefixes(prefixSet);
-
         for (NetworkState ns : netStates) {
-            addOffloadExemptPrefixes(prefixSet, ns.linkProperties);
+            final LinkProperties lp = ns.linkProperties;
+            if (lp == null) continue;
+            prefixSet.addAll(PrefixUtils.localPrefixesFrom(lp));
         }
 
         return prefixSet;
     }
-
-    private static void addDefaultLocalPrefixes(Set<IpPrefix> prefixSet) {
-        Collections.addAll(prefixSet, MINIMUM_LOCAL_PREFIXES_SET);
-    }
-
-    private static void addOffloadExemptPrefixes(Set<IpPrefix> prefixSet, LinkProperties lp) {
-        if (lp == null) return;
-
-        for (LinkAddress linkAddr : lp.getAllLinkAddresses()) {
-            prefixSet.add(new IpPrefix(linkAddr.getAddress(), linkAddr.getPrefixLength()));
-        }
-
-        // TODO: Consider adding other non-default routes associated with this
-        // network. Traffic to these destinations should perhaps not go through
-        // the Internet (upstream).
-    }
-
-    private static IpPrefix prefix(String prefixStr) {
-        return new IpPrefix(prefixStr);
-    }
 }
diff --git a/services/core/java/com/android/server/content/SyncJobService.java b/services/core/java/com/android/server/content/SyncJobService.java
index 1f02ebf..07f04b1 100644
--- a/services/core/java/com/android/server/content/SyncJobService.java
+++ b/services/core/java/com/android/server/content/SyncJobService.java
@@ -76,7 +76,7 @@
         m.what = SyncManager.SyncHandler.MESSAGE_START_SYNC;
         SyncOperation op = SyncOperation.maybeCreateFromJobExtras(params.getExtras());
 
-        mLogger.log("onStopJob() jobid=", params.getJobId(), " op=", op);
+        mLogger.log("onStartJob() jobid=", params.getJobId(), " op=", op);
 
         if (op == null) {
             Slog.e(TAG, "Got invalid job " + params.getJobId());
@@ -131,4 +131,4 @@
             }
         }
     }
-}
\ No newline at end of file
+}
diff --git a/services/core/java/com/android/server/display/DisplayPowerController.java b/services/core/java/com/android/server/display/DisplayPowerController.java
index e82724d..d4abc08 100644
--- a/services/core/java/com/android/server/display/DisplayPowerController.java
+++ b/services/core/java/com/android/server/display/DisplayPowerController.java
@@ -16,6 +16,7 @@
 
 package com.android.server.display;
 
+import android.app.ActivityManager;
 import com.android.internal.app.IBatteryStats;
 import com.android.server.LocalServices;
 import com.android.server.am.BatteryStatsService;
@@ -161,6 +162,9 @@
     // True if should use light sensor to automatically determine doze screen brightness.
     private final boolean mAllowAutoBrightnessWhileDozingConfig;
 
+    // Whether or not the color fade on screen on / off is enabled.
+    private final boolean mColorFadeEnabled;
+
     // True if we should fade the screen while turning it off, false if we should play
     // a stylish color fade animation instead.
     private boolean mColorFadeFadesConfig;
@@ -407,6 +411,8 @@
 
         mScreenBrightnessRangeMinimum = screenBrightnessRangeMinimum;
 
+
+        mColorFadeEnabled = !ActivityManager.isLowRamDeviceStatic();
         mColorFadeFadesConfig = resources.getBoolean(
                 com.android.internal.R.bool.config_animateScreenLights);
 
@@ -497,17 +503,19 @@
         // Initialize the power state object for the default display.
         // In the future, we might manage multiple displays independently.
         mPowerState = new DisplayPowerState(mBlanker,
-                new ColorFade(Display.DEFAULT_DISPLAY));
+                mColorFadeEnabled ? new ColorFade(Display.DEFAULT_DISPLAY) : null);
 
-        mColorFadeOnAnimator = ObjectAnimator.ofFloat(
-                mPowerState, DisplayPowerState.COLOR_FADE_LEVEL, 0.0f, 1.0f);
-        mColorFadeOnAnimator.setDuration(COLOR_FADE_ON_ANIMATION_DURATION_MILLIS);
-        mColorFadeOnAnimator.addListener(mAnimatorListener);
+        if (mColorFadeEnabled) {
+            mColorFadeOnAnimator = ObjectAnimator.ofFloat(
+                    mPowerState, DisplayPowerState.COLOR_FADE_LEVEL, 0.0f, 1.0f);
+            mColorFadeOnAnimator.setDuration(COLOR_FADE_ON_ANIMATION_DURATION_MILLIS);
+            mColorFadeOnAnimator.addListener(mAnimatorListener);
 
-        mColorFadeOffAnimator = ObjectAnimator.ofFloat(
-                mPowerState, DisplayPowerState.COLOR_FADE_LEVEL, 1.0f, 0.0f);
-        mColorFadeOffAnimator.setDuration(COLOR_FADE_OFF_ANIMATION_DURATION_MILLIS);
-        mColorFadeOffAnimator.addListener(mAnimatorListener);
+            mColorFadeOffAnimator = ObjectAnimator.ofFloat(
+                    mPowerState, DisplayPowerState.COLOR_FADE_LEVEL, 1.0f, 0.0f);
+            mColorFadeOffAnimator.setDuration(COLOR_FADE_OFF_ANIMATION_DURATION_MILLIS);
+            mColorFadeOffAnimator.addListener(mAnimatorListener);
+        }
 
         mScreenBrightnessRampAnimator = new RampAnimator<DisplayPowerState>(
                 mPowerState, DisplayPowerState.SCREEN_BRIGHTNESS);
@@ -784,9 +792,9 @@
         // Note that we do not wait for the brightness ramp animation to complete before
         // reporting the display is ready because we only need to ensure the screen is in the
         // right power state even as it continues to converge on the desired brightness.
-        final boolean ready = mPendingScreenOnUnblocker == null
-                && !mColorFadeOnAnimator.isStarted()
-                && !mColorFadeOffAnimator.isStarted()
+        final boolean ready = mPendingScreenOnUnblocker == null &&
+                (!mColorFadeEnabled ||
+                        (!mColorFadeOnAnimator.isStarted() && !mColorFadeOffAnimator.isStarted()))
                 && mPowerState.waitUntilClean(mCleanListener);
         final boolean finished = ready
                 && !mScreenBrightnessRampAnimator.isAnimating();
@@ -959,8 +967,8 @@
 
     private void animateScreenStateChange(int target, boolean performScreenOffTransition) {
         // If there is already an animation in progress, don't interfere with it.
-        if (mColorFadeOnAnimator.isStarted()
-                || mColorFadeOffAnimator.isStarted()) {
+        if (mColorFadeEnabled &&
+                (mColorFadeOnAnimator.isStarted() || mColorFadeOffAnimator.isStarted())) {
             if (target != Display.STATE_ON) {
                 return;
             }
@@ -984,7 +992,7 @@
             if (!setScreenState(Display.STATE_ON)) {
                 return; // screen on blocked
             }
-            if (USE_COLOR_FADE_ON_ANIMATION && mPowerRequest.isBrightOrDim()) {
+            if (USE_COLOR_FADE_ON_ANIMATION && mColorFadeEnabled && mPowerRequest.isBrightOrDim()) {
                 // Perform screen on animation.
                 if (mPowerState.getColorFadeLevel() == 1.0f) {
                     mPowerState.dismissColorFade();
@@ -1060,6 +1068,10 @@
         } else {
             // Want screen off.
             mPendingScreenOff = true;
+            if (!mColorFadeEnabled) {
+                mPowerState.setColorFadeLevel(0.0f);
+            }
+
             if (mPowerState.getColorFadeLevel() == 0.0f) {
                 // Turn the screen off.
                 // A black surface is already hiding the contents of the screen.
diff --git a/services/core/java/com/android/server/display/DisplayPowerState.java b/services/core/java/com/android/server/display/DisplayPowerState.java
index e2fd0ac..d0c1580 100644
--- a/services/core/java/com/android/server/display/DisplayPowerState.java
+++ b/services/core/java/com/android/server/display/DisplayPowerState.java
@@ -174,7 +174,7 @@
      * @return True if the electron beam was prepared.
      */
     public boolean prepareColorFade(Context context, int mode) {
-        if (!mColorFade.prepare(context, mode)) {
+        if (mColorFade == null || !mColorFade.prepare(context, mode)) {
             mColorFadePrepared = false;
             mColorFadeReady = true;
             return false;
@@ -190,7 +190,7 @@
      * Dismisses the color fade surface.
      */
     public void dismissColorFade() {
-        mColorFade.dismiss();
+        if (mColorFade != null) mColorFade.dismiss();
         mColorFadePrepared = false;
         mColorFadeReady = true;
     }
@@ -199,7 +199,7 @@
      * Dismisses the color fade resources.
      */
     public void dismissColorFadeResources() {
-        mColorFade.dismissResources();
+        if (mColorFade != null) mColorFade.dismissResources();
     }
 
     /**
@@ -269,7 +269,7 @@
         pw.println("  mColorFadeDrawPending=" + mColorFadeDrawPending);
 
         mPhotonicModulator.dump(pw);
-        mColorFade.dump(pw);
+        if (mColorFade != null) mColorFade.dump(pw);
     }
 
     private void scheduleScreenUpdate() {
diff --git a/services/core/java/com/android/server/job/controllers/ConnectivityController.java b/services/core/java/com/android/server/job/controllers/ConnectivityController.java
index 17c8928..78367fe 100644
--- a/services/core/java/com/android/server/job/controllers/ConnectivityController.java
+++ b/services/core/java/com/android/server/job/controllers/ConnectivityController.java
@@ -85,7 +85,7 @@
     @Override
     public void maybeStartTrackingJobLocked(JobStatus jobStatus, JobStatus lastJob) {
         if (jobStatus.hasConnectivityConstraint()) {
-            updateConstraintsSatisfied(jobStatus, null);
+            updateConstraintsSatisfied(jobStatus);
             mTrackedJobs.add(jobStatus);
             jobStatus.setTrackingController(JobStatus.TRACKING_CONNECTIVITY);
         }
@@ -99,23 +99,25 @@
         }
     }
 
-    private boolean updateConstraintsSatisfied(JobStatus jobStatus,
-            NetworkCapabilities capabilities) {
+    private boolean updateConstraintsSatisfied(JobStatus jobStatus) {
         final int jobUid = jobStatus.getSourceUid();
         final boolean ignoreBlocked = (jobStatus.getFlags() & JobInfo.FLAG_WILL_BE_FOREGROUND) != 0;
         final NetworkInfo info = mConnManager.getActiveNetworkInfoForUid(jobUid, ignoreBlocked);
-        if (capabilities == null) {
-            final Network network = mConnManager.getActiveNetworkForUid(jobUid, ignoreBlocked);
-            capabilities = mConnManager.getNetworkCapabilities(network);
-        }
+        final Network network = mConnManager.getActiveNetworkForUid(jobUid, ignoreBlocked);
+        final NetworkCapabilities capabilities = (network != null)
+                ? mConnManager.getNetworkCapabilities(network) : null;
 
-        final boolean validated = capabilities != null
+        final boolean validated = (capabilities != null)
                 && capabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_VALIDATED);
-        final boolean connected = info != null && info.isConnected();
+        final boolean connected = (info != null) && info.isConnected();
         final boolean connectionUsable = connected && validated;
-        final boolean metered = connected && info.isMetered();
-        final boolean unmetered = connected && !info.isMetered();
-        final boolean notRoaming = connected && !info.isRoaming();
+
+        final boolean metered = connected && (capabilities != null)
+                && !capabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED);
+        final boolean unmetered = connected && (capabilities != null)
+                && capabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED);
+        final boolean notRoaming = connected && (info != null)
+                && !info.isRoaming();
 
         boolean changed = false;
         changed |= jobStatus.setConnectivityConstraintSatisfied(connectionUsable);
@@ -148,13 +150,13 @@
      * @param uid only update jobs belonging to this UID, or {@code -1} to
      *            update all tracked jobs.
      */
-    private void updateTrackedJobs(int uid, NetworkCapabilities capabilities) {
+    private void updateTrackedJobs(int uid) {
         synchronized (mLock) {
             boolean changed = false;
             for (int i = mTrackedJobs.size()-1; i >= 0; i--) {
                 final JobStatus js = mTrackedJobs.valueAt(i);
                 if (uid == -1 || uid == js.getSourceUid()) {
-                    changed |= updateConstraintsSatisfied(js, capabilities);
+                    changed |= updateConstraintsSatisfied(js);
                 }
             }
             if (changed) {
@@ -187,7 +189,7 @@
             if (DEBUG) {
                 Slog.v(TAG, "onCapabilitiesChanged() : " + networkCapabilities);
             }
-            updateTrackedJobs(-1, networkCapabilities);
+            updateTrackedJobs(-1);
         }
 
         @Override
@@ -195,7 +197,7 @@
             if (DEBUG) {
                 Slog.v(TAG, "Network lost");
             }
-            updateTrackedJobs(-1, null);
+            updateTrackedJobs(-1);
         }
     };
 
@@ -205,7 +207,7 @@
             if (DEBUG) {
                 Slog.v(TAG, "Uid rules changed for " + uid);
             }
-            updateTrackedJobs(uid, null);
+            updateTrackedJobs(uid);
         }
 
         @Override
@@ -218,7 +220,7 @@
             if (DEBUG) {
                 Slog.v(TAG, "Background restriction change to " + restrictBackground);
             }
-            updateTrackedJobs(-1, null);
+            updateTrackedJobs(-1);
         }
 
         @Override
@@ -226,7 +228,7 @@
             if (DEBUG) {
                 Slog.v(TAG, "Uid policy changed for " + uid);
             }
-            updateTrackedJobs(uid, null);
+            updateTrackedJobs(uid);
         }
     };
 
diff --git a/services/core/java/com/android/server/location/GnssLocationProvider.java b/services/core/java/com/android/server/location/GnssLocationProvider.java
index 4511aa9..83bb17e 100644
--- a/services/core/java/com/android/server/location/GnssLocationProvider.java
+++ b/services/core/java/com/android/server/location/GnssLocationProvider.java
@@ -1651,6 +1651,9 @@
                 mSvAzimuths,
                 mSvCarrierFreqs);
 
+        // Log CN0 as part of GNSS metrics
+        mGnssMetrics.logCn0(mCn0s, svCount);
+
         if (VERBOSE) {
             Log.v(TAG, "SV count: " + svCount);
         }
@@ -2513,6 +2516,7 @@
     @Override
     public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
         StringBuilder s = new StringBuilder();
+        s.append("  mStarted=").append(mStarted).append('\n');
         s.append("  mFixInterval=").append(mFixInterval).append('\n');
         s.append("  mDisableGps (battery saver mode)=").append(mDisableGps).append('\n');
         s.append("  mEngineCapabilities=0x").append(Integer.toHexString(mEngineCapabilities));
diff --git a/services/core/java/com/android/server/locksettings/LockSettingsService.java b/services/core/java/com/android/server/locksettings/LockSettingsService.java
index 8d53447..a105c84 100644
--- a/services/core/java/com/android/server/locksettings/LockSettingsService.java
+++ b/services/core/java/com/android/server/locksettings/LockSettingsService.java
@@ -1132,12 +1132,6 @@
             fixateNewestUserKeyAuth(userId);
             synchronizeUnifiedWorkChallengeForProfiles(userId, null);
             notifyActivePasswordMetricsAvailable(null, userId);
-
-            if (mStorage.getPersistentDataBlock() != null
-                    && LockPatternUtils.userOwnsFrpCredential(mUserManager.getUserInfo(userId))) {
-                // If owner, write to persistent storage for FRP
-                mStorage.writePersistentDataBlock(PersistentData.TYPE_NONE, userId, 0, null);
-            }
             return;
         }
         if (credential == null) {
@@ -1190,12 +1184,6 @@
             // Refresh the auth token
             doVerifyCredential(credential, credentialType, true, 0, userId, null /* progressCallback */);
             synchronizeUnifiedWorkChallengeForProfiles(userId, null);
-            if (mStorage.getPersistentDataBlock() != null
-                    && LockPatternUtils.userOwnsFrpCredential(mUserManager.getUserInfo(userId))) {
-                // If owner, write to persistent storage for FRP
-                mStorage.writePersistentDataBlock(PersistentData.TYPE_GATEKEEPER, userId,
-                        requestedQuality, willStore.toBytes());
-            }
         } else {
             throw new RemoteException("Failed to enroll " +
                     (credentialType == LockPatternUtils.CREDENTIAL_TYPE_PASSWORD ? "password"
@@ -1443,18 +1431,12 @@
             return response;
         }
 
-        final CredentialHash storedHash;
         if (userId == USER_FRP) {
-            PersistentData data = mStorage.readPersistentDataBlock();
-            if (data.type != PersistentData.TYPE_GATEKEEPER) {
-                Slog.wtf(TAG, "Expected PersistentData.TYPE_GATEKEEPER, but was: " + data.type);
-                return VerifyCredentialResponse.ERROR;
-            }
-            return verifyFrpCredential(credential, credentialType, data, progressCallback);
-        } else {
-            storedHash = mStorage.readCredentialHash(userId);
+            Slog.wtf(TAG, "Unexpected FRP credential type, should be SP based.");
+            return VerifyCredentialResponse.ERROR;
         }
 
+        final CredentialHash storedHash = mStorage.readCredentialHash(userId);
         if (storedHash.type != credentialType) {
             Slog.wtf(TAG, "doVerifyCredential type mismatch with stored credential??"
                     + " stored: " + storedHash.type + " passed in: " + credentialType);
@@ -1485,29 +1467,6 @@
         return response;
     }
 
-    private VerifyCredentialResponse verifyFrpCredential(String credential, int credentialType,
-            PersistentData data, ICheckCredentialProgressCallback progressCallback)
-            throws RemoteException {
-        CredentialHash storedHash = CredentialHash.fromBytes(data.payload);
-        if (storedHash.type != credentialType) {
-            Slog.wtf(TAG, "doVerifyCredential type mismatch with stored credential??"
-                    + " stored: " + storedHash.type + " passed in: " + credentialType);
-            return VerifyCredentialResponse.ERROR;
-        }
-        if (ArrayUtils.isEmpty(storedHash.hash) || TextUtils.isEmpty(credential)) {
-            Slog.e(TAG, "Stored hash or credential is empty");
-            return VerifyCredentialResponse.ERROR;
-        }
-        VerifyCredentialResponse response = VerifyCredentialResponse.fromGateKeeperResponse(
-                getGateKeeperService().verifyChallenge(data.userId, 0 /* challenge */,
-                        storedHash.hash, credential.getBytes()));
-        if (progressCallback != null
-                && response.getResponseCode() == VerifyCredentialResponse.RESPONSE_OK) {
-            progressCallback.onCredentialVerified();
-        }
-        return response;
-    }
-
     @Override
     public VerifyCredentialResponse verifyTiedProfileChallenge(String credential, int type,
             long challenge, int userId) throws RemoteException {
diff --git a/services/core/java/com/android/server/locksettings/LockSettingsStorage.java b/services/core/java/com/android/server/locksettings/LockSettingsStorage.java
index 79372e48..b4c10ec 100644
--- a/services/core/java/com/android/server/locksettings/LockSettingsStorage.java
+++ b/services/core/java/com/android/server/locksettings/LockSettingsStorage.java
@@ -635,9 +635,8 @@
         static final int VERSION_1_HEADER_SIZE = 1 + 1 + 4 + 4;
 
         public static final int TYPE_NONE = 0;
-        public static final int TYPE_GATEKEEPER = 1;
-        public static final int TYPE_SP = 2;
-        public static final int TYPE_SP_WEAVER = 3;
+        public static final int TYPE_SP = 1;
+        public static final int TYPE_SP_WEAVER = 2;
 
         public static final PersistentData NONE = new PersistentData(TYPE_NONE,
                 UserHandle.USER_NULL, DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED, null);
diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
index b6af076..38f1c07 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
@@ -18,7 +18,7 @@
 
 import static android.Manifest.permission.ACCESS_NETWORK_STATE;
 import static android.Manifest.permission.CONNECTIVITY_INTERNAL;
-import static android.Manifest.permission.DUMP;
+import static android.Manifest.permission.MANAGE_FALLBACK_SUBSCRIPTION_PLANS;
 import static android.Manifest.permission.MANAGE_NETWORK_POLICY;
 import static android.Manifest.permission.READ_NETWORK_USAGE_HISTORY;
 import static android.Manifest.permission.READ_PHONE_STATE;
@@ -28,14 +28,12 @@
 import static android.content.Intent.ACTION_USER_ADDED;
 import static android.content.Intent.ACTION_USER_REMOVED;
 import static android.content.Intent.EXTRA_UID;
+import static android.content.pm.PackageManager.PERMISSION_GRANTED;
 import static android.net.ConnectivityManager.CONNECTIVITY_ACTION;
 import static android.net.ConnectivityManager.RESTRICT_BACKGROUND_STATUS_DISABLED;
 import static android.net.ConnectivityManager.RESTRICT_BACKGROUND_STATUS_ENABLED;
 import static android.net.ConnectivityManager.RESTRICT_BACKGROUND_STATUS_WHITELISTED;
 import static android.net.ConnectivityManager.TYPE_MOBILE;
-import static android.net.ConnectivityManager.TYPE_WIMAX;
-import static android.net.ConnectivityManager.isNetworkTypeMobile;
-import static android.net.NetworkPolicy.CYCLE_NONE;
 import static android.net.NetworkPolicy.LIMIT_DISABLED;
 import static android.net.NetworkPolicy.SNOOZE_NEVER;
 import static android.net.NetworkPolicy.WARNING_DISABLED;
@@ -46,20 +44,20 @@
 import static android.net.NetworkPolicyManager.FIREWALL_RULE_ALLOW;
 import static android.net.NetworkPolicyManager.FIREWALL_RULE_DEFAULT;
 import static android.net.NetworkPolicyManager.FIREWALL_RULE_DENY;
+import static android.net.NetworkPolicyManager.MASK_ALL_NETWORKS;
+import static android.net.NetworkPolicyManager.MASK_METERED_NETWORKS;
 import static android.net.NetworkPolicyManager.POLICY_ALLOW_METERED_BACKGROUND;
 import static android.net.NetworkPolicyManager.POLICY_NONE;
 import static android.net.NetworkPolicyManager.POLICY_REJECT_METERED_BACKGROUND;
 import static android.net.NetworkPolicyManager.RULE_ALLOW_ALL;
 import static android.net.NetworkPolicyManager.RULE_ALLOW_METERED;
-import static android.net.NetworkPolicyManager.MASK_METERED_NETWORKS;
-import static android.net.NetworkPolicyManager.MASK_ALL_NETWORKS;
 import static android.net.NetworkPolicyManager.RULE_NONE;
 import static android.net.NetworkPolicyManager.RULE_REJECT_ALL;
 import static android.net.NetworkPolicyManager.RULE_REJECT_METERED;
 import static android.net.NetworkPolicyManager.RULE_TEMPORARY_ALLOW_METERED;
-import static android.net.NetworkPolicyManager.computeLastCycleBoundary;
 import static android.net.NetworkPolicyManager.isProcStateAllowedWhileIdleOrPowerSaveMode;
 import static android.net.NetworkPolicyManager.isProcStateAllowedWhileOnRestrictBackground;
+import static android.net.NetworkPolicyManager.resolveNetworkId;
 import static android.net.NetworkPolicyManager.uidPoliciesToString;
 import static android.net.NetworkPolicyManager.uidRulesToString;
 import static android.net.NetworkTemplate.MATCH_MOBILE_3G_LOWER;
@@ -68,16 +66,9 @@
 import static android.net.NetworkTemplate.MATCH_WIFI;
 import static android.net.NetworkTemplate.buildTemplateMobileAll;
 import static android.net.TrafficStats.MB_IN_BYTES;
-import static android.net.wifi.WifiManager.CHANGE_REASON_ADDED;
-import static android.net.wifi.WifiManager.CHANGE_REASON_REMOVED;
-import static android.net.wifi.WifiManager.CONFIGURED_NETWORKS_CHANGED_ACTION;
-import static android.net.wifi.WifiManager.EXTRA_CHANGE_REASON;
-import static android.net.wifi.WifiManager.EXTRA_NETWORK_INFO;
-import static android.net.wifi.WifiManager.EXTRA_WIFI_CONFIGURATION;
-import static android.net.wifi.WifiManager.EXTRA_WIFI_INFO;
 import static android.telephony.CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED;
-import static android.telephony.CarrierConfigManager.DATA_CYCLE_USE_PLATFORM_DEFAULT;
 import static android.telephony.CarrierConfigManager.DATA_CYCLE_THRESHOLD_DISABLED;
+import static android.telephony.CarrierConfigManager.DATA_CYCLE_USE_PLATFORM_DEFAULT;
 import static android.text.format.DateUtils.DAY_IN_MILLIS;
 
 import static com.android.internal.util.ArrayUtils.appendInt;
@@ -127,15 +118,14 @@
 import android.net.INetworkStatsService;
 import android.net.LinkProperties;
 import android.net.NetworkIdentity;
-import android.net.NetworkInfo;
 import android.net.NetworkPolicy;
+import android.net.NetworkPolicyManager;
 import android.net.NetworkQuotaInfo;
 import android.net.NetworkState;
 import android.net.NetworkTemplate;
+import android.net.TrafficStats;
 import android.net.wifi.WifiConfiguration;
-import android.net.wifi.WifiInfo;
 import android.net.wifi.WifiManager;
-import android.os.PowerSaveState;
 import android.os.Binder;
 import android.os.Environment;
 import android.os.Handler;
@@ -147,18 +137,23 @@
 import android.os.PersistableBundle;
 import android.os.PowerManager;
 import android.os.PowerManagerInternal;
+import android.os.PowerSaveState;
 import android.os.Process;
 import android.os.RemoteCallbackList;
 import android.os.RemoteException;
 import android.os.ResultReceiver;
 import android.os.ServiceManager;
 import android.os.ShellCallback;
+import android.os.SystemProperties;
 import android.os.Trace;
 import android.os.UserHandle;
 import android.os.UserManager;
 import android.provider.Settings;
+import android.provider.Settings.Global;
 import android.telephony.CarrierConfigManager;
+import android.telephony.SubscriptionInfo;
 import android.telephony.SubscriptionManager;
+import android.telephony.SubscriptionPlan;
 import android.telephony.TelephonyManager;
 import android.text.TextUtils;
 import android.text.format.Formatter;
@@ -190,8 +185,8 @@
 import com.android.server.LocalServices;
 import com.android.server.ServiceThread;
 import com.android.server.SystemConfig;
-
 import com.android.server.power.BatterySaverPolicy.ServiceType;
+
 import libcore.io.IoUtils;
 
 import com.google.android.collect.Lists;
@@ -210,10 +205,11 @@
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 import java.nio.charset.StandardCharsets;
+import java.time.ZonedDateTime;
 import java.util.ArrayList;
 import java.util.Arrays;
-import java.util.List;
 import java.util.Calendar;
+import java.util.List;
 import java.util.Objects;
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.TimeUnit;
@@ -756,15 +752,10 @@
             mContext.registerReceiver(mSnoozeWarningReceiver, snoozeWarningFilter,
                     MANAGE_NETWORK_POLICY, mHandler);
 
-            // listen for configured wifi networks to be removed
-            final IntentFilter wifiConfigFilter =
-                    new IntentFilter(CONFIGURED_NETWORKS_CHANGED_ACTION);
-            mContext.registerReceiver(mWifiConfigReceiver, wifiConfigFilter, null, mHandler);
-
-            // listen for wifi state changes to catch metered hint
-            final IntentFilter wifiStateFilter = new IntentFilter(
-                    WifiManager.NETWORK_STATE_CHANGED_ACTION);
-            mContext.registerReceiver(mWifiStateReceiver, wifiStateFilter, null, mHandler);
+            // listen for configured wifi networks to be loaded
+            final IntentFilter wifiFilter =
+                    new IntentFilter(WifiManager.CONFIGURED_NETWORKS_CHANGED_ACTION);
+            mContext.registerReceiver(mWifiReceiver, wifiFilter, null, mHandler);
 
             // listen for carrier config changes to update data cycle information
             final IntentFilter carrierConfigFilter = new IntentFilter(
@@ -960,80 +951,22 @@
     };
 
     /**
-     * Receiver that watches for {@link WifiConfiguration} to be changed.
+     * Receiver that watches for {@link WifiConfiguration} to be loaded so that
+     * we can perform upgrade logic.
      */
-    final private BroadcastReceiver mWifiConfigReceiver = new BroadcastReceiver() {
+    final private BroadcastReceiver mWifiReceiver = new BroadcastReceiver() {
         @Override
         public void onReceive(Context context, Intent intent) {
-            // on background handler thread, and verified CONNECTIVITY_INTERNAL
-            // permission above.
-
-            final int reason = intent.getIntExtra(EXTRA_CHANGE_REASON, CHANGE_REASON_ADDED);
-            if (reason == CHANGE_REASON_REMOVED) {
-                final WifiConfiguration config = intent.getParcelableExtra(
-                        EXTRA_WIFI_CONFIGURATION);
-                if (config.SSID != null) {
-                    final NetworkTemplate template = NetworkTemplate.buildTemplateWifi(config.SSID);
-                    synchronized (mUidRulesFirstLock) {
-                        synchronized (mNetworkPoliciesSecondLock) {
-                            if (mNetworkPolicy.containsKey(template)) {
-                                mNetworkPolicy.remove(template);
-                                writePolicyAL();
-                            }
-                        }
-                    }
-                }
-            }
-        }
-    };
-
-    /**
-     * Receiver that watches {@link WifiInfo} state changes to infer metered
-     * state. Ignores hints when policy is user-defined.
-     */
-    final private BroadcastReceiver mWifiStateReceiver = new BroadcastReceiver() {
-        @Override
-        public void onReceive(Context context, Intent intent) {
-            // on background handler thread, and verified CONNECTIVITY_INTERNAL
-            // permission above.
-
-            // ignore when not connected
-            final NetworkInfo netInfo = intent.getParcelableExtra(EXTRA_NETWORK_INFO);
-            if (!netInfo.isConnected()) return;
-
-            final WifiInfo info = intent.getParcelableExtra(EXTRA_WIFI_INFO);
-            final boolean meteredHint = info.getMeteredHint();
-
-            final NetworkTemplate template = NetworkTemplate.buildTemplateWifi(info.getSSID());
             synchronized (mUidRulesFirstLock) {
                 synchronized (mNetworkPoliciesSecondLock) {
-                    NetworkPolicy policy = mNetworkPolicy.get(template);
-                    if (policy == null && meteredHint) {
-                        // policy doesn't exist, and AP is hinting that it's
-                        // metered: create an inferred policy.
-                        policy = newWifiPolicy(template, meteredHint);
-                        addNetworkPolicyAL(policy);
-
-                    } else if (policy != null && policy.inferred) {
-                        // policy exists, and was inferred: update its current
-                        // metered state.
-                        policy.metered = meteredHint;
-
-                        // since this is inferred for each wifi session, just update
-                        // rules without persisting.
-                        updateNetworkRulesNL();
-                    }
+                    upgradeWifiMeteredOverrideAL();
                 }
             }
+            // Only need to perform upgrade logic once
+            mContext.unregisterReceiver(this);
         }
     };
 
-    static NetworkPolicy newWifiPolicy(NetworkTemplate template, boolean metered) {
-        return new NetworkPolicy(template, CYCLE_NONE, Time.TIMEZONE_UTC,
-                WARNING_DISABLED, LIMIT_DISABLED, SNOOZE_NEVER, SNOOZE_NEVER,
-                metered, true);
-    }
-
     /**
      * Observer that watches for {@link INetworkManagementService} alerts.
      */
@@ -1072,8 +1005,10 @@
             if (!isTemplateRelevant(policy.template)) continue;
             if (!policy.hasCycle()) continue;
 
-            final long start = computeLastCycleBoundary(currentTime, policy);
-            final long end = currentTime;
+            final Pair<ZonedDateTime, ZonedDateTime> cycle = NetworkPolicyManager
+                    .cycleIterator(policy).next();
+            final long start = cycle.first.toInstant().toEpochMilli();
+            final long end = cycle.second.toInstant().toEpochMilli();
             final long totalBytes = getTotalBytes(policy.template, start, end);
 
             if (policy.isOverLimit(totalBytes)) {
@@ -1530,8 +1465,10 @@
                 continue;
             }
 
-            final long start = computeLastCycleBoundary(currentTime, policy);
-            final long end = currentTime;
+            final Pair<ZonedDateTime, ZonedDateTime> cycle = NetworkPolicyManager
+                    .cycleIterator(policy).next();
+            final long start = cycle.first.toInstant().toEpochMilli();
+            final long end = cycle.second.toInstant().toEpochMilli();
             final long totalBytes = getTotalBytes(policy.template, start, end);
 
             // disable data connection when over limit and not snoozed
@@ -1641,15 +1578,11 @@
             final NetworkPolicy policy = mNetworkRules.keyAt(i);
             final String[] ifaces = mNetworkRules.valueAt(i);
 
-            final long start;
-            final long totalBytes;
-            if (policy.hasCycle()) {
-                start = computeLastCycleBoundary(currentTime, policy);
-                totalBytes = getTotalBytes(policy.template, start, currentTime);
-            } else {
-                start = Long.MAX_VALUE;
-                totalBytes = 0;
-            }
+            final Pair<ZonedDateTime, ZonedDateTime> cycle = NetworkPolicyManager
+                    .cycleIterator(policy).next();
+            final long start = cycle.first.toInstant().toEpochMilli();
+            final long end = cycle.second.toInstant().toEpochMilli();
+            final long totalBytes = getTotalBytes(policy.template, start, end);
 
             if (LOGD) {
                 Slog.d(TAG, "applying policy " + policy + " to ifaces " + Arrays.toString(ifaces));
@@ -1905,7 +1838,6 @@
                                     cycleTimezone, warningBytes, limitBytes, lastWarningSnooze,
                                     lastLimitSnooze, metered, inferred));
                         }
-
                     } else if (TAG_UID_POLICY.equals(tag)) {
                         final int uid = readIntAttribute(in, ATTR_UID);
                         final int policy = readIntAttribute(in, ATTR_POLICY);
@@ -1965,7 +1897,7 @@
 
         } catch (FileNotFoundException e) {
             // missing policy is okay, probably first boot
-            upgradeLegacyBackgroundDataUL();
+            upgradeDefaultBackgroundDataUL();
         } catch (IOException e) {
             Log.wtf(TAG, "problem reading network policy", e);
         } catch (XmlPullParserException e) {
@@ -1979,15 +1911,55 @@
      * Upgrade legacy background data flags, notifying listeners of one last
      * change to always-true.
      */
-    private void upgradeLegacyBackgroundDataUL() {
-        mRestrictBackground = Settings.Secure.getInt(
-                mContext.getContentResolver(), Settings.Secure.BACKGROUND_DATA, 1) != 1;
+    private void upgradeDefaultBackgroundDataUL() {
+        // This method is only called when we're unable to find the network policy flag, which
+        // usually happens on first boot of a new device and not one that has received an OTA.
 
-        // kick off one last broadcast if restricted
-        if (mRestrictBackground) {
-            final Intent broadcast = new Intent(
-                    ConnectivityManager.ACTION_BACKGROUND_DATA_SETTING_CHANGED);
-            mContext.sendBroadcastAsUser(broadcast, UserHandle.ALL);
+        // Seed from the default value configured for this device.
+        mRestrictBackground = Settings.Global.getInt(
+                mContext.getContentResolver(), Global.DEFAULT_RESTRICT_BACKGROUND_DATA, 0) == 1;
+
+        // NOTE: We used to read the legacy setting here :
+        //
+        // final int legacyFlagValue = Settings.Secure.getInt(
+        //        mContext.getContentResolver(), Settings.Secure.BACKGROUND_DATA, ..);
+        //
+        // This is no longer necessary because we will never upgrade directly from Gingerbread
+        // to O+. Devices upgrading from ICS onwards to O will have a netpolicy.xml file that
+        // contains the correct value that we will continue to use.
+    }
+
+    /**
+     * Perform upgrade step of moving any user-defined meterness overrides over
+     * into {@link WifiConfiguration}.
+     */
+    private void upgradeWifiMeteredOverrideAL() {
+        boolean modified = false;
+        final WifiManager wm = mContext.getSystemService(WifiManager.class);
+        final List<WifiConfiguration> configs = wm.getConfiguredNetworks();
+        for (int i = 0; i < mNetworkPolicy.size(); ) {
+            final NetworkPolicy policy = mNetworkPolicy.valueAt(i);
+            if (policy.template.getMatchRule() == NetworkTemplate.MATCH_WIFI
+                    && !policy.inferred) {
+                mNetworkPolicy.removeAt(i);
+                modified = true;
+
+                final String networkId = resolveNetworkId(policy.template.getNetworkId());
+                for (WifiConfiguration config : configs) {
+                    if (Objects.equals(resolveNetworkId(config), networkId)) {
+                        Slog.d(TAG, "Found network " + networkId + "; upgrading metered hint");
+                        config.meteredOverride = policy.metered
+                                ? WifiConfiguration.METERED_OVERRIDE_METERED
+                                : WifiConfiguration.METERED_OVERRIDE_NOT_METERED;
+                        wm.updateNetwork(config);
+                    }
+                }
+            } else {
+                i++;
+            }
+        }
+        if (modified) {
+            writePolicyAL();
         }
     }
 
@@ -2495,81 +2467,202 @@
         }
     }
 
-    private NetworkPolicy findPolicyForNetworkNL(NetworkIdentity ident) {
-        for (int i = mNetworkPolicy.size()-1; i >= 0; i--) {
-            NetworkPolicy policy = mNetworkPolicy.valueAt(i);
-            if (policy.template.matches(ident)) {
-                return policy;
-            }
-        }
-        return null;
-    }
-
     @Override
-    public NetworkQuotaInfo getNetworkQuotaInfo(NetworkState state) {
-        mContext.enforceCallingOrSelfPermission(ACCESS_NETWORK_STATE, TAG);
-
-        // only returns usage summary, so we don't require caller to have
-        // READ_NETWORK_USAGE_HISTORY.
+    public void setWifiMeteredOverride(String networkId, int meteredOverride) {
+        mContext.enforceCallingOrSelfPermission(MANAGE_NETWORK_POLICY, TAG);
         final long token = Binder.clearCallingIdentity();
         try {
-            return getNetworkQuotaInfoUnchecked(state);
+            final WifiManager wm = mContext.getSystemService(WifiManager.class);
+            final List<WifiConfiguration> configs = wm.getConfiguredNetworks();
+            for (WifiConfiguration config : configs) {
+                if (Objects.equals(resolveNetworkId(config), networkId)) {
+                    config.meteredOverride = meteredOverride;
+                    wm.updateNetwork(config);
+                }
+            }
         } finally {
             Binder.restoreCallingIdentity(token);
         }
     }
 
-    private NetworkQuotaInfo getNetworkQuotaInfoUnchecked(NetworkState state) {
-        final NetworkIdentity ident = NetworkIdentity.buildNetworkIdentity(mContext, state);
+    @Override
+    @Deprecated
+    public NetworkQuotaInfo getNetworkQuotaInfo(NetworkState state) {
+        Log.w(TAG, "Shame on UID " + Binder.getCallingUid()
+                + " for calling the hidden API getNetworkQuotaInfo(). Shame!");
+        return new NetworkQuotaInfo();
+    }
 
-        final NetworkPolicy policy;
-        synchronized (mNetworkPoliciesSecondLock) {
-            policy = findPolicyForNetworkNL(ident);
+    private void enforceSubscriptionPlanAccess(int subId, int callingUid, String callingPackage) {
+        // Verify they're not lying about package name
+        mAppOps.checkPackage(callingUid, callingPackage);
+
+        // Verify they have phone permission from user
+        mContext.enforceCallingOrSelfPermission(READ_PHONE_STATE, TAG);
+        if (mAppOps.checkOp(AppOpsManager.OP_READ_PHONE_STATE, callingUid,
+                callingPackage) != AppOpsManager.MODE_ALLOWED) {
+            throw new SecurityException(
+                    "Calling package " + callingPackage + " does not hold " + READ_PHONE_STATE);
         }
 
-        if (policy == null || !policy.hasCycle()) {
-            // missing policy means we can't derive useful quota info
-            return null;
+        final SubscriptionInfo si;
+        final long token = Binder.clearCallingIdentity();
+        try {
+            si = mContext.getSystemService(SubscriptionManager.class)
+                    .getActiveSubscriptionInfo(subId);
+        } finally {
+            Binder.restoreCallingIdentity(token);
         }
 
-        final long currentTime = currentTimeMillis();
+        // First check: does caller have carrier access?
+        if (si.isEmbedded() && si.canManageSubscription(mContext, callingPackage)) {
+            Slog.v(TAG, "Granting access because " + callingPackage + " is carrier");
+            return;
+        }
 
-        // find total bytes used under policy
-        final long start = computeLastCycleBoundary(currentTime, policy);
-        final long end = currentTime;
-        final long totalBytes = getTotalBytes(policy.template, start, end);
+        // Second check: was caller first to claim this HNI?
+        // TODO: extend to support external data sources
 
-        // report soft and hard limits under policy
-        final long softLimitBytes = policy.warningBytes != WARNING_DISABLED ? policy.warningBytes
-                : NetworkQuotaInfo.NO_LIMIT;
-        final long hardLimitBytes = policy.limitBytes != LIMIT_DISABLED ? policy.limitBytes
-                : NetworkQuotaInfo.NO_LIMIT;
+        // Final check: does caller have fallback permission?
+        if (mContext.checkCallingOrSelfPermission(
+                MANAGE_FALLBACK_SUBSCRIPTION_PLANS) == PERMISSION_GRANTED) {
+            Slog.v(TAG, "Granting access because " + callingPackage + " is fallback");
+            return;
+        }
 
-        return new NetworkQuotaInfo(totalBytes, softLimitBytes, hardLimitBytes);
+        throw new SecurityException("Calling package " + callingPackage
+                + " has no access to subscription plans for " + subId);
     }
 
     @Override
-    public boolean isNetworkMetered(NetworkState state) {
-        if (state.networkInfo == null) {
-            return false;
+    public SubscriptionPlan[] getSubscriptionPlans(int subId, String callingPackage) {
+        enforceSubscriptionPlanAccess(subId, Binder.getCallingUid(), callingPackage);
+
+        // TODO: extend to support external data sources
+        if (!"com.android.settings".equals(callingPackage)) {
+            throw new UnsupportedOperationException();
         }
 
-        final NetworkIdentity ident = NetworkIdentity.buildNetworkIdentity(mContext, state);
-
-        final NetworkPolicy policy;
-        synchronized (mNetworkPoliciesSecondLock) {
-            policy = findPolicyForNetworkNL(ident);
-        }
-
-        if (policy != null) {
-            return policy.metered;
-        } else {
-            final int type = state.networkInfo.getType();
-            if ((isNetworkTypeMobile(type) && ident.getMetered()) || type == TYPE_WIMAX) {
-                return true;
+        final String fake = SystemProperties.get("fw.fake_plan");
+        if (!TextUtils.isEmpty(fake)) {
+            final List<SubscriptionPlan> plans = new ArrayList<>();
+            if ("month_hard".equals(fake)) {
+                plans.add(SubscriptionPlan.Builder
+                        .createRecurringMonthly(ZonedDateTime.parse("2007-03-14T00:00:00.000Z"))
+                        .setTitle("G-Mobile")
+                        .setDataWarning(2 * TrafficStats.GB_IN_BYTES)
+                        .setDataLimit(5 * TrafficStats.GB_IN_BYTES,
+                                SubscriptionPlan.LIMIT_BEHAVIOR_BILLED)
+                        .setDataUsage(1 * TrafficStats.GB_IN_BYTES,
+                                ZonedDateTime.now().minusHours(36).toInstant().toEpochMilli())
+                        .build());
+            } else if ("month_soft".equals(fake)) {
+                plans.add(SubscriptionPlan.Builder
+                        .createRecurringMonthly(ZonedDateTime.parse("2007-03-14T00:00:00.000Z"))
+                        .setTitle("G-Mobile is the carriers name who this plan belongs to")
+                        .setSummary("Crazy unlimited bandwidth plan with incredibly long title "
+                                + "that should be cut off to prevent UI from looking terrible")
+                        .setDataWarning(2 * TrafficStats.GB_IN_BYTES)
+                        .setDataLimit(5 * TrafficStats.GB_IN_BYTES,
+                                SubscriptionPlan.LIMIT_BEHAVIOR_THROTTLED)
+                        .setDataUsage(1 * TrafficStats.GB_IN_BYTES,
+                                ZonedDateTime.now().minusHours(1).toInstant().toEpochMilli())
+                        .build());
+            } else if ("month_none".equals(fake)) {
+                plans.add(SubscriptionPlan.Builder
+                        .createRecurringMonthly(ZonedDateTime.parse("2007-03-14T00:00:00.000Z"))
+                        .setTitle("G-Mobile")
+                        .build());
+            } else if ("prepaid".equals(fake)) {
+                plans.add(SubscriptionPlan.Builder
+                        .createNonrecurring(ZonedDateTime.now().minusDays(20),
+                                ZonedDateTime.now().plusDays(10))
+                        .setTitle("G-Mobile")
+                        .setDataLimit(512 * TrafficStats.MB_IN_BYTES,
+                                SubscriptionPlan.LIMIT_BEHAVIOR_DISABLED)
+                        .setDataUsage(100 * TrafficStats.MB_IN_BYTES,
+                                ZonedDateTime.now().minusHours(3).toInstant().toEpochMilli())
+                        .build());
+            } else if ("prepaid_crazy".equals(fake)) {
+                plans.add(SubscriptionPlan.Builder
+                        .createNonrecurring(ZonedDateTime.now().minusDays(20),
+                                ZonedDateTime.now().plusDays(10))
+                        .setTitle("G-Mobile Anytime")
+                        .setDataLimit(512 * TrafficStats.MB_IN_BYTES,
+                                SubscriptionPlan.LIMIT_BEHAVIOR_DISABLED)
+                        .setDataUsage(100 * TrafficStats.MB_IN_BYTES,
+                                ZonedDateTime.now().minusHours(3).toInstant().toEpochMilli())
+                        .build());
+                plans.add(SubscriptionPlan.Builder
+                        .createNonrecurring(ZonedDateTime.now().minusDays(10),
+                                ZonedDateTime.now().plusDays(20))
+                        .setTitle("G-Mobile Nickel Nights")
+                        .setSummary("5¢/GB between 1-5AM")
+                        .setDataUsage(15 * TrafficStats.MB_IN_BYTES,
+                                ZonedDateTime.now().minusHours(30).toInstant().toEpochMilli())
+                        .build());
+                plans.add(SubscriptionPlan.Builder
+                        .createNonrecurring(ZonedDateTime.now().minusDays(10),
+                                ZonedDateTime.now().plusDays(20))
+                        .setTitle("G-Mobile Bonus 3G")
+                        .setSummary("Unlimited 3G data")
+                        .setDataLimit(5 * TrafficStats.GB_IN_BYTES,
+                                SubscriptionPlan.LIMIT_BEHAVIOR_THROTTLED)
+                        .setDataUsage(300 * TrafficStats.MB_IN_BYTES,
+                                ZonedDateTime.now().minusHours(1).toInstant().toEpochMilli())
+                        .build());
             }
-            return false;
+            return plans.toArray(new SubscriptionPlan[plans.size()]);
         }
+
+        final long token = Binder.clearCallingIdentity();
+        try {
+            final TelephonyManager tm = mContext.getSystemService(TelephonyManager.class);
+            final NetworkTemplate template = NetworkTemplate
+                    .buildTemplateMobileAll(tm.getSubscriberId(subId));
+            final NetworkPolicy policy = mNetworkPolicy.get(template);
+            if (policy != null) {
+                return new SubscriptionPlan[] { SubscriptionPlan.convert(policy) };
+            } else {
+                return new SubscriptionPlan[0];
+            }
+        } finally {
+            Binder.restoreCallingIdentity(token);
+        }
+    }
+
+    @Override
+    public void setSubscriptionPlans(int subId, SubscriptionPlan[] plans, String callingPackage) {
+        enforceSubscriptionPlanAccess(subId, Binder.getCallingUid(), callingPackage);
+
+        // TODO: extend to support external data sources
+        if (!"com.android.settings".equals(callingPackage)) {
+            throw new UnsupportedOperationException();
+        }
+
+        final long token = Binder.clearCallingIdentity();
+        try {
+            final TelephonyManager tm = mContext.getSystemService(TelephonyManager.class);
+            final NetworkTemplate template = NetworkTemplate
+                    .buildTemplateMobileAll(tm.getSubscriberId(subId));
+            if (ArrayUtils.isEmpty(plans)) {
+                mNetworkPolicy.remove(template);
+            } else {
+                final NetworkPolicy policy = SubscriptionPlan.convert(plans[0]);
+                policy.template = template;
+                mNetworkPolicy.put(template, policy);
+            }
+        } finally {
+            Binder.restoreCallingIdentity(token);
+        }
+    }
+
+    @Override
+    public String getSubscriptionPlanOwner(int subId) {
+        mContext.enforceCallingOrSelfPermission(MANAGE_NETWORK_POLICY, TAG);
+
+        // TODO: extend to support external data sources
+        return "com.android.settings";
     }
 
     @Override
diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerShellCommand.java b/services/core/java/com/android/server/net/NetworkPolicyManagerShellCommand.java
index 8ced1c2..b65b9d7 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyManagerShellCommand.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyManagerShellCommand.java
@@ -19,26 +19,17 @@
 import static android.net.NetworkPolicyManager.POLICY_ALLOW_METERED_BACKGROUND;
 import static android.net.NetworkPolicyManager.POLICY_NONE;
 import static android.net.NetworkPolicyManager.POLICY_REJECT_METERED_BACKGROUND;
-import static android.net.wifi.WifiInfo.removeDoubleQuotes;
-
-import static com.android.server.net.NetworkPolicyManagerService.newWifiPolicy;
-import static com.android.server.net.NetworkPolicyManagerService.TAG;
-
-import java.io.PrintWriter;
-import java.util.ArrayList;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Set;
 
 import android.content.Context;
 import android.net.INetworkPolicyManager;
-import android.net.NetworkPolicy;
-import android.net.NetworkTemplate;
+import android.net.NetworkPolicyManager;
 import android.net.wifi.WifiConfiguration;
 import android.net.wifi.WifiManager;
 import android.os.RemoteException;
 import android.os.ShellCommand;
-import android.util.Log;
+
+import java.io.PrintWriter;
+import java.util.List;
 
 class NetworkPolicyManagerShellCommand extends ShellCommand {
 
@@ -90,7 +81,7 @@
         pw.println("    Adds a UID to the blacklist for restrict background usage.");
         pw.println("  get restrict-background");
         pw.println("    Gets the global restrict background usage status.");
-        pw.println("  list wifi-networks [BOOLEAN]");
+        pw.println("  list wifi-networks [true|false]");
         pw.println("    Lists all saved wifi networks and whether they are metered or not.");
         pw.println("    If a boolean argument is passed, filters just the metered (or unmetered)");
         pw.println("    networks.");
@@ -102,7 +93,7 @@
         pw.println("    Removes a UID from the whitelist for restrict background usage.");
         pw.println("  remove restrict-background-blacklist UID");
         pw.println("    Removes a UID from the blacklist for restrict background usage.");
-        pw.println("  set metered-network ID BOOLEAN");
+        pw.println("  set metered-network ID [undefined|true|false]");
         pw.println("    Toggles whether the given wi-fi network is metered.");
         pw.println("  set restrict-background BOOLEAN");
         pw.println("    Sets the global restrict background usage status.");
@@ -276,107 +267,60 @@
         return resetUidPolicy("not blacklisted", POLICY_REJECT_METERED_BACKGROUND);
     }
 
-    private int listWifiNetworks() throws RemoteException {
+    private int listWifiNetworks() {
         final PrintWriter pw = getOutPrintWriter();
         final String arg = getNextArg();
-        final Boolean filter = arg == null ? null : Boolean.valueOf(arg);
-        for (NetworkPolicy policy : getWifiPolicies()) {
-            if (filter != null && filter.booleanValue() != policy.metered) {
-                continue;
+        final int match;
+        if (arg == null) {
+            match = WifiConfiguration.METERED_OVERRIDE_NONE;
+        } else if (Boolean.parseBoolean(arg)) {
+            match = WifiConfiguration.METERED_OVERRIDE_METERED;
+        } else {
+            match = WifiConfiguration.METERED_OVERRIDE_NOT_METERED;
+        }
+
+        final List<WifiConfiguration> configs = mWifiManager.getConfiguredNetworks();
+        for (WifiConfiguration config : configs) {
+            if (arg == null || config.meteredOverride == match) {
+                pw.print(NetworkPolicyManager.resolveNetworkId(config));
+                pw.print(';');
+                pw.println(overrideToString(config.meteredOverride));
             }
-            pw.print(getNetworkId(policy));
-            pw.print(';');
-            pw.println(policy.metered);
         }
         return 0;
     }
 
     private int setMeteredWifiNetwork() throws RemoteException {
         final PrintWriter pw = getOutPrintWriter();
-        final String id = getNextArg();
-        if (id == null) {
-            pw.println("Error: didn't specify ID");
+        final String networkId = getNextArg();
+        if (networkId == null) {
+            pw.println("Error: didn't specify networkId");
             return -1;
         }
         final String arg = getNextArg();
         if (arg == null) {
-            pw.println("Error: didn't specify BOOLEAN");
+            pw.println("Error: didn't specify meteredOverride");
             return -1;
         }
-        final boolean metered = Boolean.valueOf(arg);
-        final NetworkPolicy[] policies = mInterface.getNetworkPolicies(null);
-        boolean changed = false;
-        // First try to find a policy with such id
-        for (NetworkPolicy policy : policies) {
-            if (policy.template.isMatchRuleMobile() || policy.metered == metered) {
-                continue;
-            }
-            final String networkId = getNetworkId(policy);
-            if (id.equals(networkId)) {
-                Log.i(TAG, "Changing " + networkId + " metered status to " + metered);
-                policy.metered = metered;
-                changed = true;
-            }
-        }
-        if (changed) {
-            mInterface.setNetworkPolicies(policies);
-            return 0;
-        }
-        // Policy not found: check if there is a saved wi-fi with such id.
-        for (WifiConfiguration config : mWifiManager.getConfiguredNetworks()) {
-            final String ssid = removeDoubleQuotes(config.SSID);
-            if (id.equals(ssid)) {
-                final NetworkPolicy policy = newPolicy(ssid);
-                policy.metered = true;
-                Log.i(TAG, "Creating new policy for " + ssid + ": " + policy);
-                final NetworkPolicy[] newPolicies = new NetworkPolicy[policies.length + 1];
-                System.arraycopy(policies, 0, newPolicies, 0, policies.length);
-                newPolicies[newPolicies.length - 1] = policy;
-                mInterface.setNetworkPolicies(newPolicies);
-                return 0;
-            }
-        }
-        pw.print("Error: didn't find network with SSID "); pw.println(id);
+        mInterface.setWifiMeteredOverride(NetworkPolicyManager.resolveNetworkId(networkId),
+                stringToOverride(arg));
         return -1;
     }
 
-    private List<NetworkPolicy> getWifiPolicies() throws RemoteException {
-        // First gets a list of saved wi-fi networks.
-        final List<WifiConfiguration> configs = mWifiManager.getConfiguredNetworks();
-        final int size = configs != null ? configs.size() : 0;
-        final Set<String> ssids = new HashSet<>(size);
-        if (configs != null) {
-            for (WifiConfiguration config : configs) {
-                ssids.add(removeDoubleQuotes(config.SSID));
-            }
+    private static String overrideToString(int override) {
+        switch (override) {
+            case WifiConfiguration.METERED_OVERRIDE_METERED: return "true";
+            case WifiConfiguration.METERED_OVERRIDE_NOT_METERED: return "false";
+            default: return "none";
         }
-
-        // Then gets the saved policies.
-        final NetworkPolicy[] policies = mInterface.getNetworkPolicies(null);
-        final List<NetworkPolicy> wifiPolicies = new ArrayList<NetworkPolicy>(policies.length);
-        for (NetworkPolicy policy: policies) {
-            if (!policy.template.isMatchRuleMobile()) {
-                wifiPolicies.add(policy);
-                final String netId = getNetworkId(policy);
-                ssids.remove(netId);
-            }
-        }
-        // Finally, creates new default policies for saved WI-FIs not policied yet.
-        for (String ssid : ssids) {
-            final NetworkPolicy policy = newPolicy(ssid);
-            wifiPolicies.add(policy);
-        }
-        return wifiPolicies;
     }
 
-    private NetworkPolicy newPolicy(String ssid) {
-        final NetworkTemplate template = NetworkTemplate.buildTemplateWifi(ssid);
-        final NetworkPolicy policy = newWifiPolicy(template, false);
-        return policy;
-    }
-
-    private String getNetworkId(NetworkPolicy policy) {
-        return removeDoubleQuotes(policy.template.getNetworkId());
+    private static int stringToOverride(String override) {
+        switch (override) {
+            case "true": return WifiConfiguration.METERED_OVERRIDE_METERED;
+            case "false": return WifiConfiguration.METERED_OVERRIDE_NOT_METERED;
+            default: return WifiConfiguration.METERED_OVERRIDE_NONE;
+        }
     }
 
     private int getNextBooleanArg() {
diff --git a/services/core/java/com/android/server/net/NetworkStatsService.java b/services/core/java/com/android/server/net/NetworkStatsService.java
index ab685ca..8209ade 100644
--- a/services/core/java/com/android/server/net/NetworkStatsService.java
+++ b/services/core/java/com/android/server/net/NetworkStatsService.java
@@ -18,7 +18,6 @@
 
 import static android.Manifest.permission.ACCESS_NETWORK_STATE;
 import static android.Manifest.permission.CONNECTIVITY_INTERNAL;
-import static android.Manifest.permission.MODIFY_NETWORK_ACCOUNTING;
 import static android.Manifest.permission.READ_NETWORK_USAGE_HISTORY;
 import static android.content.Intent.ACTION_SHUTDOWN;
 import static android.content.Intent.ACTION_UID_REMOVED;
@@ -689,7 +688,8 @@
     @Override
     public void incrementOperationCount(int uid, int tag, int operationCount) {
         if (Binder.getCallingUid() != uid) {
-            mContext.enforceCallingOrSelfPermission(MODIFY_NETWORK_ACCOUNTING, TAG);
+            mContext.enforceCallingOrSelfPermission(
+                    android.Manifest.permission.UPDATE_DEVICE_STATS, TAG);
         }
 
         if (operationCount < 0) {
@@ -710,7 +710,7 @@
 
     @Override
     public void setUidForeground(int uid, boolean uidForeground) {
-        mContext.enforceCallingOrSelfPermission(MODIFY_NETWORK_ACCOUNTING, TAG);
+        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
 
         synchronized (mStatsLock) {
             final int set = uidForeground ? SET_FOREGROUND : SET_DEFAULT;
@@ -750,7 +750,7 @@
 
     @Override
     public void advisePersistThreshold(long thresholdBytes) {
-        mContext.enforceCallingOrSelfPermission(MODIFY_NETWORK_ACCOUNTING, TAG);
+        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
         assertBandwidthControlEnabled();
 
         // clamp threshold into safe range
diff --git a/services/core/java/com/android/server/notification/BadgeExtractor.java b/services/core/java/com/android/server/notification/BadgeExtractor.java
index 1bd2085..184f8b2 100644
--- a/services/core/java/com/android/server/notification/BadgeExtractor.java
+++ b/services/core/java/com/android/server/notification/BadgeExtractor.java
@@ -47,9 +47,11 @@
         if (!userWantsBadges || !appCanShowBadge) {
             record.setShowBadge(false);
         } else {
-            record.setShowBadge(mConfig.getNotificationChannel(record.sbn.getPackageName(),
-                    record.sbn.getUid(), record.getChannel().getId(), false).canShowBadge()
-                    && appCanShowBadge);
+            if (record.getChannel() != null) {
+                record.setShowBadge(record.getChannel().canShowBadge() && appCanShowBadge);
+            } else {
+                record.setShowBadge(appCanShowBadge);
+            }
         }
 
         return null;
diff --git a/services/core/java/com/android/server/notification/NotificationAdjustmentExtractor.java b/services/core/java/com/android/server/notification/NotificationAdjustmentExtractor.java
new file mode 100644
index 0000000..7c82845
--- /dev/null
+++ b/services/core/java/com/android/server/notification/NotificationAdjustmentExtractor.java
@@ -0,0 +1,48 @@
+/**
+* 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 android.content.Context;
+import android.util.Slog;
+
+/**
+ * Applies adjustments from the group helper and notification assistant
+ */
+public class NotificationAdjustmentExtractor implements NotificationSignalExtractor {
+    private static final String TAG = "BadgeExtractor";
+    private static final boolean DBG = false;
+
+
+    public void initialize(Context ctx, NotificationUsageStats usageStats) {
+        if (DBG) Slog.d(TAG, "Initializing  " + getClass().getSimpleName() + ".");
+    }
+
+    public RankingReconsideration process(NotificationRecord record) {
+        if (record == null || record.getNotification() == null) {
+            if (DBG) Slog.d(TAG, "skipping empty notification");
+            return null;
+        }
+
+        record.applyAdjustments();
+
+        return null;
+    }
+
+    @Override
+    public void setConfig(RankingConfig config) {
+        // config is not used
+    }
+}
diff --git a/services/core/java/com/android/server/notification/NotificationChannelExtractor.java b/services/core/java/com/android/server/notification/NotificationChannelExtractor.java
new file mode 100644
index 0000000..46ab556
--- /dev/null
+++ b/services/core/java/com/android/server/notification/NotificationChannelExtractor.java
@@ -0,0 +1,55 @@
+/**
+* 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 android.content.Context;
+import android.util.Slog;
+
+/**
+ * Stores the latest notification channel information for this notification
+ */
+public class NotificationChannelExtractor implements NotificationSignalExtractor {
+    private static final String TAG = "BadgeExtractor";
+    private static final boolean DBG = false;
+
+    private RankingConfig mConfig;
+
+    public void initialize(Context ctx, NotificationUsageStats usageStats) {
+        if (DBG) Slog.d(TAG, "Initializing  " + getClass().getSimpleName() + ".");
+    }
+
+    public RankingReconsideration process(NotificationRecord record) {
+        if (record == null || record.getNotification() == null) {
+            if (DBG) Slog.d(TAG, "skipping empty notification");
+            return null;
+        }
+
+        if (mConfig == null) {
+            if (DBG) Slog.d(TAG, "missing config");
+            return null;
+        }
+
+        record.updateNotificationChannel(mConfig.getNotificationChannel(record.sbn.getPackageName(),
+                record.sbn.getUid(), record.getChannel().getId(), false));
+
+        return null;
+    }
+
+    @Override
+    public void setConfig(RankingConfig config) {
+        mConfig = config;
+    }
+}
diff --git a/services/core/java/com/android/server/notification/NotificationIntrusivenessExtractor.java b/services/core/java/com/android/server/notification/NotificationIntrusivenessExtractor.java
index 4981d5c..12b29cf 100644
--- a/services/core/java/com/android/server/notification/NotificationIntrusivenessExtractor.java
+++ b/services/core/java/com/android/server/notification/NotificationIntrusivenessExtractor.java
@@ -58,6 +58,10 @@
             }
         }
 
+        if (!record.isRecentlyIntrusive()) {
+            return null;
+        }
+
         return new RankingReconsideration(record.getKey(), HANG_TIME_MS) {
             @Override
             public void work() {
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index c57b2fe..6a10113 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -278,7 +278,7 @@
     private ICompanionDeviceManager mCompanionManager;
 
     final IBinder mForegroundToken = new Binder();
-    private Handler mHandler;
+    private WorkerHandler mHandler;
     private final HandlerThread mRankingThread = new HandlerThread("ranker",
             Process.THREAD_PRIORITY_BACKGROUND);
 
@@ -295,8 +295,8 @@
     private String mVibrateNotificationKey;
 
     private final SparseArray<ArraySet<ManagedServiceInfo>> mListenersDisablingEffects =
-            new SparseArray<ArraySet<ManagedServiceInfo>>();
-    private List<ComponentName> mEffectsSuppressors = new ArrayList<ComponentName>();
+            new SparseArray<>();
+    private List<ComponentName> mEffectsSuppressors = new ArrayList<>();
     private int mListenerHints;  // right now, all hints are global
     private int mInterruptionFilter = NotificationListenerService.INTERRUPTION_FILTER_UNKNOWN;
 
@@ -312,16 +312,14 @@
     // used as a mutex for access to all active notifications & listeners
     final Object mNotificationLock = new Object();
     @GuardedBy("mNotificationLock")
-    final ArrayList<NotificationRecord> mNotificationList =
-            new ArrayList<NotificationRecord>();
+    final ArrayList<NotificationRecord> mNotificationList = new ArrayList<>();
     @GuardedBy("mNotificationLock")
-    final ArrayMap<String, NotificationRecord> mNotificationsByKey =
-            new ArrayMap<String, NotificationRecord>();
+    final ArrayMap<String, NotificationRecord> mNotificationsByKey = new ArrayMap<>();
     @GuardedBy("mNotificationLock")
     final ArrayList<NotificationRecord> mEnqueuedNotifications = new ArrayList<>();
     @GuardedBy("mNotificationLock")
     final ArrayMap<Integer, ArrayMap<String, String>> mAutobundledSummaries = new ArrayMap<>();
-    final ArrayList<ToastRecord> mToastQueue = new ArrayList<ToastRecord>();
+    final ArrayList<ToastRecord> mToastQueue = new ArrayList<>();
     final ArrayMap<String, NotificationRecord> mSummaryByGroupKey = new ArrayMap<>();
 
     // The last key in this list owns the hardware.
@@ -1083,6 +1081,32 @@
     }
 
     @VisibleForTesting
+    int getNotificationRecordCount() {
+        synchronized (mNotificationLock) {
+            int count = mNotificationList.size() + mNotificationsByKey.size()
+                    + mSummaryByGroupKey.size() + mEnqueuedNotifications.size();
+            // subtract duplicates
+            for (NotificationRecord posted : mNotificationList) {
+                if (mNotificationsByKey.containsKey(posted.getKey())) {
+                    count--;
+                }
+                if (posted.sbn.isGroup() && posted.getNotification().isGroupSummary()) {
+                    count--;
+                }
+            }
+
+            return count;
+        }
+    }
+
+    void clearNotifications() {
+        mEnqueuedNotifications.clear();
+        mNotificationList.clear();
+        mNotificationsByKey.clear();
+        mSummaryByGroupKey.clear();
+    }
+
+    @VisibleForTesting
     void addNotification(NotificationRecord r) {
         mNotificationList.add(r);
         mNotificationsByKey.put(r.sbn.getKey(), r);
@@ -1102,7 +1126,7 @@
     }
 
     @VisibleForTesting
-    void setHandler(Handler handler) {
+    void setHandler(WorkerHandler handler) {
         mHandler = handler;
     }
 
@@ -1122,6 +1146,11 @@
     }
 
     @VisibleForTesting
+    void setRankingHandler(RankingHandler rankingHandler) {
+        mRankingHandler = rankingHandler;
+    }
+
+    @VisibleForTesting
     void setIsTelevision(boolean isTelevision) {
         mIsTelevision = isTelevision;
     }
@@ -1131,9 +1160,10 @@
         mUsageStats = us;
     }
 
-    // TODO: Tests should call onStart instead once the methods above are removed.
+    // TODO: All tests should use this init instead of the one-off setters above.
     @VisibleForTesting
-    void init(Looper looper, IPackageManager packageManager, PackageManager packageManagerClient,
+    void init(Looper looper, IPackageManager packageManager,
+            PackageManager packageManagerClient,
             LightsManager lightsManager, NotificationListeners notificationListeners,
             NotificationAssistants notificationAssistants, ConditionProviders conditionProviders,
             ICompanionDeviceManager companionManager, SnoozeHelper snoozeHelper,
@@ -1235,6 +1265,46 @@
         mUserProfiles.updateCache(getContext());
         listenForCallState();
 
+        mSettingsObserver = new SettingsObserver(mHandler);
+
+        mArchive = new Archive(resources.getInteger(
+                R.integer.config_notificationServiceArchiveSize));
+
+        mIsTelevision = mPackageManagerClient.hasSystemFeature(FEATURE_LEANBACK)
+                || mPackageManagerClient.hasSystemFeature(FEATURE_TELEVISION);
+    }
+
+    @Override
+    public void onStart() {
+        SnoozeHelper snoozeHelper = new SnoozeHelper(getContext(), new SnoozeHelper.Callback() {
+            @Override
+            public void repost(int userId, NotificationRecord r) {
+                try {
+                    if (DBG) {
+                        Slog.d(TAG, "Reposting " + r.getKey());
+                    }
+                    enqueueNotificationInternal(r.sbn.getPackageName(), r.sbn.getOpPkg(),
+                            r.sbn.getUid(), r.sbn.getInitialPid(), r.sbn.getTag(), r.sbn.getId(),
+                            r.sbn.getNotification(), userId);
+                } catch (Exception e) {
+                    Slog.e(TAG, "Cannot un-snooze notification", e);
+                }
+            }
+        }, mUserProfiles);
+
+        final File systemDir = new File(Environment.getDataDirectory(), "system");
+
+        init(Looper.myLooper(),
+                AppGlobals.getPackageManager(), getContext().getPackageManager(),
+                getLocalService(LightsManager.class),
+                new NotificationListeners(AppGlobals.getPackageManager()),
+                new NotificationAssistants(AppGlobals.getPackageManager()),
+                new ConditionProviders(getContext(), mUserProfiles, AppGlobals.getPackageManager()),
+                null, snoozeHelper, new NotificationUsageStats(getContext()),
+                new AtomicFile(new File(systemDir, "notification_policy.xml")),
+                (ActivityManager) getContext().getSystemService(Context.ACTIVITY_SERVICE),
+                getGroupHelper());
+
         // register for various Intents
         IntentFilter filter = new IntentFilter();
         filter.addAction(Intent.ACTION_SCREEN_ON);
@@ -1275,44 +1345,6 @@
         IntentFilter settingsRestoredFilter = new IntentFilter(Intent.ACTION_SETTING_RESTORED);
         getContext().registerReceiver(mRestoreReceiver, settingsRestoredFilter);
 
-        mSettingsObserver = new SettingsObserver(mHandler);
-
-        mArchive = new Archive(resources.getInteger(
-                R.integer.config_notificationServiceArchiveSize));
-
-        mIsTelevision = mPackageManagerClient.hasSystemFeature(FEATURE_LEANBACK)
-                || mPackageManagerClient.hasSystemFeature(FEATURE_TELEVISION);
-    }
-
-    @Override
-    public void onStart() {
-        SnoozeHelper snoozeHelper = new SnoozeHelper(getContext(), new SnoozeHelper.Callback() {
-            @Override
-            public void repost(int userId, NotificationRecord r) {
-                try {
-                    if (DBG) {
-                        Slog.d(TAG, "Reposting " + r.getKey());
-                    }
-                    enqueueNotificationInternal(r.sbn.getPackageName(), r.sbn.getOpPkg(),
-                            r.sbn.getUid(), r.sbn.getInitialPid(), r.sbn.getTag(), r.sbn.getId(),
-                            r.sbn.getNotification(), userId);
-                } catch (Exception e) {
-                    Slog.e(TAG, "Cannot un-snooze notification", e);
-                }
-            }
-        }, mUserProfiles);
-
-        final File systemDir = new File(Environment.getDataDirectory(), "system");
-
-        init(Looper.myLooper(), AppGlobals.getPackageManager(), getContext().getPackageManager(),
-                getLocalService(LightsManager.class),
-                new NotificationListeners(AppGlobals.getPackageManager()),
-                new NotificationAssistants(AppGlobals.getPackageManager()),
-                new ConditionProviders(getContext(), mUserProfiles, AppGlobals.getPackageManager()),
-                null, snoozeHelper, new NotificationUsageStats(getContext()),
-                new AtomicFile(new File(systemDir, "notification_policy.xml")),
-                (ActivityManager) getContext().getSystemService(Context.ACTIVITY_SERVICE),
-                getGroupHelper());
         publishBinderService(Context.NOTIFICATION_SERVICE, mService);
         publishLocalService(NotificationManagerInternal.class, mInternalService);
     }
@@ -1324,7 +1356,6 @@
                 synchronized (mNotificationLock) {
                     addAutogroupKeyLocked(key);
                 }
-                mRankingHandler.requestSort(false);
             }
 
             @Override
@@ -1332,7 +1363,6 @@
                 synchronized (mNotificationLock) {
                     removeAutogroupKeyLocked(key);
                 }
-                mRankingHandler.requestSort(false);
             }
 
             @Override
@@ -1405,28 +1435,14 @@
         }
         mRankingHelper.updateNotificationChannel(pkg, uid, channel);
 
-        final NotificationChannel modifiedChannel =
-                mRankingHelper.getNotificationChannel(pkg, uid, channel.getId(), false);
-
         if (!fromListener) {
+            final NotificationChannel modifiedChannel =
+                    mRankingHelper.getNotificationChannel(pkg, uid, channel.getId(), false);
             mListeners.notifyNotificationChannelChanged(
                     pkg, UserHandle.getUserHandleForUid(uid),
                     modifiedChannel, NOTIFICATION_CHANNEL_OR_GROUP_UPDATED);
         }
 
-        synchronized (mNotificationLock) {
-            final int N = mNotificationList.size();
-            for (int i = N - 1; i >= 0; --i) {
-                NotificationRecord r = mNotificationList.get(i);
-                if (r.sbn.getPackageName().equals(pkg)
-                        && r.sbn.getUid() == uid
-                        && channel.getId() != null
-                        && channel.getId().equals(r.getChannel().getId())) {
-                    r.updateNotificationChannel(modifiedChannel);
-                }
-            }
-        }
-        mRankingHandler.requestSort(true);
         savePolicyFile();
     }
 
@@ -2864,7 +2880,7 @@
                     NotificationRecord n = mNotificationsByKey.get(adjustment.getKey());
                     applyAdjustment(n, adjustment);
                 }
-                mRankingHandler.requestSort(true);
+                mRankingHandler.requestSort();
             } finally {
                 Binder.restoreCallingIdentity(identity);
             }
@@ -2883,7 +2899,7 @@
                         applyAdjustment(n, adjustment);
                     }
                 }
-                mRankingHandler.requestSort(true);
+                mRankingHandler.requestSort();
             } finally {
                 Binder.restoreCallingIdentity(identity);
             }
@@ -2957,39 +2973,44 @@
         }
     };
 
-    private void applyAdjustment(NotificationRecord n, Adjustment adjustment) {
-        if (n == null) {
+    private void applyAdjustment(NotificationRecord r, Adjustment adjustment) {
+        if (r == null) {
             return;
         }
         if (adjustment.getSignals() != null) {
             Bundle.setDefusable(adjustment.getSignals(), true);
-            final ArrayList<String> people =
-                    adjustment.getSignals().getStringArrayList(Adjustment.KEY_PEOPLE);
-            final ArrayList<SnoozeCriterion> snoozeCriterionList =
-                    adjustment.getSignals().getParcelableArrayList(Adjustment.KEY_SNOOZE_CRITERIA);
-            n.setPeopleOverride(people);
-            n.setSnoozeCriteria(snoozeCriterionList);
+            r.addAdjustment(adjustment);
         }
     }
 
     @GuardedBy("mNotificationLock")
-    private void addAutogroupKeyLocked(String key) {
-        NotificationRecord n = mNotificationsByKey.get(key);
-        if (n == null) {
+    void addAutogroupKeyLocked(String key) {
+        NotificationRecord r = mNotificationsByKey.get(key);
+        if (r == null) {
             return;
         }
-        n.setOverrideGroupKey(GroupHelper.AUTOGROUP_KEY);
+        addAutoGroupAdjustment(r, GroupHelper.AUTOGROUP_KEY);
         EventLogTags.writeNotificationAutogrouped(key);
+        mRankingHandler.requestSort();
     }
 
     @GuardedBy("mNotificationLock")
-    private void removeAutogroupKeyLocked(String key) {
-        NotificationRecord n = mNotificationsByKey.get(key);
-        if (n == null) {
+    void removeAutogroupKeyLocked(String key) {
+        NotificationRecord r = mNotificationsByKey.get(key);
+        if (r == null) {
             return;
         }
-        n.setOverrideGroupKey(null);
+        addAutoGroupAdjustment(r, null);
         EventLogTags.writeNotificationUnautogrouped(key);
+        mRankingHandler.requestSort();
+    }
+
+    private void addAutoGroupAdjustment(NotificationRecord r, String overrideGroupKey) {
+        Bundle signals = new Bundle();
+        signals.putString(Adjustment.KEY_GROUP_KEY, overrideGroupKey);
+        Adjustment adjustment =
+                new Adjustment(r.sbn.getPackageName(), r.getKey(), signals, "", r.sbn.getUserId());
+        r.addAdjustment(adjustment);
     }
 
     // Clears the 'fake' auto-group summary.
@@ -4248,39 +4269,44 @@
             }
         }
         if (changed) {
-            scheduleSendRankingUpdate();
+            mHandler.scheduleSendRankingUpdate();
         }
     }
 
-    private void handleRankingSort(Message msg) {
-        if (!(msg.obj instanceof Boolean)) return;
+    void handleRankingSort() {
         if (mRankingHelper == null) return;
-        boolean forceUpdate = ((Boolean) msg.obj == null) ? false : (boolean) msg.obj;
         synchronized (mNotificationLock) {
             final int N = mNotificationList.size();
-            // Any field that can change via one of the extractors or by the assistant
-            // needs to be added here.
-            ArrayList<String> orderBefore = new ArrayList<String>(N);
-            ArrayList<String> groupOverrideBefore = new ArrayList<>(N);
+            // Any field that can change via one of the extractors needs to be added here.
+            ArrayList<String> orderBefore = new ArrayList<>(N);
             int[] visibilities = new int[N];
             boolean[] showBadges = new boolean[N];
+            ArrayList<NotificationChannel> channelBefore = new ArrayList<>(N);
+            ArrayList<String> groupKeyBefore = new ArrayList<>(N);
+            ArrayList<ArrayList<String>> overridePeopleBefore = new ArrayList<>(N);
+            ArrayList<ArrayList<SnoozeCriterion>> snoozeCriteriaBefore = new ArrayList<>(N);
             for (int i = 0; i < N; i++) {
                 final NotificationRecord r = mNotificationList.get(i);
                 orderBefore.add(r.getKey());
-                groupOverrideBefore.add(r.sbn.getGroupKey());
                 visibilities[i] = r.getPackageVisibilityOverride();
                 showBadges[i] = r.canShowBadge();
+                channelBefore.add(r.getChannel());
+                groupKeyBefore.add(r.getGroupKey());
+                overridePeopleBefore.add(r.getPeopleOverride());
+                snoozeCriteriaBefore.add(r.getSnoozeCriteria());
                 mRankingHelper.extractSignals(r);
             }
             mRankingHelper.sort(mNotificationList);
             for (int i = 0; i < N; i++) {
                 final NotificationRecord r = mNotificationList.get(i);
-                if (forceUpdate
-                        || !orderBefore.get(i).equals(r.getKey())
+                if (!orderBefore.get(i).equals(r.getKey())
                         || visibilities[i] != r.getPackageVisibilityOverride()
-                        || !groupOverrideBefore.get(i).equals(r.sbn.getGroupKey())
-                        || showBadges[i] != r.canShowBadge()) {
-                    scheduleSendRankingUpdate();
+                        || showBadges[i] != r.canShowBadge()
+                        || !Objects.equals(channelBefore.get(i), r.getChannel())
+                        || !Objects.equals(groupKeyBefore.get(i), r.getGroupKey())
+                        || !Objects.equals(overridePeopleBefore.get(i), r.getPeopleOverride())
+                        || !Objects.equals(snoozeCriteriaBefore.get(i), r.getSnoozeCriteria())) {
+                    mHandler.scheduleSendRankingUpdate();
                     return;
                 }
             }
@@ -4314,13 +4340,6 @@
         return mRankingHelper.indexOf(mNotificationList, target);
     }
 
-    private void scheduleSendRankingUpdate() {
-        if (!mHandler.hasMessages(MESSAGE_SEND_RANKING_UPDATE)) {
-            Message m = Message.obtain(mHandler, MESSAGE_SEND_RANKING_UPDATE);
-            mHandler.sendMessage(m);
-        }
-    }
-
     private void handleSendRankingUpdate() {
         synchronized (mNotificationLock) {
             mListeners.notifyRankingUpdateLocked();
@@ -4352,7 +4371,7 @@
         }
     }
 
-    private final class WorkerHandler extends Handler
+    protected class WorkerHandler extends Handler
     {
         public WorkerHandler(Looper looper) {
             super(looper);
@@ -4381,6 +4400,13 @@
             }
         }
 
+        protected void scheduleSendRankingUpdate() {
+            if (!hasMessages(MESSAGE_SEND_RANKING_UPDATE)) {
+                Message m = Message.obtain(this, MESSAGE_SEND_RANKING_UPDATE);
+                sendMessage(m);
+            }
+        }
+
     }
 
     private final class RankingHandlerWorker extends Handler implements RankingHandler
@@ -4396,16 +4422,15 @@
                     handleRankingReconsideration(msg);
                     break;
                 case MESSAGE_RANKING_SORT:
-                    handleRankingSort(msg);
+                    handleRankingSort();
                     break;
             }
         }
 
-        public void requestSort(boolean forceUpdate) {
+        public void requestSort() {
             removeMessages(MESSAGE_RANKING_SORT);
             Message msg = Message.obtain();
             msg.what = MESSAGE_RANKING_SORT;
-            msg.obj = forceUpdate;
             sendMessage(msg);
         }
 
@@ -4734,6 +4759,7 @@
                 canceledNotifications = new ArrayList<>();
             }
             notificationList.remove(i);
+            mNotificationsByKey.remove(r.getKey());
             canceledNotifications.add(r);
             cancelNotificationLocked(r, sendDelete, reason, wasPosted);
         }
@@ -4844,6 +4870,7 @@
                 EventLogTags.writeNotificationCancel(callingUid, callingPid, pkg, childSbn.getId(),
                         childSbn.getTag(), userId, 0, 0, reason, listenerName);
                 notificationList.remove(i);
+                mNotificationsByKey.remove(childR.getKey());
                 cancelNotificationLocked(childR, sendDelete, reason, wasPosted);
             }
         }
@@ -5720,11 +5747,16 @@
         public static final String USAGE = "help\n"
                 + "allow_listener COMPONENT\n"
                 + "disallow_listener COMPONENT\n"
+                + "set_assistant COMPONENT\n"
+                + "remove_assistant COMPONENT\n"
                 + "allow_dnd PACKAGE\n"
                 + "disallow_dnd PACKAGE";
 
         @Override
         public int onCommand(String cmd) {
+            if (cmd == null) {
+                return handleDefaultCommands(cmd);
+            }
             final PrintWriter pw = getOutPrintWriter();
             try {
                 switch (cmd) {
@@ -5757,6 +5789,24 @@
                         getBinderService().setNotificationListenerAccessGranted(cn, false);
                     }
                     break;
+                    case "allow_assistant": {
+                        ComponentName cn = ComponentName.unflattenFromString(getNextArgRequired());
+                        if (cn == null) {
+                            pw.println("Invalid assistant - must be a ComponentName");
+                            return -1;
+                        }
+                        getBinderService().setNotificationAssistantAccessGranted(cn, true);
+                    }
+                    break;
+                    case "disallow_assistant": {
+                        ComponentName cn = ComponentName.unflattenFromString(getNextArgRequired());
+                        if (cn == null) {
+                            pw.println("Invalid assistant - must be a ComponentName");
+                            return -1;
+                        }
+                        getBinderService().setNotificationAssistantAccessGranted(cn, false);
+                    }
+                    break;
 
                     default:
                         return handleDefaultCommands(cmd);
diff --git a/services/core/java/com/android/server/notification/NotificationRecord.java b/services/core/java/com/android/server/notification/NotificationRecord.java
index 1dee71c..77bf9e3 100644
--- a/services/core/java/com/android/server/notification/NotificationRecord.java
+++ b/services/core/java/com/android/server/notification/NotificationRecord.java
@@ -35,8 +35,10 @@
 import android.metrics.LogMaker;
 import android.net.Uri;
 import android.os.Build;
+import android.os.Bundle;
 import android.os.UserHandle;
 import android.provider.Settings;
+import android.service.notification.Adjustment;
 import android.service.notification.NotificationListenerService;
 import android.service.notification.NotificationRecordProto;
 import android.service.notification.SnoozeCriterion;
@@ -57,6 +59,7 @@
 import java.lang.reflect.Array;
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.List;
 import java.util.Objects;
 
 /**
@@ -132,6 +135,8 @@
     private String mGroupLogTag;
     private String mChannelIdLogTag;
 
+    private final List<Adjustment> mAdjustments;
+
     @VisibleForTesting
     public NotificationRecord(Context context, StatusBarNotification sbn,
             NotificationChannel channel)
@@ -150,6 +155,7 @@
         mAttributes = calculateAttributes();
         mImportance = calculateImportance();
         mLight = calculateLights();
+        mAdjustments = new ArrayList<>();
     }
 
     private boolean isPreChannelsNotification() {
@@ -504,6 +510,7 @@
         if (getSnoozeCriteria() != null) {
             pw.println(prefix + "snoozeCriteria=" + TextUtils.join(",", getSnoozeCriteria()));
         }
+        pw.println(prefix + "mAdjustments=" + mAdjustments);
     }
 
 
@@ -539,6 +546,36 @@
                 this.sbn.getNotification());
     }
 
+    public void addAdjustment(Adjustment adjustment) {
+        synchronized (mAdjustments) {
+            mAdjustments.add(adjustment);
+        }
+    }
+
+    public void applyAdjustments() {
+        synchronized (mAdjustments) {
+            for (Adjustment adjustment: mAdjustments) {
+                Bundle signals = adjustment.getSignals();
+                if (signals.containsKey(Adjustment.KEY_PEOPLE)) {
+                    final ArrayList<String> people =
+                            adjustment.getSignals().getStringArrayList(Adjustment.KEY_PEOPLE);
+                    setPeopleOverride(people);
+                }
+                if (signals.containsKey(Adjustment.KEY_SNOOZE_CRITERIA)) {
+                    final ArrayList<SnoozeCriterion> snoozeCriterionList =
+                            adjustment.getSignals().getParcelableArrayList(
+                                    Adjustment.KEY_SNOOZE_CRITERIA);
+                    setSnoozeCriteria(snoozeCriterionList);
+                }
+                if (signals.containsKey(Adjustment.KEY_GROUP_KEY)) {
+                    final String groupOverrideKey =
+                            adjustment.getSignals().getString(Adjustment.KEY_GROUP_KEY);
+                    setOverrideGroupKey(groupOverrideKey);
+                }
+            }
+        }
+    }
+
     public void setContactAffinity(float contactAffinity) {
         mContactAffinity = contactAffinity;
         if (mImportance < IMPORTANCE_DEFAULT &&
diff --git a/services/core/java/com/android/server/notification/RankingHandler.java b/services/core/java/com/android/server/notification/RankingHandler.java
index 656d727..96324d8 100644
--- a/services/core/java/com/android/server/notification/RankingHandler.java
+++ b/services/core/java/com/android/server/notification/RankingHandler.java
@@ -16,6 +16,6 @@
 package com.android.server.notification;
 
 public interface RankingHandler {
-    public void requestSort(boolean forceUpdate);
+    public void requestSort();
     public void requestReconsideration(RankingReconsideration recon);
-}
+}
\ No newline at end of file
diff --git a/services/core/java/com/android/server/notification/RankingHelper.java b/services/core/java/com/android/server/notification/RankingHelper.java
index 108a41d..5c2c2b3 100644
--- a/services/core/java/com/android/server/notification/RankingHelper.java
+++ b/services/core/java/com/android/server/notification/RankingHelper.java
@@ -399,7 +399,7 @@
         for (int i = 0; i < N; i++) {
             mSignalExtractors[i].setConfig(this);
         }
-        mRankingHandler.requestSort(false);
+        mRankingHandler.requestSort();
     }
 
     public void sort(ArrayList<NotificationRecord> notificationList) {
@@ -511,7 +511,6 @@
             MetricsLogger.action(getChannelGroupLog(group.getId(), pkg));
         }
         r.groups.put(group.getId(), group);
-        updateConfig();
     }
 
     @Override
@@ -569,7 +568,6 @@
         r.channels.put(channel.getId(), channel);
         MetricsLogger.action(getChannelLog(channel, pkg).setType(
                 MetricsProto.MetricsEvent.TYPE_OPEN));
-        updateConfig();
     }
 
     void clearLockedFields(NotificationChannel channel) {
@@ -641,7 +639,6 @@
             LogMaker lm = getChannelLog(channel, pkg);
             lm.setType(MetricsProto.MetricsEvent.TYPE_CLOSE);
             MetricsLogger.action(lm);
-            updateConfig();
         }
     }
 
@@ -655,7 +652,6 @@
             return;
         }
         r.channels.remove(channelId);
-        updateConfig();
     }
 
     @Override
@@ -672,7 +668,6 @@
                 r.channels.remove(key);
             }
         }
-        updateConfig();
     }
 
     public NotificationChannelGroup getNotificationChannelGroup(String groupId, String pkg,
@@ -735,7 +730,6 @@
                 deletedChannels.add(nc);
             }
         }
-        updateConfig();
         return deletedChannels;
     }
 
@@ -1150,7 +1144,7 @@
             changed |= oldValue != newValue;
         }
         if (changed) {
-            mRankingHandler.requestSort(false);
+            mRankingHandler.requestSort();
         }
     }
 
diff --git a/services/core/java/com/android/server/notification/ZenModeFiltering.java b/services/core/java/com/android/server/notification/ZenModeFiltering.java
index cbaad46..a7a2743 100644
--- a/services/core/java/com/android/server/notification/ZenModeFiltering.java
+++ b/services/core/java/com/android/server/notification/ZenModeFiltering.java
@@ -104,9 +104,6 @@
     }
 
     public boolean shouldIntercept(int zen, ZenModeConfig config, NotificationRecord record) {
-        if (isSystem(record)) {
-            return false;
-        }
         switch (zen) {
             case Global.ZEN_MODE_NO_INTERRUPTIONS:
                 // #notevenalarms
@@ -177,10 +174,6 @@
         return false;
     }
 
-    private static boolean isSystem(NotificationRecord record) {
-        return record.isCategory(Notification.CATEGORY_SYSTEM);
-    }
-
     private static boolean isAlarm(NotificationRecord record) {
         return record.isCategory(Notification.CATEGORY_ALARM)
                 || record.isAudioStream(AudioManager.STREAM_ALARM)
diff --git a/services/core/java/com/android/server/pm/BackgroundDexOptService.java b/services/core/java/com/android/server/pm/BackgroundDexOptService.java
index 6749afb..0f28647 100644
--- a/services/core/java/com/android/server/pm/BackgroundDexOptService.java
+++ b/services/core/java/com/android/server/pm/BackgroundDexOptService.java
@@ -18,7 +18,6 @@
 
 import static com.android.server.pm.PackageManagerService.DEBUG_DEXOPT;
 
-import android.app.AlarmManager;
 import android.app.job.JobInfo;
 import android.app.job.JobParameters;
 import android.app.job.JobScheduler;
@@ -40,6 +39,7 @@
 import com.android.server.PinnerService;
 
 import java.io.File;
+import java.util.Set;
 import java.util.concurrent.atomic.AtomicBoolean;
 import java.util.concurrent.TimeUnit;
 
@@ -73,6 +73,9 @@
     // Optimizations should be aborted. No space left on device.
     private static final int OPTIMIZE_ABORT_NO_SPACE_LEFT = 3;
 
+    // Used for calculating space threshold for downgrading unused apps.
+    private static final int LOW_THRESHOLD_MULTIPLIER_FOR_DOWNGRADE = 2;
+
     /**
      * Set of failed packages remembered across job runs.
      */
@@ -92,7 +95,14 @@
 
     private final File mDataDir = Environment.getDataDirectory();
 
+    private static final long mDowngradeUnusedAppsThresholdInMillis =
+            getDowngradeUnusedAppsThresholdInMillis();
+
     public static void schedule(Context context) {
+        if (isBackgroundDexoptDisabled()) {
+            return;
+        }
+
         JobScheduler js = (JobScheduler) context.getSystemService(Context.JOB_SCHEDULER_SERVICE);
 
         // Schedule a one-off job which scans installed packages and updates
@@ -215,7 +225,8 @@
                     /* checkProfiles */ false,
                     PackageManagerService.REASON_BOOT,
                     /* force */ false,
-                    /* bootComplete */ true);
+                    /* bootComplete */ true,
+                    /* downgrade */ false);
             if (result == PackageDexOptimizer.DEX_OPT_PERFORMED)  {
                 updatedPackages.add(pkg);
             }
@@ -243,7 +254,8 @@
     }
 
     // Optimize the given packages and return the optimization result (one of the OPTIMIZE_* codes).
-    private int idleOptimization(PackageManagerService pm, ArraySet<String> pkgs, Context context) {
+    private int idleOptimization(PackageManagerService pm, ArraySet<String> pkgs,
+            Context context) {
         Log.i(TAG, "Performing idle optimizations");
         // If post-boot update is still running, request that it exits early.
         mExitPostBootUpdate.set(true);
@@ -274,9 +286,16 @@
             long lowStorageThreshold, boolean is_for_primary_dex,
             ArraySet<String> failedPackageNames) {
         ArraySet<String> updatedPackages = new ArraySet<>();
+        Set<String> unusedPackages = pm.getUnusedPackages(mDowngradeUnusedAppsThresholdInMillis);
+        // Only downgrade apps when space is low on device.
+        // Threshold is selected above the lowStorageThreshold so that we can pro-actively clean
+        // up disk before user hits the actual lowStorageThreshold.
+        final long lowStorageThresholdForDowngrade = LOW_THRESHOLD_MULTIPLIER_FOR_DOWNGRADE *
+                lowStorageThreshold;
+        boolean shouldDowngrade = shouldDowngrade(lowStorageThresholdForDowngrade);
         for (String pkg : pkgs) {
             int abort_code = abortIdleOptimizations(lowStorageThreshold);
-            if (abort_code != OPTIMIZE_CONTINUE) {
+            if (abort_code == OPTIMIZE_ABORT_BY_JOB_SCHEDULER) {
                 return abort_code;
             }
 
@@ -284,30 +303,57 @@
                 if (failedPackageNames.contains(pkg)) {
                     // Skip previously failing package
                     continue;
-                } else {
-                    // Conservatively add package to the list of failing ones in case performDexOpt
-                    // never returns.
-                    failedPackageNames.add(pkg);
                 }
             }
 
+            int reason;
+            boolean downgrade;
+            // Downgrade unused packages.
+            if (unusedPackages.contains(pkg) && shouldDowngrade) {
+                // This applies for system apps or if packages location is not a directory, i.e.
+                // monolithic install.
+                if (is_for_primary_dex && !pm.canHaveOatDir(pkg)) {
+                    // For apps that don't have the oat directory, instead of downgrading,
+                    // remove their compiler artifacts from dalvik cache.
+                    pm.deleteOatArtifactsOfPackage(pkg);
+                    continue;
+                } else {
+                    reason = PackageManagerService.REASON_INACTIVE_PACKAGE_DOWNGRADE;
+                    downgrade = true;
+                }
+            } else if (abort_code != OPTIMIZE_ABORT_NO_SPACE_LEFT) {
+                reason = PackageManagerService.REASON_BACKGROUND_DEXOPT;
+                downgrade = false;
+            } else {
+                // can't dexopt because of low space.
+                continue;
+            }
+
+            synchronized (failedPackageNames) {
+                // Conservatively add package to the list of failing ones in case
+                // performDexOpt never returns.
+                failedPackageNames.add(pkg);
+            }
+
             // Optimize package if needed. Note that there can be no race between
             // concurrent jobs because PackageDexOptimizer.performDexOpt is synchronized.
             boolean success;
             if (is_for_primary_dex) {
                 int result = pm.performDexOptWithStatus(pkg,
                         /* checkProfiles */ true,
-                        PackageManagerService.REASON_BACKGROUND_DEXOPT,
-                        /* force */ false,
-                        /* bootComplete */ true);
+                        reason,
+                        false /* forceCompile*/,
+                        true /* bootComplete */,
+                        downgrade);
                 success = result != PackageDexOptimizer.DEX_OPT_FAILED;
                 if (result == PackageDexOptimizer.DEX_OPT_PERFORMED) {
                     updatedPackages.add(pkg);
                 }
             } else {
                 success = pm.performDexOptSecondary(pkg,
-                        PackageManagerService.REASON_BACKGROUND_DEXOPT,
-                        /* force */ false);
+                        reason,
+                        false /* force */,
+                        downgrade);
             }
             if (success) {
                 // Dexopt succeeded, remove package from the list of failing ones.
@@ -347,6 +393,16 @@
         return OPTIMIZE_CONTINUE;
     }
 
+    // Evaluate whether apps should be downgraded.
+    private boolean shouldDowngrade(long lowStorageThresholdForDowngrade) {
+        long usableSpace = mDataDir.getUsableSpace();
+        if (usableSpace < lowStorageThresholdForDowngrade) {
+            return true;
+        }
+
+        return false;
+    }
+
     /**
      * Execute the idle optimizations immediately.
      */
@@ -415,4 +471,19 @@
             pinnerService.update(updatedPackages);
         }
     }
+
+    private static long getDowngradeUnusedAppsThresholdInMillis() {
+        final String sysPropKey = "pm.dexopt.downgrade_after_inactive_days";
+        String sysPropValue = SystemProperties.get(sysPropKey);
+        if (sysPropValue == null || sysPropValue.isEmpty()) {
+            Log.w(TAG, "SysProp " + sysPropKey + " not set");
+            return Long.MAX_VALUE;
+        }
+        return TimeUnit.DAYS.toMillis(Long.parseLong(sysPropValue));
+    }
+
+    private static boolean isBackgroundDexoptDisabled() {
+        return SystemProperties.getBoolean("pm.dexopt.disable_bg_dexopt" /* key */,
+                false /* default */);
+    }
 }
diff --git a/services/core/java/com/android/server/pm/Installer.java b/services/core/java/com/android/server/pm/Installer.java
index bd765b4..371b3ef 100644
--- a/services/core/java/com/android/server/pm/Installer.java
+++ b/services/core/java/com/android/server/pm/Installer.java
@@ -279,13 +279,13 @@
     public void dexopt(String apkPath, int uid, @Nullable String pkgName, String instructionSet,
             int dexoptNeeded, @Nullable String outputPath, int dexFlags,
             String compilerFilter, @Nullable String volumeUuid, @Nullable String sharedLibraries,
-            @Nullable String seInfo)
+            @Nullable String seInfo, boolean downgrade)
             throws InstallerException {
         assertValidInstructionSet(instructionSet);
         if (!checkBeforeRemote()) return;
         try {
             mInstalld.dexopt(apkPath, uid, pkgName, instructionSet, dexoptNeeded, outputPath,
-                    dexFlags, compilerFilter, volumeUuid, sharedLibraries, seInfo);
+                    dexFlags, compilerFilter, volumeUuid, sharedLibraries, seInfo, downgrade);
         } catch (Exception e) {
             throw InstallerException.from(e);
         }
diff --git a/services/core/java/com/android/server/pm/OtaDexoptService.java b/services/core/java/com/android/server/pm/OtaDexoptService.java
index 2a45d15..4ff6cbf 100644
--- a/services/core/java/com/android/server/pm/OtaDexoptService.java
+++ b/services/core/java/com/android/server/pm/OtaDexoptService.java
@@ -30,7 +30,6 @@
 import android.os.ServiceManager;
 import android.os.ShellCallback;
 import android.os.storage.StorageManager;
-import android.text.TextUtils;
 import android.util.Log;
 import android.util.Slog;
 
@@ -40,7 +39,6 @@
 import java.io.File;
 import java.io.FileDescriptor;
 import java.util.ArrayList;
-import java.util.Arrays;
 import java.util.Collection;
 import java.util.List;
 import java.util.concurrent.TimeUnit;
@@ -152,7 +150,7 @@
             Log.i(TAG, "Low on space, deleting oat files in an attempt to free up space: "
                     + PackageManagerServiceUtils.packagesToString(others));
             for (PackageParser.Package pkg : others) {
-                deleteOatArtifactsOfPackage(pkg);
+                mPackageManagerService.deleteOatArtifactsOfPackage(pkg.packageName);
             }
         }
         long spaceAvailableNow = getAvailableSpace();
@@ -242,30 +240,6 @@
         return usableSpace - lowThreshold;
     }
 
-    private static String getOatDir(PackageParser.Package pkg) {
-        if (!pkg.canHaveOatDir()) {
-            return null;
-        }
-        File codePath = new File(pkg.codePath);
-        if (codePath.isDirectory()) {
-            return PackageDexOptimizer.getOatDir(codePath).getAbsolutePath();
-        }
-        return null;
-    }
-
-    private void deleteOatArtifactsOfPackage(PackageParser.Package pkg) {
-        String[] instructionSets = getAppDexInstructionSets(pkg.applicationInfo);
-        for (String codePath : pkg.getAllCodePaths()) {
-            for (String isa : instructionSets) {
-                try {
-                    mPackageManagerService.mInstaller.deleteOdex(codePath, isa, getOatDir(pkg));
-                } catch (InstallerException e) {
-                    Log.e(TAG, "Failed deleting oat files for " + codePath, e);
-                }
-            }
-        }
-    }
-
     /**
      * Generate all dexopt commands for the given package.
      */
@@ -285,11 +259,12 @@
             public void dexopt(String apkPath, int uid, @Nullable String pkgName,
                     String instructionSet, int dexoptNeeded, @Nullable String outputPath,
                     int dexFlags, String compilerFilter, @Nullable String volumeUuid,
-                    @Nullable String sharedLibraries, @Nullable String seInfo) throws InstallerException {
+                    @Nullable String sharedLibraries, @Nullable String seInfo, boolean downgrade)
+                    throws InstallerException {
                 final StringBuilder builder = new StringBuilder();
 
-                // The version. Right now it's 2.
-                builder.append("2 ");
+                // The version. Right now it's 3.
+                builder.append("3 ");
 
                 builder.append("dexopt");
 
@@ -304,6 +279,7 @@
                 encodeParameter(builder, volumeUuid);
                 encodeParameter(builder, sharedLibraries);
                 encodeParameter(builder, seInfo);
+                encodeParameter(builder, downgrade);
 
                 commands.add(builder.toString());
             }
@@ -343,12 +319,14 @@
                 getCompilerFilterForReason(compilationReason),
                 null /* CompilerStats.PackageStats */,
                 mPackageManagerService.getDexManager().isUsedByOtherApps(pkg.packageName),
-                true /* bootComplete */);
+                true /* bootComplete */,
+                false /* downgrade */);
 
         mPackageManagerService.getDexManager().dexoptSecondaryDex(pkg.packageName,
                 getCompilerFilterForReason(compilationReason),
                 false /* force */,
-                false /* compileOnlySharedDex */);
+                false /* compileOnlySharedDex */,
+                false /* downgrade */);
         return commands;
     }
 
diff --git a/services/core/java/com/android/server/pm/PackageDexOptimizer.java b/services/core/java/com/android/server/pm/PackageDexOptimizer.java
index 2b7bd34..3eaeb6d 100644
--- a/services/core/java/com/android/server/pm/PackageDexOptimizer.java
+++ b/services/core/java/com/android/server/pm/PackageDexOptimizer.java
@@ -114,7 +114,7 @@
     int performDexOpt(PackageParser.Package pkg, String[] sharedLibraries,
             String[] instructionSets, boolean checkProfiles, String targetCompilationFilter,
             CompilerStats.PackageStats packageStats, boolean isUsedByOtherApps,
-            boolean bootComplete) {
+            boolean bootComplete, boolean downgrade) {
         if (!canOptimizePackage(pkg)) {
             return DEX_OPT_SKIPPED;
         }
@@ -122,7 +122,8 @@
             final long acquireTime = acquireWakeLockLI(pkg.applicationInfo.uid);
             try {
                 return performDexOptLI(pkg, sharedLibraries, instructionSets, checkProfiles,
-                        targetCompilationFilter, packageStats, isUsedByOtherApps, bootComplete);
+                        targetCompilationFilter, packageStats, isUsedByOtherApps, bootComplete,
+                        downgrade);
             } finally {
                 releaseWakeLockLI(acquireTime);
             }
@@ -137,7 +138,7 @@
     private int performDexOptLI(PackageParser.Package pkg, String[] sharedLibraries,
             String[] targetInstructionSets, boolean checkForProfileUpdates,
             String targetCompilerFilter, CompilerStats.PackageStats packageStats,
-            boolean isUsedByOtherApps, boolean bootComplete) {
+            boolean isUsedByOtherApps, boolean bootComplete, boolean downgrade) {
         final String[] instructionSets = targetInstructionSets != null ?
                 targetInstructionSets : getAppDexInstructionSets(pkg.applicationInfo);
         final String[] dexCodeInstructionSets = getDexCodeInstructionSets(instructionSets);
@@ -174,7 +175,8 @@
             }
             for (String dexCodeIsa : dexCodeInstructionSets) {
                 int newResult = dexOptPath(pkg, path, dexCodeIsa, compilerFilter, profileUpdated,
-                        sharedLibrariesPathWithSplits, dexoptFlags, sharedGid, packageStats);
+                        sharedLibrariesPathWithSplits, dexoptFlags, sharedGid, packageStats,
+                        downgrade);
                 // The end result is:
                 //  - FAILED if any path failed,
                 //  - PERFORMED if at least one path needed compilation,
@@ -198,8 +200,8 @@
     @GuardedBy("mInstallLock")
     private int dexOptPath(PackageParser.Package pkg, String path, String isa,
             String compilerFilter, boolean profileUpdated, String sharedLibrariesPath,
-            int dexoptFlags, int uid, CompilerStats.PackageStats packageStats) {
-        int dexoptNeeded = getDexoptNeeded(path, isa, compilerFilter, profileUpdated);
+            int dexoptFlags, int uid, CompilerStats.PackageStats packageStats, boolean downgrade) {
+        int dexoptNeeded = getDexoptNeeded(path, isa, compilerFilter, profileUpdated, downgrade);
         if (Math.abs(dexoptNeeded) == DexFile.NO_DEXOPT_NEEDED) {
             return DEX_OPT_SKIPPED;
         }
@@ -218,8 +220,12 @@
         try {
             long startTime = System.currentTimeMillis();
 
+            // TODO: Consider adding 2 different APIs for primary and secondary dexopt.
+            // installd only uses downgrade flag for secondary dex files and ignores it for
+            // primary dex files.
             mInstaller.dexopt(path, uid, pkg.packageName, isa, dexoptNeeded, oatDir, dexoptFlags,
-                    compilerFilter, pkg.volumeUuid, sharedLibrariesPath, pkg.applicationInfo.seInfo);
+                    compilerFilter, pkg.volumeUuid, sharedLibrariesPath, pkg.applicationInfo.seInfo,
+                    false /* downgrade*/);
 
             if (packageStats != null) {
                 long endTime = System.currentTimeMillis();
@@ -247,12 +253,12 @@
      * that seems wasteful.
      */
     public int dexOptSecondaryDexPath(ApplicationInfo info, String path, Set<String> isas,
-            String compilerFilter, boolean isUsedByOtherApps) {
+            String compilerFilter, boolean isUsedByOtherApps, boolean downgrade) {
         synchronized (mInstallLock) {
             final long acquireTime = acquireWakeLockLI(info.uid);
             try {
                 return dexOptSecondaryDexPathLI(info, path, isas, compilerFilter,
-                        isUsedByOtherApps);
+                        isUsedByOtherApps, downgrade);
             } finally {
                 releaseWakeLockLI(acquireTime);
             }
@@ -294,7 +300,7 @@
 
     @GuardedBy("mInstallLock")
     private int dexOptSecondaryDexPathLI(ApplicationInfo info, String path, Set<String> isas,
-            String compilerFilter, boolean isUsedByOtherApps) {
+            String compilerFilter, boolean isUsedByOtherApps, boolean downgrade) {
         compilerFilter = getRealCompilerFilter(info, compilerFilter, isUsedByOtherApps);
         // Get the dexopt flags after getRealCompilerFilter to make sure we get the correct flags.
         // Secondary dex files are currently not compiled at boot.
@@ -324,7 +330,8 @@
                 // TODO(calin): maybe add a separate call.
                 mInstaller.dexopt(path, info.uid, info.packageName, isa, /*dexoptNeeded*/ 0,
                         /*oatDir*/ null, dexoptFlags,
-                        compilerFilter, info.volumeUuid, SKIP_SHARED_LIBRARY_CHECK, info.seInfoUser);
+                        compilerFilter, info.volumeUuid, SKIP_SHARED_LIBRARY_CHECK, info.seInfoUser,
+                        downgrade);
             }
 
             return DEX_OPT_PERFORMED;
@@ -425,10 +432,11 @@
      * configuration (isa, compiler filter, profile).
      */
     private int getDexoptNeeded(String path, String isa, String compilerFilter,
-            boolean newProfile) {
+            boolean newProfile, boolean downgrade) {
         int dexoptNeeded;
         try {
-            dexoptNeeded = DexFile.getDexOptNeeded(path, isa, compilerFilter, newProfile);
+            dexoptNeeded = DexFile.getDexOptNeeded(path, isa, compilerFilter, newProfile,
+                    downgrade);
         } catch (IOException ioe) {
             Slog.w(TAG, "IOException reading apk: " + path, ioe);
             return DEX_OPT_FAILED;
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 700d3c2..1810174 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -283,6 +283,7 @@
 import com.android.server.pm.Settings.DatabaseVersion;
 import com.android.server.pm.Settings.VersionInfo;
 import com.android.server.pm.dex.DexManager;
+import com.android.server.pm.dex.PackageDexUsage;
 import com.android.server.storage.DeviceStorageMonitorInternal;
 
 import dalvik.system.CloseGuard;
@@ -561,8 +562,9 @@
     public static final int REASON_INSTALL = 2;
     public static final int REASON_BACKGROUND_DEXOPT = 3;
     public static final int REASON_AB_OTA = 4;
+    public static final int REASON_INACTIVE_PACKAGE_DOWNGRADE = 5;
 
-    public static final int REASON_LAST = REASON_AB_OTA;
+    public static final int REASON_LAST = REASON_INACTIVE_PACKAGE_DOWNGRADE;
 
     /** All dangerous permission names in the same order as the events in MetricsEvent */
     private static final List<String> ALL_DANGEROUS_PERMISSIONS = Arrays.asList(
@@ -2699,7 +2701,16 @@
 
             // Remove any shared userIDs that have no associated packages
             mSettings.pruneSharedUsersLPw();
-
+            final long systemScanTime = SystemClock.uptimeMillis() - startTime;
+            final int systemPackagesCount = mPackages.size();
+            Slog.i(TAG, "Finished scanning system apps. Time: " + systemScanTime
+                    + " ms, packageCount: " + systemPackagesCount
+                    + " ms, timePerPackage: "
+                    + (systemPackagesCount == 0 ? 0 : systemScanTime / systemPackagesCount));
+            if (mIsUpgrade && systemPackagesCount > 0) {
+                MetricsLogger.histogram(null, "ota_package_manager_system_app_avg_scan_time",
+                        ((int) systemScanTime) / systemPackagesCount);
+            }
             if (!mOnlyCore) {
                 EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_DATA_SCAN_START,
                         SystemClock.uptimeMillis());
@@ -2780,6 +2791,16 @@
                         }
                     }
                 }
+                final long dataScanTime = SystemClock.uptimeMillis() - systemScanTime - startTime;
+                final int dataPackagesCount = mPackages.size() - systemPackagesCount;
+                Slog.i(TAG, "Finished scanning non-system apps. Time: " + dataScanTime
+                        + " ms, packageCount: " + dataPackagesCount
+                        + " ms, timePerPackage: "
+                        + (dataPackagesCount == 0 ? 0 : dataScanTime / dataPackagesCount));
+                if (mIsUpgrade && dataPackagesCount > 0) {
+                    MetricsLogger.histogram(null, "ota_package_manager_data_app_avg_scan_time",
+                            ((int) dataScanTime) / dataPackagesCount);
+                }
             }
             mExpectingBetter.clear();
 
@@ -3009,6 +3030,10 @@
                 userPackages.put(userId, getInstalledPackages(/*flags*/ 0, userId).getList());
             }
             mDexManager.load(userPackages);
+            if (mIsUpgrade) {
+                MetricsLogger.histogram(null, "ota_package_manager_init_time",
+                        (int) (SystemClock.uptimeMillis() - startTime));
+            }
         } // synchronized (mPackages)
         } // synchronized (mInstallLock)
 
@@ -9344,7 +9369,8 @@
                     false /* checkProfiles */,
                     compilerFilter,
                     false /* force */,
-                    bootComplete);
+                    bootComplete,
+                    false /* downgrade */);
 
             if (pkg.isSystemApp()) {
                 // Only dexopt shared secondary dex files belonging to system apps to not slow down
@@ -9352,7 +9378,8 @@
                 mDexManager.dexoptSecondaryDex(pkg.packageName,
                         compilerFilter,
                         false /* force */,
-                        true /* compileOnlySharedDex */);
+                        true /* compileOnlySharedDex */,
+                        false /* downgrade */);
             }
 
             // TODO(shubhamajmera): Record secondary dexopt stats.
@@ -9437,14 +9464,16 @@
 
     @Override
     public boolean performDexOpt(String packageName,
-            boolean checkProfiles, int compileReason, boolean force, boolean bootComplete) {
+            boolean checkProfiles, int compileReason, boolean force, boolean bootComplete,
+            boolean downgrade) {
         if (getInstantAppPackageName(Binder.getCallingUid()) != null) {
             return false;
         } else if (isInstantApp(packageName, UserHandle.getCallingUserId())) {
             return false;
         }
         int dexoptStatus = performDexOptWithStatus(
-              packageName, checkProfiles, compileReason, force, bootComplete);
+              packageName, checkProfiles, compileReason, force, bootComplete,
+              downgrade);
         return dexoptStatus != PackageDexOptimizer.DEX_OPT_FAILED;
     }
 
@@ -9455,9 +9484,10 @@
      *  {@link PackageDexOptimizer#DEX_OPT_FAILED}
      */
     /* package */ int performDexOptWithStatus(String packageName,
-            boolean checkProfiles, int compileReason, boolean force, boolean bootComplete) {
+            boolean checkProfiles, int compileReason, boolean force, boolean bootComplete,
+            boolean downgrade) {
         return performDexOptTraced(packageName, checkProfiles,
-                getCompilerFilterForReason(compileReason), force, bootComplete);
+                getCompilerFilterForReason(compileReason), force, bootComplete, downgrade);
     }
 
     @Override
@@ -9470,17 +9500,17 @@
             return false;
         }
         int dexOptStatus = performDexOptTraced(packageName, checkProfiles,
-                targetCompilerFilter, force, bootComplete);
+                targetCompilerFilter, force, bootComplete, false /* downgrade */);
         return dexOptStatus != PackageDexOptimizer.DEX_OPT_FAILED;
     }
 
     private int performDexOptTraced(String packageName,
                 boolean checkProfiles, String targetCompilerFilter, boolean force,
-                boolean bootComplete) {
+                boolean bootComplete, boolean downgrade) {
         Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "dexopt");
         try {
             return performDexOptInternal(packageName, checkProfiles,
-                    targetCompilerFilter, force, bootComplete);
+                    targetCompilerFilter, force, bootComplete, downgrade);
         } finally {
             Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
         }
@@ -9490,7 +9520,7 @@
     // if the package can now be considered up to date for the given filter.
     private int performDexOptInternal(String packageName,
                 boolean checkProfiles, String targetCompilerFilter, boolean force,
-                boolean bootComplete) {
+                boolean bootComplete, boolean downgrade) {
         PackageParser.Package p;
         synchronized (mPackages) {
             p = mPackages.get(packageName);
@@ -9505,7 +9535,7 @@
         try {
             synchronized (mInstallLock) {
                 return performDexOptInternalWithDependenciesLI(p, checkProfiles,
-                        targetCompilerFilter, force, bootComplete);
+                        targetCompilerFilter, force, bootComplete, downgrade);
             }
         } finally {
             Binder.restoreCallingIdentity(callingId);
@@ -9526,7 +9556,7 @@
 
     private int performDexOptInternalWithDependenciesLI(PackageParser.Package p,
             boolean checkProfiles, String targetCompilerFilter,
-            boolean force, boolean bootComplete) {
+            boolean force, boolean bootComplete, boolean downgrade) {
         // Select the dex optimizer based on the force parameter.
         // Note: The force option is rarely used (cmdline input for testing, mostly), so it's OK to
         //       allocate an object here.
@@ -9551,12 +9581,13 @@
                         targetCompilerFilter,
                         getOrCreateCompilerPackageStats(depPackage),
                         true /* isUsedByOtherApps */,
-                        bootComplete);
+                        bootComplete,
+                        downgrade);
             }
         }
         return pdo.performDexOpt(p, p.usesLibraryFiles, instructionSets, checkProfiles,
                 targetCompilerFilter, getOrCreateCompilerPackageStats(p),
-                mDexManager.isUsedByOtherApps(p.packageName), bootComplete);
+                mDexManager.isUsedByOtherApps(p.packageName), bootComplete, downgrade);
     }
 
     // Performs dexopt on the used secondary dex files belonging to the given package.
@@ -9571,12 +9602,12 @@
             return false;
         }
         return mDexManager.dexoptSecondaryDex(packageName, compilerFilter, force,
-                /* compileOnlySharedDex*/ false);
+                false /* compileOnlySharedDex */, false /* downgrade */);
     }
 
     public boolean performDexOptSecondary(String packageName, int compileReason,
-            boolean force) {
-        return mDexManager.dexoptSecondaryDex(packageName, compileReason, force);
+            boolean force, boolean downgrade) {
+        return mDexManager.dexoptSecondaryDex(packageName, compileReason, force, downgrade);
     }
 
     /**
@@ -9755,7 +9786,8 @@
             final int res = performDexOptInternalWithDependenciesLI(pkg,
                     false /* checkProfiles */, getDefaultCompilerFilter(),
                     true /* force */,
-                    true /* bootComplete */);
+                    true /* bootComplete */,
+                    false /* downgrade */);
 
             Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
             if (res != PackageDexOptimizer.DEX_OPT_PERFORMED) {
@@ -16176,7 +16208,7 @@
         }
     }
 
-    private void removeDexFiles(List<String> allCodePaths, String[] instructionSets) {
+    void removeDexFiles(List<String> allCodePaths, String[] instructionSets) {
         if (!allCodePaths.isEmpty()) {
             if (instructionSets == null) {
                 throw new IllegalStateException("instructionSet == null");
@@ -18222,7 +18254,8 @@
                         getCompilerFilterForReason(REASON_INSTALL),
                         getOrCreateCompilerPackageStats(pkg),
                         mDexManager.isUsedByOtherApps(pkg.packageName),
-                        true /* bootComplete */);
+                        true /* bootComplete */,
+                        false /* downgrade */);
                 Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
             }
 
@@ -24984,6 +25017,73 @@
             return mInstantAppRegistry.getInstantAppAndroidIdLPw(packageName, userId);
         }
     }
+
+    boolean canHaveOatDir(String packageName) {
+        synchronized (mPackages) {
+            PackageParser.Package p = mPackages.get(packageName);
+            if (p == null) {
+                return false;
+            }
+            return p.canHaveOatDir();
+        }
+    }
+
+    private String getOatDir(PackageParser.Package pkg) {
+        if (!pkg.canHaveOatDir()) {
+            return null;
+        }
+        File codePath = new File(pkg.codePath);
+        if (codePath.isDirectory()) {
+            return PackageDexOptimizer.getOatDir(codePath).getAbsolutePath();
+        }
+        return null;
+    }
+
+    void deleteOatArtifactsOfPackage(String packageName) {
+        final String[] instructionSets;
+        final List<String> codePaths;
+        final String oatDir;
+        final PackageParser.Package pkg;
+        synchronized (mPackages) {
+            pkg = mPackages.get(packageName);
+        }
+        instructionSets = getAppDexInstructionSets(pkg.applicationInfo);
+        codePaths = pkg.getAllCodePaths();
+        oatDir = getOatDir(pkg);
+
+        for (String codePath : codePaths) {
+            for (String isa : instructionSets) {
+                try {
+                    mInstaller.deleteOdex(codePath, isa, oatDir);
+                } catch (InstallerException e) {
+                    Log.e(TAG, "Failed deleting oat files for " + codePath, e);
+                }
+            }
+        }
+    }
+
+    Set<String> getUnusedPackages(long downgradeTimeThresholdMillis) {
+        Set<String> unusedPackages = new HashSet<>();
+        long currentTimeInMillis = System.currentTimeMillis();
+        synchronized (mPackages) {
+            for (PackageParser.Package pkg : mPackages.values()) {
+                PackageSetting ps =  mSettings.mPackages.get(pkg.packageName);
+                if (ps == null) {
+                    continue;
+                }
+                PackageDexUsage.PackageUseInfo packageUseInfo = getDexManager().getPackageUseInfo(
+                        pkg.packageName);
+                if (PackageManagerServiceUtils
+                        .isUnusedSinceTimeInMillis(ps.firstInstallTime, currentTimeInMillis,
+                                downgradeTimeThresholdMillis, packageUseInfo,
+                                pkg.getLatestPackageUseTimeInMills(),
+                                pkg.getLatestForegroundPackageUseTimeInMills())) {
+                    unusedPackages.add(pkg.packageName);
+                }
+            }
+        }
+        return unusedPackages;
+    }
 }
 
 interface PackageSender {
diff --git a/services/core/java/com/android/server/pm/PackageManagerServiceCompilerMapping.java b/services/core/java/com/android/server/pm/PackageManagerServiceCompilerMapping.java
index ec248f5..1a97a72 100644
--- a/services/core/java/com/android/server/pm/PackageManagerServiceCompilerMapping.java
+++ b/services/core/java/com/android/server/pm/PackageManagerServiceCompilerMapping.java
@@ -26,7 +26,7 @@
 public class PackageManagerServiceCompilerMapping {
     // Names for compilation reasons.
     static final String REASON_STRINGS[] = {
-            "first-boot", "boot", "install", "bg-dexopt", "ab-ota"
+            "first-boot", "boot", "install", "bg-dexopt", "ab-ota", "inactive"
     };
 
     // Static block to ensure the strings array is of the right length.
diff --git a/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java b/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
index 9feee8c..a7031c9 100644
--- a/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
+++ b/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
@@ -16,12 +16,16 @@
 
 package com.android.server.pm;
 
+import com.android.server.pm.dex.DexManager;
+import com.android.server.pm.dex.PackageDexUsage;
+
 import static com.android.server.pm.PackageManagerService.DEBUG_DEXOPT;
 import static com.android.server.pm.PackageManagerService.TAG;
 
 import android.annotation.NonNull;
 import android.app.AppGlobals;
 import android.content.Intent;
+import android.content.pm.PackageInfo;
 import android.content.pm.PackageParser;
 import android.content.pm.ResolveInfo;
 import android.os.Build;
@@ -179,6 +183,43 @@
     }
 
     /**
+     * Checks if the package was inactive during since <code>thresholdTimeinMillis</code>.
+     * Package is considered active, if:
+     * 1) It was active in foreground.
+     * 2) It was active in background and also used by other apps.
+     *
+     * If it doesn't have sufficient information about the package, it return <code>false</code>.
+     */
+    static boolean isUnusedSinceTimeInMillis(long firstInstallTime, long currentTimeInMillis,
+            long thresholdTimeinMillis, PackageDexUsage.PackageUseInfo packageUseInfo,
+            long latestPackageUseTimeInMillis, long latestForegroundPackageUseTimeInMillis) {
+
+        if (currentTimeInMillis - firstInstallTime < thresholdTimeinMillis) {
+            return false;
+        }
+
+        // If the app was active in foreground during the threshold period.
+        boolean isActiveInForeground = (currentTimeInMillis
+                - latestForegroundPackageUseTimeInMillis)
+                < thresholdTimeinMillis;
+
+        if (isActiveInForeground) {
+            return false;
+        }
+
+        // If the app was active in background during the threshold period and was used
+        // by other packages.
+        // If packageUseInfo is null, it can be said that the package was not used by other
+        // packages.
+        boolean isActiveInBackgroundAndUsedByOtherPackages = ((currentTimeInMillis
+                - latestPackageUseTimeInMillis)
+                < thresholdTimeinMillis)
+                && (packageUseInfo != null && packageUseInfo.isUsedByOtherApps());
+
+        return !isActiveInBackgroundAndUsedByOtherPackages;
+    }
+
+    /**
      * Returns the canonicalized path of {@code path} as per {@code realpath(3)}
      * semantics.
      */
diff --git a/services/core/java/com/android/server/pm/PermissionsState.java b/services/core/java/com/android/server/pm/PermissionsState.java
index 8a427cd..f4d2ad2 100644
--- a/services/core/java/com/android/server/pm/PermissionsState.java
+++ b/services/core/java/com/android/server/pm/PermissionsState.java
@@ -427,7 +427,7 @@
                 mPermissionReviewRequired.put(userId, true);
             } else if ((oldFlags & PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED) != 0
                     && (newFlags & PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED) == 0) {
-                if (mPermissionReviewRequired != null) {
+                if (mPermissionReviewRequired != null && !hasPermissionRequiringReview(userId)) {
                     mPermissionReviewRequired.delete(userId);
                     if (mPermissionReviewRequired.size() <= 0) {
                         mPermissionReviewRequired = null;
@@ -438,6 +438,18 @@
         return updated;
     }
 
+    private boolean hasPermissionRequiringReview(int userId) {
+        final int permissionCount = mPermissions.size();
+        for (int i = 0; i < permissionCount; i++) {
+            final PermissionData permission = mPermissions.valueAt(i);
+            if ((permission.getFlags(userId)
+                    & PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED) != 0) {
+                return true;
+            }
+        }
+        return false;
+    }
+
     public boolean updatePermissionFlagsForAllPermissions(
             int userId, int flagMask, int flagValues) {
         enforceValidUserId(userId);
diff --git a/services/core/java/com/android/server/pm/dex/DexManager.java b/services/core/java/com/android/server/pm/dex/DexManager.java
index db2d30f..22de010 100644
--- a/services/core/java/com/android/server/pm/dex/DexManager.java
+++ b/services/core/java/com/android/server/pm/dex/DexManager.java
@@ -148,7 +148,8 @@
                 // or UsedBytOtherApps), record will return true and we trigger an async write
                 // to disk to make sure we don't loose the data in case of a reboot.
                 if (mPackageDexUsage.record(searchResult.mOwningPackageName,
-                        dexPath, loaderUserId, loaderIsa, isUsedByOtherApps, primaryOrSplit)) {
+                        dexPath, loaderUserId, loaderIsa, isUsedByOtherApps, primaryOrSplit,
+                        loadingAppInfo.packageName)) {
                     mPackageDexUsage.maybeWriteAsync();
                 }
             } else {
@@ -304,10 +305,11 @@
      * @return true if all secondary dex files were processed successfully (compiled or skipped
      *         because they don't need to be compiled)..
      */
-    public boolean dexoptSecondaryDex(String packageName, int compilerReason, boolean force) {
+    public boolean dexoptSecondaryDex(String packageName, int compilerReason, boolean force,
+            boolean downgrade) {
         return dexoptSecondaryDex(packageName,
                 PackageManagerServiceCompilerMapping.getCompilerFilterForReason(compilerReason),
-                force, /* compileOnlySharedDex */ false);
+                force, /* compileOnlySharedDex */ false, downgrade);
     }
 
     /**
@@ -316,7 +318,7 @@
      *         because they don't need to be compiled)..
      */
     public boolean dexoptSecondaryDex(String packageName, String compilerFilter, boolean force,
-            boolean compileOnlySharedDex) {
+            boolean compileOnlySharedDex, boolean downgrade) {
         // Select the dex optimizer based on the force parameter.
         // Forced compilation is done through ForcedUpdatePackageDexOptimizer which will adjust
         // the necessary dexopt flags to make sure that compilation is not skipped. This avoid
@@ -360,7 +362,8 @@
             }
 
             int result = pdo.dexOptSecondaryDexPath(pkg.applicationInfo, dexPath,
-                    dexUseInfo.getLoaderIsas(), compilerFilter, dexUseInfo.isUsedByOtherApps());
+                    dexUseInfo.getLoaderIsas(), compilerFilter, dexUseInfo.isUsedByOtherApps(),
+                    downgrade);
             success = success && (result != PackageDexOptimizer.DEX_OPT_FAILED);
         }
         return success;
@@ -465,7 +468,8 @@
         for (String isa : getAppDexInstructionSets(info)) {
             isas.add(isa);
             boolean newUpdate = mPackageDexUsage.record(searchResult.mOwningPackageName,
-                dexPath, userId, isa, isUsedByOtherApps, /*primaryOrSplit*/ false);
+                    dexPath, userId, isa, isUsedByOtherApps, /*primaryOrSplit*/ false,
+                    searchResult.mOwningPackageName);
             update |= newUpdate;
         }
         if (update) {
@@ -476,7 +480,7 @@
         String compilerFilter = PackageManagerServiceCompilerMapping.getCompilerFilterForReason(
                 PackageManagerService.REASON_INSTALL);
         int result = mPackageDexOptimizer.dexOptSecondaryDexPath(info, dexPath, isas,
-                compilerFilter, isUsedByOtherApps);
+                compilerFilter, isUsedByOtherApps, /* downgrade */ false);
 
         // If we fail to optimize the package log an error but don't propagate the error
         // back to the app. The app cannot do much about it and the background job
diff --git a/services/core/java/com/android/server/pm/dex/PackageDexUsage.java b/services/core/java/com/android/server/pm/dex/PackageDexUsage.java
index 8a66f12..f7dd174 100644
--- a/services/core/java/com/android/server/pm/dex/PackageDexUsage.java
+++ b/services/core/java/com/android/server/pm/dex/PackageDexUsage.java
@@ -35,14 +35,19 @@
 import java.io.Reader;
 import java.io.StringWriter;
 import java.io.Writer;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
 import java.util.Iterator;
 import java.util.HashMap;
 import java.util.HashSet;
+import java.util.List;
 import java.util.Map;
 import java.util.Set;
 
 import dalvik.system.VMRuntime;
 import libcore.io.IoUtils;
+import libcore.util.Objects;
 
 /**
  * Stat file which store usage information about dex files.
@@ -50,19 +55,23 @@
 public class PackageDexUsage extends AbstractStatsBase<Void> {
     private final static String TAG = "PackageDexUsage";
 
-    private final static int PACKAGE_DEX_USAGE_VERSION = 1;
+    // The last version update: add the list of packages that load the dex files.
+    private final static int PACKAGE_DEX_USAGE_VERSION = 2;
+    // We support VERSION 1 to ensure that the usage list remains valid cross OTAs.
+    private final static int PACKAGE_DEX_USAGE_SUPPORTED_VERSION_1 = 1;
+
     private final static String PACKAGE_DEX_USAGE_VERSION_HEADER =
             "PACKAGE_MANAGER__PACKAGE_DEX_USAGE__";
 
     private final static String SPLIT_CHAR = ",";
     private final static String DEX_LINE_CHAR = "#";
-
+    private final static String LOADING_PACKAGE_CHAR = "@";
     // Map which structures the information we have on a package.
     // Maps package name to package data (which stores info about UsedByOtherApps and
     // secondary dex files.).
     // Access to this map needs synchronized.
     @GuardedBy("mPackageUseInfoMap")
-    private Map<String, PackageUseInfo> mPackageUseInfoMap;
+    private final Map<String, PackageUseInfo> mPackageUseInfoMap;
 
     public PackageDexUsage() {
         super("package-dex-usage.list", "PackageDexUsage_DiskWriter", /*lock*/ false);
@@ -75,18 +84,21 @@
      * Note this is called when apps load dex files and as such it should return
      * as fast as possible.
      *
-     * @param loadingPackage the package performing the load
+     * @param owningPackageName the package owning the dex path
      * @param dexPath the path of the dex files being loaded
      * @param ownerUserId the user id which runs the code loading the dex files
      * @param loaderIsa the ISA of the app loading the dex files
      * @param isUsedByOtherApps whether or not this dex file was not loaded by its owning package
      * @param primaryOrSplit whether or not the dex file is a primary/split dex. True indicates
      *        the file is either primary or a split. False indicates the file is secondary dex.
+     * @param loadingPackageName the package performing the load. Recorded only if it is different
+     *        than {@param owningPackageName}.
      * @return true if the dex load constitutes new information, or false if this information
      *         has been seen before.
      */
     public boolean record(String owningPackageName, String dexPath, int ownerUserId,
-            String loaderIsa, boolean isUsedByOtherApps, boolean primaryOrSplit) {
+            String loaderIsa, boolean isUsedByOtherApps, boolean primaryOrSplit,
+            String loadingPackageName) {
         if (!PackageManagerServiceUtils.checkISA(loaderIsa)) {
             throw new IllegalArgumentException("loaderIsa " + loaderIsa + " is unsupported");
         }
@@ -100,11 +112,15 @@
                     // We do not need to record the loaderIsa or the owner because we compile
                     // primaries for all users and all ISAs.
                     packageUseInfo.mIsUsedByOtherApps = isUsedByOtherApps;
+                    maybeAddLoadingPackage(owningPackageName, loadingPackageName,
+                            packageUseInfo.mLoadingPackages);
                 } else {
                     // For secondary dex files record the loaderISA and the owner. We'll need
                     // to know under which user to compile and for what ISA.
-                    packageUseInfo.mDexUseInfoMap.put(
-                            dexPath, new DexUseInfo(isUsedByOtherApps, ownerUserId, loaderIsa));
+                    DexUseInfo newData = new DexUseInfo(isUsedByOtherApps, ownerUserId, loaderIsa);
+                    packageUseInfo.mDexUseInfoMap.put(dexPath, newData);
+                    maybeAddLoadingPackage(owningPackageName, loadingPackageName,
+                            newData.mLoadingPackages);
                 }
                 mPackageUseInfoMap.put(owningPackageName, packageUseInfo);
                 return true;
@@ -113,10 +129,15 @@
                 if (primaryOrSplit) {
                     // We have a possible update on the primary apk usage. Merge
                     // isUsedByOtherApps information and return if there was an update.
-                    return packageUseInfo.merge(isUsedByOtherApps);
+                    boolean updateLoadingPackages = maybeAddLoadingPackage(owningPackageName,
+                            loadingPackageName, packageUseInfo.mLoadingPackages);
+                    return packageUseInfo.merge(isUsedByOtherApps) || updateLoadingPackages;
                 } else {
                     DexUseInfo newData = new DexUseInfo(
                             isUsedByOtherApps, ownerUserId, loaderIsa);
+                    boolean updateLoadingPackages = maybeAddLoadingPackage(owningPackageName,
+                            loadingPackageName, newData.mLoadingPackages);
+
                     DexUseInfo existingData = packageUseInfo.mDexUseInfoMap.get(dexPath);
                     if (existingData == null) {
                         // It's the first time we see this dex file.
@@ -138,7 +159,7 @@
                         }
                         // Merge the information into the existing data.
                         // Returns true if there was an update.
-                        return existingData.merge(newData);
+                        return existingData.merge(newData) || updateLoadingPackages;
                     }
                 }
             }
@@ -185,15 +206,21 @@
      *
      * file_magic_version
      * package_name_1
+     * @ loading_package_1_1, loading_package_1_2...
      * #dex_file_path_1_1
+     * @ loading_package_1_1_1, loading_package_1_1_2...
      * user_1_1, used_by_other_app_1_1, user_isa_1_1_1, user_isa_1_1_2
      * #dex_file_path_1_2
+     * @ loading_package_1_2_1, loading_package_1_2_2...
      * user_1_2, used_by_other_app_1_2, user_isa_1_2_1, user_isa_1_2_2
      * ...
      * package_name_2
+     * @ loading_package_2_1, loading_package_2_1_2...
      * #dex_file_path_2_1
+     * @ loading_package_2_1_1, loading_package_2_1_2...
      * user_2_1, used_by_other_app_2_1, user_isa_2_1_1, user_isa_2_1_2
      * #dex_file_path_2_2,
+     * @ loading_package_2_2_1, loading_package_2_2_2...
      * user_2_2, used_by_other_app_2_2, user_isa_2_2_1, user_isa_2_2_2
      * ...
     */
@@ -214,12 +241,17 @@
 
             fpw.println(String.join(SPLIT_CHAR, packageName,
                     writeBoolean(packageUseInfo.mIsUsedByOtherApps)));
+            fpw.println(LOADING_PACKAGE_CHAR +
+                    String.join(SPLIT_CHAR, packageUseInfo.mLoadingPackages));
 
             // Write dex file lines.
             for (Map.Entry<String, DexUseInfo> dEntry : packageUseInfo.mDexUseInfoMap.entrySet()) {
                 String dexPath = dEntry.getKey();
                 DexUseInfo dexUseInfo = dEntry.getValue();
                 fpw.println(DEX_LINE_CHAR + dexPath);
+                    fpw.println(LOADING_PACKAGE_CHAR +
+                            String.join(SPLIT_CHAR, dexUseInfo.mLoadingPackages));
+
                 fpw.print(String.join(SPLIT_CHAR, Integer.toString(dexUseInfo.mOwnerUserId),
                         writeBoolean(dexUseInfo.mIsUsedByOtherApps)));
                 for (String isa : dexUseInfo.mLoaderIsas) {
@@ -252,6 +284,7 @@
         BufferedReader in = new BufferedReader(reader);
         // Read header, do version check.
         String versionLine = in.readLine();
+        int version;
         if (versionLine == null) {
             throw new IllegalStateException("No version line found.");
         } else {
@@ -259,16 +292,16 @@
                 // TODO(calin): the caller is responsible to clear the file.
                 throw new IllegalStateException("Invalid version line: " + versionLine);
             }
-            int version = Integer.parseInt(
+            version = Integer.parseInt(
                     versionLine.substring(PACKAGE_DEX_USAGE_VERSION_HEADER.length()));
-            if (version != PACKAGE_DEX_USAGE_VERSION) {
+            if (!isSupportedVersion(version)) {
                 throw new IllegalStateException("Unexpected version: " + version);
             }
         }
 
-        String s = null;
-        String currentPakage = null;
-        PackageUseInfo currentPakageData = null;
+        String s;
+        String currentPackage = null;
+        PackageUseInfo currentPackageData = null;
 
         Set<String> supportedIsas = new HashSet<>();
         for (String abi : Build.SUPPORTED_ABIS) {
@@ -280,17 +313,21 @@
                 // We expect two lines for each dex entry:
                 // #dexPaths
                 // onwerUserId,isUsedByOtherApps,isa1,isa2
-                if (currentPakage == null) {
+                if (currentPackage == null) {
                     throw new IllegalStateException(
                         "Malformed PackageDexUsage file. Expected package line before dex line.");
                 }
 
                 // First line is the dex path.
                 String dexPath = s.substring(DEX_LINE_CHAR.length());
+
+                // In version 2 the second line contains the list of packages that loaded the file.
+                List<String> loadingPackages = maybeReadLoadingPackages(in, version);
+
                 // Next line is the dex data.
                 s = in.readLine();
                 if (s == null) {
-                    throw new IllegalStateException("Could not fine dexUseInfo for line: " + s);
+                    throw new IllegalStateException("Could not find dexUseInfo for line: " + s);
                 }
 
                 // We expect at least 3 elements (isUsedByOtherApps, userId, isa).
@@ -301,6 +338,8 @@
                 int ownerUserId = Integer.parseInt(elems[0]);
                 boolean isUsedByOtherApps = readBoolean(elems[1]);
                 DexUseInfo dexUseInfo = new DexUseInfo(isUsedByOtherApps, ownerUserId);
+                dexUseInfo.mLoadingPackages.addAll(loadingPackages);
+
                 for (int i = 2; i < elems.length; i++) {
                     String isa = elems[i];
                     if (supportedIsas.contains(isa)) {
@@ -317,7 +356,7 @@
                             "unsupported isas. dexPath=" + dexPath);
                     continue;
                 }
-                currentPakageData.mDexUseInfoMap.put(dexPath, dexUseInfo);
+                currentPackageData.mDexUseInfoMap.put(dexPath, dexUseInfo);
             } else {
                 // This is a package line.
                 // We expect it to be: `packageName,isUsedByOtherApps`.
@@ -325,10 +364,11 @@
                 if (elems.length != 2) {
                     throw new IllegalStateException("Invalid PackageDexUsage line: " + s);
                 }
-                currentPakage = elems[0];
-                currentPakageData = new PackageUseInfo();
-                currentPakageData.mIsUsedByOtherApps = readBoolean(elems[1]);
-                data.put(currentPakage, currentPakageData);
+                currentPackage = elems[0];
+                currentPackageData = new PackageUseInfo();
+                currentPackageData.mIsUsedByOtherApps = readBoolean(elems[1]);
+                currentPackageData.mLoadingPackages.addAll(maybeReadLoadingPackages(in, version));
+                data.put(currentPackage, currentPackageData);
             }
         }
 
@@ -339,6 +379,43 @@
     }
 
     /**
+     * Reads the list of loading packages from the buffer {@parm in} if
+     * {@code version} is at least {PACKAGE_DEX_USAGE_VERSION}.
+     */
+    private List<String> maybeReadLoadingPackages(BufferedReader in, int version)
+            throws IOException {
+        if (version == PACKAGE_DEX_USAGE_VERSION) {
+            String line = in.readLine();
+            if (line == null) {
+                throw new IllegalStateException("Could not find the loadingPackages line.");
+            }
+            // We expect that most of the times the list of loading packages will be empty.
+            if (line.length() == LOADING_PACKAGE_CHAR.length()) {
+                return Collections.emptyList();
+            } else {
+                return Arrays.asList(
+                        line.substring(LOADING_PACKAGE_CHAR.length()).split(SPLIT_CHAR));
+            }
+        } else {
+            return Collections.emptyList();
+        }
+    }
+
+    /**
+     * Utility method which adds {@param loadingPackage} to {@param loadingPackages} only if it's
+     * not equal to {@param owningPackage}
+     */
+    private boolean maybeAddLoadingPackage(String owningPackage, String loadingPackage,
+            Set<String> loadingPackages) {
+        return !owningPackage.equals(loadingPackage) && loadingPackages.add(loadingPackage);
+    }
+
+    private boolean isSupportedVersion(int version) {
+        return version == PACKAGE_DEX_USAGE_VERSION ||
+                version == PACKAGE_DEX_USAGE_SUPPORTED_VERSION_1;
+    }
+
+    /**
      * Syncs the existing data with the set of available packages by removing obsolete entries.
      */
     public void syncData(Map<String, Set<Integer>> packageToUsersMap) {
@@ -539,10 +616,13 @@
         private boolean mIsUsedByOtherApps;
         // Map dex paths to their data (isUsedByOtherApps, owner id, loader isa).
         private final Map<String, DexUseInfo> mDexUseInfoMap;
+        // Packages who load this dex file.
+        private final Set<String> mLoadingPackages;
 
         public PackageUseInfo() {
             mIsUsedByOtherApps = false;
             mDexUseInfoMap = new HashMap<>();
+            mLoadingPackages = new HashSet<>();
         }
 
         // Creates a deep copy of the `other`.
@@ -552,6 +632,7 @@
             for (Map.Entry<String, DexUseInfo> e : other.mDexUseInfoMap.entrySet()) {
                 mDexUseInfoMap.put(e.getKey(), new DexUseInfo(e.getValue()));
             }
+            mLoadingPackages = new HashSet<>(other.mLoadingPackages);
         }
 
         private boolean merge(boolean isUsedByOtherApps) {
@@ -567,6 +648,10 @@
         public Map<String, DexUseInfo> getDexUseInfoMap() {
             return mDexUseInfoMap;
         }
+
+        public Set<String> getLoadingPackages() {
+            return mLoadingPackages;
+        }
     }
 
     /**
@@ -576,6 +661,8 @@
         private boolean mIsUsedByOtherApps;
         private final int mOwnerUserId;
         private final Set<String> mLoaderIsas;
+        // Packages who load this dex file.
+        private final Set<String> mLoadingPackages;
 
         public DexUseInfo(boolean isUsedByOtherApps, int ownerUserId) {
             this(isUsedByOtherApps, ownerUserId, null);
@@ -588,6 +675,7 @@
             if (loaderIsa != null) {
                 mLoaderIsas.add(loaderIsa);
             }
+            mLoadingPackages = new HashSet<>();
         }
 
         // Creates a deep copy of the `other`.
@@ -595,13 +683,16 @@
             mIsUsedByOtherApps = other.mIsUsedByOtherApps;
             mOwnerUserId = other.mOwnerUserId;
             mLoaderIsas = new HashSet<>(other.mLoaderIsas);
+            mLoadingPackages = new HashSet<>(other.mLoadingPackages);
         }
 
         private boolean merge(DexUseInfo dexUseInfo) {
             boolean oldIsUsedByOtherApps = mIsUsedByOtherApps;
             mIsUsedByOtherApps = mIsUsedByOtherApps || dexUseInfo.mIsUsedByOtherApps;
             boolean updateIsas = mLoaderIsas.addAll(dexUseInfo.mLoaderIsas);
-            return updateIsas || (oldIsUsedByOtherApps != mIsUsedByOtherApps);
+            boolean updateLoadingPackages = mLoadingPackages.addAll(dexUseInfo.mLoadingPackages);
+            return updateIsas || (oldIsUsedByOtherApps != mIsUsedByOtherApps) ||
+                    updateLoadingPackages;
         }
 
         public boolean isUsedByOtherApps() {
@@ -615,5 +706,9 @@
         public Set<String> getLoaderIsas() {
             return mLoaderIsas;
         }
+
+        public Set<String> getLoadingPackages() {
+            return mLoadingPackages;
+        }
     }
 }
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index 8425d23..75fc25a 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -358,10 +358,6 @@
     private static final String SYSUI_SCREENSHOT_ERROR_RECEIVER =
             "com.android.systemui.screenshot.ScreenshotServiceErrorReceiver";
 
-    private static final int NAV_BAR_BOTTOM = 0;
-    private static final int NAV_BAR_RIGHT = 1;
-    private static final int NAV_BAR_LEFT = 2;
-
     /**
      * Keyguard stuff
      */
@@ -6943,6 +6939,12 @@
     }
 
     @Override
+    public int getNavBarPosition() {
+        // TODO(multi-display): Support system decor on secondary displays.
+        return mNavigationBarPosition;
+    }
+
+    @Override
     public boolean isDockSideAllowed(int dockSide) {
 
         // We do not allow all dock sides at which the navigation bar touches the docked stack.
diff --git a/services/core/java/com/android/server/radio/Tuner.java b/services/core/java/com/android/server/radio/Tuner.java
index 81128c2..3d16cac 100644
--- a/services/core/java/com/android/server/radio/Tuner.java
+++ b/services/core/java/com/android/server/radio/Tuner.java
@@ -19,6 +19,7 @@
 import android.annotation.NonNull;
 import android.hardware.radio.ITuner;
 import android.hardware.radio.ITunerCallback;
+import android.hardware.radio.ProgramSelector;
 import android.hardware.radio.RadioManager;
 import android.os.IBinder;
 import android.os.RemoteException;
@@ -44,12 +45,13 @@
     private int mRegion;  // TODO(b/62710330): find better solution to handle regions
     private final boolean mWithAudio;
 
-    Tuner(@NonNull ITunerCallback clientCallback, int halRev, int region, boolean withAudio) {
+    Tuner(@NonNull ITunerCallback clientCallback, int halRev,
+            int region, boolean withAudio, int band) {
         mClientCallback = clientCallback;
         mTunerCallback = new TunerCallback(this, clientCallback, halRev);
         mRegion = region;
         mWithAudio = withAudio;
-        mNativeContext = nativeInit(halRev, withAudio);
+        mNativeContext = nativeInit(halRev, withAudio, band);
         mDeathRecipient = this::close;
         try {
             mClientCallback.asBinder().linkToDeath(mDeathRecipient, 0);
@@ -64,7 +66,7 @@
         super.finalize();
     }
 
-    private native long nativeInit(int halRev, boolean withAudio);
+    private native long nativeInit(int halRev, boolean withAudio, int band);
     private native void nativeFinalize(long nativeContext);
     private native void nativeClose(long nativeContext);
 
@@ -74,9 +76,11 @@
 
     private native void nativeStep(long nativeContext, boolean directionDown, boolean skipSubChannel);
     private native void nativeScan(long nativeContext, boolean directionDown, boolean skipSubChannel);
-    private native void nativeTune(long nativeContext, int channel, int subChannel);
+    private native void nativeTune(long nativeContext, @NonNull ProgramSelector selector);
     private native void nativeCancel(long nativeContext);
 
+    private native void nativeCancelAnnouncement(long nativeContext);
+
     private native RadioManager.ProgramInfo nativeGetProgramInformation(long nativeContext);
     private native boolean nativeStartBackgroundScan(long nativeContext);
     private native List<RadioManager.ProgramInfo> nativeGetProgramList(long nativeContext,
@@ -173,10 +177,14 @@
     }
 
     @Override
-    public void tune(int channel, int subChannel) {
+    public void tune(ProgramSelector selector) {
+        if (selector == null) {
+            throw new IllegalArgumentException("The argument must not be a null pointer");
+        }
+        Slog.i(TAG, "Tuning to " + selector);
         synchronized (mLock) {
             checkNotClosedLocked();
-            nativeTune(mNativeContext, channel, subChannel);
+            nativeTune(mNativeContext, selector);
         }
     }
 
@@ -189,6 +197,14 @@
     }
 
     @Override
+    public void cancelAnnouncement() {
+        synchronized (mLock) {
+            checkNotClosedLocked();
+            nativeCancelAnnouncement(mNativeContext);
+        }
+    }
+
+    @Override
     public RadioManager.ProgramInfo getProgramInformation() {
         synchronized (mLock) {
             checkNotClosedLocked();
diff --git a/services/core/java/com/android/server/radio/TunerCallback.java b/services/core/java/com/android/server/radio/TunerCallback.java
index 9430dc9..62110cf 100644
--- a/services/core/java/com/android/server/radio/TunerCallback.java
+++ b/services/core/java/com/android/server/radio/TunerCallback.java
@@ -86,13 +86,8 @@
     }
 
     @Override
-    public void onProgramInfoChanged(RadioManager.ProgramInfo info) {
-        dispatch(() -> mClientCallback.onProgramInfoChanged(info));
-    }
-
-    @Override
-    public void onMetadataChanged(RadioMetadata metadata) {
-        dispatch(() -> mClientCallback.onMetadataChanged(metadata));
+    public void onProgramInfoChanged() {
+        dispatch(() -> mClientCallback.onProgramInfoChanged());
     }
 
     @Override
diff --git a/services/core/java/com/android/server/timezone/PackageStatusStorage.java b/services/core/java/com/android/server/timezone/PackageStatusStorage.java
index fe82dc4..cac7f7b 100644
--- a/services/core/java/com/android/server/timezone/PackageStatusStorage.java
+++ b/services/core/java/com/android/server/timezone/PackageStatusStorage.java
@@ -16,6 +16,7 @@
 
 package com.android.server.timezone;
 
+import com.android.internal.annotations.GuardedBy;
 import com.android.internal.util.FastXmlSerializer;
 
 import org.xmlpull.v1.XmlPullParser;
@@ -80,7 +81,7 @@
     private final AtomicFile mPackageStatusFile;
 
     PackageStatusStorage(File storageDir) {
-        mPackageStatusFile = new AtomicFile(new File(storageDir, "packageStatus.xml"));
+        mPackageStatusFile = new AtomicFile(new File(storageDir, "package-status.xml"));
         if (!mPackageStatusFile.getBaseFile().exists()) {
             try {
                 insertInitialPackageStatus();
@@ -103,7 +104,7 @@
     PackageStatus getPackageStatus() {
         synchronized (this) {
             try {
-                return getPackageStatusInternal();
+                return getPackageStatusLocked();
             } catch (ParseException e) {
                 // This means that data exists in the file but it was bad.
                 Slog.e(LOG_TAG, "Package status invalid, resetting and retrying", e);
@@ -111,7 +112,7 @@
                 // Reset the storage so it is in a good state again.
                 recoverFromBadData(e);
                 try {
-                    return getPackageStatusInternal();
+                    return getPackageStatusLocked();
                 } catch (ParseException e2) {
                     throw new IllegalStateException("Recovery from bad file failed", e2);
                 }
@@ -119,7 +120,8 @@
         }
     }
 
-    private PackageStatus getPackageStatusInternal() throws ParseException {
+    @GuardedBy("this")
+    private PackageStatus getPackageStatusLocked() throws ParseException {
         try (FileInputStream fis = mPackageStatusFile.openRead()) {
             XmlPullParser parser = parseToPackageStatusTag(fis);
             Integer checkStatus = getNullableIntAttribute(parser, ATTRIBUTE_CHECK_STATUS);
@@ -137,7 +139,7 @@
         }
     }
 
-    // Callers should be synchronized(this).
+    @GuardedBy("this")
     private int recoverFromBadData(Exception cause) {
         mPackageStatusFile.delete();
         try {
@@ -155,7 +157,7 @@
         // is reset to ensure that old tokens are unlikely to work.
         final int initialOptimisticLockId = (int) System.currentTimeMillis();
 
-        writePackageStatusInternal(null /* status */, initialOptimisticLockId,
+        writePackageStatusLocked(null /* status */, initialOptimisticLockId,
                 null /* packageVersions */);
         return initialOptimisticLockId;
     }
@@ -243,7 +245,7 @@
         }
     }
 
-    // Caller should be synchronized(this).
+    @GuardedBy("this")
     private int getCurrentOptimisticLockId() throws ParseException {
         try (FileInputStream fis = mPackageStatusFile.openRead()) {
             XmlPullParser parser = parseToPackageStatusTag(fis);
@@ -278,7 +280,7 @@
         }
     }
 
-    // Caller should be synchronized(this).
+    @GuardedBy("this")
     private boolean writePackageStatusWithOptimisticLockCheck(int optimisticLockId,
             int newOptimisticLockId, Integer status, PackageVersions packageVersions)
             throws IOException {
@@ -294,12 +296,12 @@
             return false;
         }
 
-        writePackageStatusInternal(status, newOptimisticLockId, packageVersions);
+        writePackageStatusLocked(status, newOptimisticLockId, packageVersions);
         return true;
     }
 
-    // Caller should be synchronized(this).
-    private void writePackageStatusInternal(Integer status, int optimisticLockId,
+    @GuardedBy("this")
+    private void writePackageStatusLocked(Integer status, int optimisticLockId,
             PackageVersions packageVersions) throws IOException {
         if ((status == null) != (packageVersions == null)) {
             throw new IllegalArgumentException(
diff --git a/services/core/java/com/android/server/timezone/PackageTracker.java b/services/core/java/com/android/server/timezone/PackageTracker.java
index e8dfd77..9b49996 100644
--- a/services/core/java/com/android/server/timezone/PackageTracker.java
+++ b/services/core/java/com/android/server/timezone/PackageTracker.java
@@ -164,33 +164,29 @@
         }
 
         // Validate the updater application package.
-        // TODO(nfuller) Uncomment or remove the code below. Currently an app stops being a priv-app
-        // after it is replaced by one in data so this check fails. http://b/35995024
-        // try {
-        //     if (!mPackageManagerHelper.isPrivilegedApp(mUpdateAppPackageName)) {
-        //         throw failWithException(
-        //                 "Update app " + mUpdateAppPackageName + " must be a priv-app.", null);
-        //     }
-        // } catch (PackageManager.NameNotFoundException e) {
-        //     throw failWithException("Could not determine update app package details for "
-        //             + mUpdateAppPackageName, e);
-        // }
+        try {
+            if (!mPackageManagerHelper.isPrivilegedApp(mUpdateAppPackageName)) {
+                throw logAndThrowRuntimeException(
+                        "Update app " + mUpdateAppPackageName + " must be a priv-app.", null);
+            }
+        } catch (PackageManager.NameNotFoundException e) {
+            throw logAndThrowRuntimeException("Could not determine update app package details for "
+                    + mUpdateAppPackageName, e);
+        }
         // TODO(nfuller) Consider permission checks. While an updated system app retains permissions
         // obtained by the system version it's not clear how to check them.
         Slog.d(TAG, "Update app " + mUpdateAppPackageName + " is valid.");
 
         // Validate the data application package.
-        // TODO(nfuller) Uncomment or remove the code below. Currently an app stops being a priv-app
-        // after it is replaced by one in data. http://b/35995024
-        // try {
-        //     if (!mPackageManagerHelper.isPrivilegedApp(mDataAppPackageName)) {
-        //         throw failWithException(
-        //                 "Data app " + mDataAppPackageName + " must be a priv-app.", null);
-        //     }
-        // } catch (PackageManager.NameNotFoundException e) {
-        //     throw failWithException("Could not determine data app package details for "
-        //             + mDataAppPackageName, e);
-        // }
+        try {
+            if (!mPackageManagerHelper.isPrivilegedApp(mDataAppPackageName)) {
+                throw logAndThrowRuntimeException(
+                        "Data app " + mDataAppPackageName + " must be a priv-app.", null);
+            }
+        } catch (PackageManager.NameNotFoundException e) {
+            throw logAndThrowRuntimeException("Could not determine data app package details for "
+                    + mDataAppPackageName, e);
+        }
         // TODO(nfuller) Consider permission checks. While an updated system app retains permissions
         // obtained by the system version it's not clear how to check them.
         Slog.d(TAG, "Data app " + mDataAppPackageName + " is valid.");
diff --git a/services/core/java/com/android/server/timezone/RulesManagerServiceHelperImpl.java b/services/core/java/com/android/server/timezone/RulesManagerServiceHelperImpl.java
index 767f0e0..b89ce1c 100644
--- a/services/core/java/com/android/server/timezone/RulesManagerServiceHelperImpl.java
+++ b/services/core/java/com/android/server/timezone/RulesManagerServiceHelperImpl.java
@@ -18,6 +18,7 @@
 
 import android.content.Context;
 import android.content.pm.PackageManager;
+import android.os.AsyncTask;
 import android.os.Binder;
 import android.os.ParcelFileDescriptor;
 
@@ -56,10 +57,9 @@
         return true;
     }
 
-    // TODO Wake lock required?
+    // TODO(nfuller): Wake lock required while running in background thread?
     @Override
     public void execute(Runnable runnable) {
-        // TODO Is there a better way?
-        new Thread(runnable).start();
+        AsyncTask.execute(runnable);
     }
 }
diff --git a/services/core/java/com/android/server/wm/AppWindowContainerController.java b/services/core/java/com/android/server/wm/AppWindowContainerController.java
index 86e130d..a6ffe83 100644
--- a/services/core/java/com/android/server/wm/AppWindowContainerController.java
+++ b/services/core/java/com/android/server/wm/AppWindowContainerController.java
@@ -685,7 +685,10 @@
 
             if (DEBUG_STARTING_WINDOW) Slog.v(TAG_WM, "Schedule remove starting " + mContainer
                     + " startingWindow=" + mContainer.startingWindow);
-            mHandler.post(mRemoveStartingWindow);
+
+            // Use the same thread to remove the window as we used to add it, as otherwise we end up
+            // with things in the view hierarchy being called from different threads.
+            mService.mAnimationHandler.post(mRemoveStartingWindow);
         }
     }
 
diff --git a/services/core/java/com/android/server/wm/AppWindowToken.java b/services/core/java/com/android/server/wm/AppWindowToken.java
index 839ee0e..b9d02a9 100644
--- a/services/core/java/com/android/server/wm/AppWindowToken.java
+++ b/services/core/java/com/android/server/wm/AppWindowToken.java
@@ -1357,8 +1357,10 @@
      * @return {@code true} If all children have been considered, {@code false}.
      */
     private boolean allDrawnStatesConsidered() {
-        for (WindowState child : mChildren) {
-            if (!child.getDrawnStatedEvaluated()) {
+        for (int i = mChildren.size() - 1; i >= 0; --i) {
+            final WindowState child = mChildren.get(i);
+            if (child.mightAffectAllDrawn(false /*visibleOnly*/ )
+                    && !child.getDrawnStateEvaluated()) {
                 return false;
             }
         }
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 9fe7381..05f4626 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -1073,7 +1073,7 @@
             }
             if (w.mHasSurface && !rotateSeamlessly) {
                 if (DEBUG_ORIENTATION) Slog.v(TAG_WM, "Set mOrientationChanging of " + w);
-                w.mOrientationChanging = true;
+                w.setOrientationChanging(true);
                 mService.mRoot.mOrientationChangeComplete = false;
                 w.mLastFreezeDuration = 0;
             }
@@ -2679,10 +2679,10 @@
         mService.mWindowsFreezingScreen = WINDOWS_FREEZING_SCREENS_TIMEOUT;
 
         forAllWindows(w -> {
-            if (!w.mOrientationChanging) {
+            if (!w.getOrientationChanging()) {
                 return;
             }
-            w.mOrientationChanging = false;
+            w.setOrientationChanging(false);
             w.mLastFreezeDuration = (int)(SystemClock.elapsedRealtime()
                     - mService.mDisplayFreezeTime);
             Slog.w(TAG_WM, "Force clearing orientation change: " + w);
diff --git a/services/core/java/com/android/server/wm/TaskWindowContainerController.java b/services/core/java/com/android/server/wm/TaskWindowContainerController.java
index 9f02485..54a6cc0 100644
--- a/services/core/java/com/android/server/wm/TaskWindowContainerController.java
+++ b/services/core/java/com/android/server/wm/TaskWindowContainerController.java
@@ -80,7 +80,9 @@
             final Task task = createTask(taskId, stack, userId, bounds, overrideConfig, resizeMode,
                     supportsPictureInPicture, homeTask, taskDescription);
             final int position = toTop ? POSITION_TOP : POSITION_BOTTOM;
-            stack.addTask(task, position, showForAllUsers, true /* moveParents */);
+            // We only want to move the parents to the parents if we are creating this task at the
+            // top of its stack.
+            stack.addTask(task, position, showForAllUsers, toTop /* moveParents */);
         }
     }
 
diff --git a/services/core/java/com/android/server/wm/WindowLayersController.java b/services/core/java/com/android/server/wm/WindowLayersController.java
index 01a3143..5dc79f8 100644
--- a/services/core/java/com/android/server/wm/WindowLayersController.java
+++ b/services/core/java/com/android/server/wm/WindowLayersController.java
@@ -17,13 +17,14 @@
 package com.android.server.wm;
 
 import android.util.Slog;
-import android.view.Display;
 
 import java.util.ArrayDeque;
 import java.util.function.Consumer;
 
+import static android.app.ActivityManager.StackId;
 import static android.app.ActivityManager.StackId.ASSISTANT_STACK_ID;
 import static android.app.ActivityManager.StackId.DOCKED_STACK_ID;
+import static android.app.ActivityManager.StackId.INVALID_STACK_ID;
 import static android.app.ActivityManager.StackId.PINNED_STACK_ID;
 import static android.view.Display.DEFAULT_DISPLAY;
 import static android.view.WindowManager.LayoutParams.TYPE_DOCK_DIVIDER;
@@ -56,7 +57,6 @@
         mService = service;
     }
 
-    private int mHighestApplicationLayer = 0;
     private ArrayDeque<WindowState> mPinnedWindows = new ArrayDeque<>();
     private ArrayDeque<WindowState> mDockedWindows = new ArrayDeque<>();
     private ArrayDeque<WindowState> mAssistantWindows = new ArrayDeque<>();
@@ -66,6 +66,8 @@
     private int mCurBaseLayer;
     private int mCurLayer;
     private boolean mAnyLayerChanged;
+    private int mHighestApplicationLayer;
+    private int mHighestDockedAffectedLayer;
     private int mHighestLayerInImeTargetBaseLayer;
     private WindowState mImeTarget;
     private boolean mAboveImeTarget;
@@ -98,6 +100,10 @@
             mHighestLayerInImeTargetBaseLayer = Math.max(mHighestLayerInImeTargetBaseLayer,
                     w.mWinAnimator.mAnimLayer);
         }
+        if (w.getAppToken() != null && StackId.isResizeableByDockedStack(w.getStackId())) {
+            mHighestDockedAffectedLayer = Math.max(mHighestDockedAffectedLayer,
+                    w.mWinAnimator.mAnimLayer);
+        }
 
         collectSpecialWindows(w);
 
@@ -135,7 +141,6 @@
     }
 
     private void reset() {
-        mHighestApplicationLayer = 0;
         mPinnedWindows.clear();
         mInputMethodWindows.clear();
         mDockedWindows.clear();
@@ -147,8 +152,10 @@
         mCurLayer = 0;
         mAnyLayerChanged = false;
 
-        mImeTarget = mService.mInputMethodTarget;
+        mHighestApplicationLayer = 0;
+        mHighestDockedAffectedLayer = 0;
         mHighestLayerInImeTargetBaseLayer = (mImeTarget != null) ? mImeTarget.mBaseLayer : 0;
+        mImeTarget = mService.mInputMethodTarget;
         mAboveImeTarget = false;
         mAboveImeTargetAppWindows.clear();
     }
@@ -179,32 +186,41 @@
             }
         }
 
-        final Task task = w.getTask();
-        if (task == null) {
-            return;
-        }
-        final TaskStack stack = task.mStack;
-        if (stack == null) {
-            return;
-        }
-        if (stack.mStackId == PINNED_STACK_ID) {
+        final int stackId = w.getAppToken() != null ? w.getStackId() : INVALID_STACK_ID;
+        if (stackId == PINNED_STACK_ID) {
             mPinnedWindows.add(w);
-        } else if (stack.mStackId == DOCKED_STACK_ID) {
+        } else if (stackId == DOCKED_STACK_ID) {
             mDockedWindows.add(w);
-        } else if (stack.mStackId == ASSISTANT_STACK_ID) {
+        } else if (stackId == ASSISTANT_STACK_ID) {
             mAssistantWindows.add(w);
         }
     }
 
     private void adjustSpecialWindows() {
-        int layer = mHighestApplicationLayer + WINDOW_LAYER_MULTIPLIER;
-        // For pinned and docked stack window, we want to make them above other windows also when
-        // these windows are animating.
-        while (!mDockedWindows.isEmpty()) {
-            layer = assignAndIncreaseLayerIfNeeded(mDockedWindows.remove(), layer);
+        // The following adjustments are beyond the highest docked-affected layer
+        int layer = mHighestDockedAffectedLayer +  WINDOW_LAYER_MULTIPLIER;
+
+        // Adjust the docked stack windows and dock divider above only the windows that are affected
+        // by the docked stack. When this happens, also boost the assistant window layers, otherwise
+        // the docked stack windows & divider would be promoted above the assistant.
+        if (!mDockedWindows.isEmpty() && mHighestDockedAffectedLayer > 0) {
+            while (!mDockedWindows.isEmpty()) {
+                final WindowState window = mDockedWindows.remove();
+                layer = assignAndIncreaseLayerIfNeeded(window, layer);
+            }
+
+            layer = assignAndIncreaseLayerIfNeeded(mDockDivider, layer);
+
+            while (!mAssistantWindows.isEmpty()) {
+                final WindowState window = mAssistantWindows.remove();
+                if (window.mLayer > mHighestDockedAffectedLayer) {
+                    layer = assignAndIncreaseLayerIfNeeded(window, layer);
+                }
+            }
         }
 
-        layer = assignAndIncreaseLayerIfNeeded(mDockDivider, layer);
+        // The following adjustments are beyond the highest app layer or boosted layer
+        layer = Math.max(layer, mHighestApplicationLayer + WINDOW_LAYER_MULTIPLIER);
 
         // We know that we will be animating a relaunching window in the near future, which will
         // receive a z-order increase. We want the replaced window to immediately receive the same
@@ -213,12 +229,6 @@
             layer = assignAndIncreaseLayerIfNeeded(mReplacingWindows.remove(), layer);
         }
 
-        // Adjust the assistant stack windows to be above the docked and fullscreen stack windows,
-        // but under the pinned stack windows
-        while (!mAssistantWindows.isEmpty()) {
-            layer = assignAndIncreaseLayerIfNeeded(mAssistantWindows.remove(), layer);
-        }
-
         while (!mPinnedWindows.isEmpty()) {
             layer = assignAndIncreaseLayerIfNeeded(mPinnedWindows.remove(), layer);
         }
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 730c08c..902c2ff 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -5796,7 +5796,7 @@
         // orientation.
         if (!okToDisplay() && mWindowsFreezingScreen != WINDOWS_FREEZING_SCREENS_TIMEOUT) {
             if (DEBUG_ORIENTATION) Slog.v(TAG_WM, "Changing surface while display frozen: " + w);
-            w.mOrientationChanging = true;
+            w.setOrientationChanging(true);
             w.mLastFreezeDuration = 0;
             mRoot.mOrientationChangeComplete = false;
             if (mWindowsFreezingScreen == WINDOWS_FREEZING_SCREENS_NONE) {
@@ -6299,6 +6299,22 @@
         }
     }
 
+    /**
+     * Used by ActivityManager to determine where to position an app with aspect ratio shorter then
+     * the screen is.
+     * @see WindowManagerPolicy#getNavBarPosition()
+     */
+    public int getNavBarPosition() {
+        synchronized (mWindowMap) {
+            // Perform layout if it was scheduled before to make sure that we get correct nav bar
+            // position when doing rotations.
+            final DisplayContent defaultDisplayContent = getDefaultDisplayContentLocked();
+            defaultDisplayContent.performLayout(false /* initial */,
+                    false /* updateInputWindows */);
+            return mPolicy.getNavBarPosition();
+        }
+    }
+
     @Override
     public WindowManagerPolicy.InputConsumer createInputConsumer(Looper looper, String name,
             InputEventReceiver.Factory inputEventReceiverFactory) {
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 92e8f49..7decb11 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -446,7 +446,7 @@
      * Set when the orientation is changing and this window has not yet
      * been updated for the new orientation.
      */
-    boolean mOrientationChanging;
+    private boolean mOrientationChanging;
 
     /**
      * The orientation during the last visible call to relayout. If our
@@ -690,7 +690,7 @@
     /**
      * Returns whether this {@link WindowState} has been considered for drawing by its parent.
      */
-    boolean getDrawnStatedEvaluated() {
+    boolean getDrawnStateEvaluated() {
         return mDrawnStateEvaluated;
     }
 
@@ -1189,7 +1189,8 @@
             // then we need to hold off on unfreezing the display until this window has been
             // redrawn; to do that, we need to go through the process of getting informed by the
             // application when it has finished drawing.
-            if (mOrientationChanging || dragResizingChanged || isResizedWhileNotDragResizing()) {
+            if (getOrientationChanging() || dragResizingChanged
+                    || isResizedWhileNotDragResizing()) {
                 if (DEBUG_SURFACE_TRACE || DEBUG_ANIM || DEBUG_ORIENTATION || DEBUG_RESIZE) {
                     Slog.v(TAG_WM, "Orientation or resize start waiting for draw"
                             + ", mDrawState=DRAW_PENDING in " + this
@@ -1204,17 +1205,33 @@
                 if (DEBUG_RESIZE || DEBUG_ORIENTATION) Slog.v(TAG_WM, "Resizing window " + this);
                 mService.mResizingWindows.add(this);
             }
-        } else if (mOrientationChanging) {
+        } else if (getOrientationChanging()) {
             if (isDrawnLw()) {
                 if (DEBUG_ORIENTATION) Slog.v(TAG_WM, "Orientation not waiting for draw in "
                         + this + ", surfaceController " + winAnimator.mSurfaceController);
-                mOrientationChanging = false;
+                setOrientationChanging(false);
                 mLastFreezeDuration = (int)(SystemClock.elapsedRealtime()
                         - mService.mDisplayFreezeTime);
             }
         }
     }
 
+    boolean getOrientationChanging() {
+        // In addition to the local state flag, we must also consider the difference in the last
+        // reported configuration vs. the current state. If the client code has not been informed of
+        // the change, logic dependent on having finished processing the orientation, such as
+        // unfreezing, could be improperly triggered.
+        // TODO(b/62846907): Checking against {@link mLastReportedConfiguration} could be flaky as
+        //                   this is not necessarily what the client has processed yet. Find a
+        //                   better indicator consistent with the client.
+        return mOrientationChanging || (isVisible()
+                && getConfiguration().orientation != mLastReportedConfiguration.orientation);
+    }
+
+    void setOrientationChanging(boolean changing) {
+        mOrientationChanging = changing;
+    }
+
     DisplayContent getDisplayContent() {
         return mToken.getDisplayContent();
     }
@@ -1975,7 +1992,7 @@
             return false;
         }
 
-        final boolean windowsAreFocusable = mAppToken != null && mAppToken.windowsAreFocusable();
+        final boolean windowsAreFocusable = mAppToken == null || mAppToken.windowsAreFocusable();
         if (!windowsAreFocusable) {
             // This window can't be an IME target if the app's windows should not be focusable.
             return false;
@@ -2694,10 +2711,10 @@
 
         mAppFreezing = false;
 
-        if (mHasSurface && !mOrientationChanging
+        if (mHasSurface && !getOrientationChanging()
                 && mService.mWindowsFreezingScreen != WINDOWS_FREEZING_SCREENS_TIMEOUT) {
             if (DEBUG_ORIENTATION) Slog.v(TAG_WM, "set mOrientationChanging of " + this);
-            mOrientationChanging = true;
+            setOrientationChanging(true);
             mService.mRoot.mOrientationChangeComplete = false;
         }
         mLastFreezeDuration = 0;
@@ -3116,7 +3133,7 @@
             mWinAnimator.mSurfaceResized = false;
             mReportOrientationChanged = false;
         } catch (RemoteException e) {
-            mOrientationChanging = false;
+            setOrientationChanging(false);
             mLastFreezeDuration = (int)(SystemClock.elapsedRealtime()
                     - mService.mDisplayFreezeTime);
             // We are assuming the hosting process is dead or in a zombie state.
@@ -3375,7 +3392,11 @@
                 pw.print(prefix); pw.print("mAppToken="); pw.println(mAppToken);
                 pw.print(prefix); pw.print(" isAnimatingWithSavedSurface()=");
                 pw.print(isAnimatingWithSavedSurface());
-                pw.print(" mAppDied=");pw.println(mAppDied);
+                pw.print(" mAppDied=");pw.print(mAppDied);
+                pw.print(prefix); pw.print("drawnStateEvaluated=");
+                        pw.print(getDrawnStateEvaluated());
+                pw.print(prefix); pw.print("mightAffectAllDrawn=");
+                        pw.println(mightAffectAllDrawn(false /*visibleOnly*/));
             }
             pw.print(prefix); pw.print("mViewVisibility=0x");
             pw.print(Integer.toHexString(mViewVisibility));
@@ -3477,10 +3498,13 @@
                     pw.print(" mDestroying="); pw.print(mDestroying);
                     pw.print(" mRemoved="); pw.println(mRemoved);
         }
-        if (mOrientationChanging || mAppFreezing || mTurnOnScreen
+        if (getOrientationChanging() || mAppFreezing || mTurnOnScreen
                 || mReportOrientationChanged) {
             pw.print(prefix); pw.print("mOrientationChanging=");
                     pw.print(mOrientationChanging);
+                    pw.print(" configOrientationChanging=");
+                    pw.print(mLastReportedConfiguration.orientation
+                            != getConfiguration().orientation);
                     pw.print(" mAppFreezing="); pw.print(mAppFreezing);
                     pw.print(" mTurnOnScreen="); pw.print(mTurnOnScreen);
                     pw.print(" mReportOrientationChanged="); pw.println(mReportOrientationChanged);
@@ -3517,6 +3541,8 @@
         if (computeDragResizing()) {
             pw.print(prefix); pw.println("computeDragResizing=" + computeDragResizing());
         }
+        pw.print(prefix); pw.println("isOnScreen=" + isOnScreen());
+        pw.print(prefix); pw.println("isVisible=" + isVisible());
     }
 
     @Override
diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java
index cd55156..8f1065f 100644
--- a/services/core/java/com/android/server/wm/WindowStateAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java
@@ -1527,11 +1527,11 @@
 
             // There is no need to wait for an animation change if our window is gone for layout
             // already as we'll never be visible.
-            if (w.mOrientationChanging && w.isGoneForLayoutLw()) {
+            if (w.getOrientationChanging() && w.isGoneForLayoutLw()) {
                 if (DEBUG_ORIENTATION) {
                     Slog.v(TAG, "Orientation change skips hidden " + w);
                 }
-                w.mOrientationChanging = false;
+                w.setOrientationChanging(false);
             }
             return;
         }
@@ -1564,8 +1564,8 @@
             // really hidden (gone for layout), there is no point in still waiting for it.
             // Note that this does introduce a potential glitch if the window becomes unhidden
             // before it has drawn for the new orientation.
-            if (w.mOrientationChanging && w.isGoneForLayoutLw()) {
-                w.mOrientationChanging = false;
+            if (w.getOrientationChanging() && w.isGoneForLayoutLw()) {
+                w.setOrientationChanging(false);
                 if (DEBUG_ORIENTATION) Slog.v(TAG,
                         "Orientation change skips hidden " + w);
             }
@@ -1618,7 +1618,7 @@
                     mAnimator.setPendingLayoutChanges(w.getDisplayId(),
                             WindowManagerPolicy.FINISH_LAYOUT_REDO_ANIM);
                 } else {
-                    w.mOrientationChanging = false;
+                    w.setOrientationChanging(false);
                 }
             }
             if (hasSurface()) {
@@ -1631,14 +1631,14 @@
             displayed = true;
         }
 
-        if (w.mOrientationChanging) {
+        if (w.getOrientationChanging()) {
             if (!w.isDrawnLw()) {
                 mAnimator.mBulkUpdateParams &= ~SET_ORIENTATION_CHANGE_COMPLETE;
                 mAnimator.mLastWindowFreezeSource = w;
                 if (DEBUG_ORIENTATION) Slog.v(TAG,
                         "Orientation continue waiting for draw in " + w);
             } else {
-                w.mOrientationChanging = false;
+                w.setOrientationChanging(false);
                 if (DEBUG_ORIENTATION) Slog.v(TAG, "Orientation change complete in " + w);
             }
         }
diff --git a/services/core/java/com/android/server/wm/WindowSurfacePlacer.java b/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
index 82c862f..6909892 100644
--- a/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
+++ b/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
@@ -233,7 +233,7 @@
      */
     int handleAppTransitionReadyLocked() {
         int appsCount = mService.mOpeningApps.size();
-        if (!transitionGoodToGo(appsCount)) {
+        if (!transitionGoodToGo(appsCount, mTempTransitionReasons)) {
             return 0;
         }
         Trace.traceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "AppTransitionReady");
@@ -375,6 +375,9 @@
                 true /*updateInputWindows*/);
         mService.mFocusMayChange = false;
 
+        mService.mH.obtainMessage(NOTIFY_APP_TRANSITION_STARTING,
+                mTempTransitionReasons.clone()).sendToTarget();
+
         Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER);
 
         return layoutRedo | FINISH_LAYOUT_REDO_LAYOUT | FINISH_LAYOUT_REDO_CONFIG;
@@ -499,7 +502,7 @@
         }
     }
 
-    private boolean transitionGoodToGo(int appsCount) {
+    private boolean transitionGoodToGo(int appsCount, SparseIntArray outReasons) {
         if (DEBUG_APP_TRANSITIONS) Slog.v(TAG,
                 "Checking " + appsCount + " opening apps (frozen="
                         + mService.mDisplayFrozen + " timeout="
@@ -508,7 +511,7 @@
             mService.mAnimator.getScreenRotationAnimationLocked(
                     Display.DEFAULT_DISPLAY);
 
-        final SparseIntArray reasons = mTempTransitionReasons;
+        outReasons.clear();
         if (!mService.mAppTransition.isTimeout()) {
             // Imagine the case where we are changing orientation due to an app transition, but a previous
             // orientation change is still in progress. We won't process the orientation change
@@ -542,10 +545,10 @@
                 final TaskStack stack = wtoken.getStack();
                 final int stackId = stack != null ? stack.mStackId : INVALID_STACK_ID;
                 if (allDrawn) {
-                    reasons.put(stackId, drawnBeforeRestoring ? APP_TRANSITION_WINDOWS_DRAWN
+                    outReasons.put(stackId, drawnBeforeRestoring ? APP_TRANSITION_WINDOWS_DRAWN
                             : APP_TRANSITION_SAVED_SURFACE);
                 } else {
-                    reasons.put(stackId, wtoken.startingData instanceof SplashScreenStartingData
+                    outReasons.put(stackId, wtoken.startingData instanceof SplashScreenStartingData
                             ? APP_TRANSITION_SPLASH_SCREEN
                             : APP_TRANSITION_SNAPSHOT);
                 }
@@ -569,13 +572,10 @@
             boolean wallpaperReady = !mWallpaperControllerLocked.isWallpaperVisible() ||
                     mWallpaperControllerLocked.wallpaperTransitionReady();
             if (wallpaperReady) {
-                mService.mH.obtainMessage(NOTIFY_APP_TRANSITION_STARTING, reasons.clone())
-                        .sendToTarget();
                 return true;
             }
             return false;
         }
-        mService.mH.obtainMessage(NOTIFY_APP_TRANSITION_STARTING, reasons.clone()).sendToTarget();
         return true;
     }
 
diff --git a/services/core/jni/Android.mk b/services/core/jni/Android.mk
index c8c629f..a6065025 100644
--- a/services/core/jni/Android.mk
+++ b/services/core/jni/Android.mk
@@ -108,4 +108,6 @@
     android.frameworks.schedulerservice@1.0 \
     android.frameworks.sensorservice@1.0 \
 
-LOCAL_STATIC_LIBRARIES += libscrypt_static
+LOCAL_STATIC_LIBRARIES += \
+    android.hardware.broadcastradio@1.1-utils-lib \
+    libscrypt_static \
diff --git a/services/core/jni/com_android_server_VibratorService.cpp b/services/core/jni/com_android_server_VibratorService.cpp
index cb8416b..45e9cc7 100644
--- a/services/core/jni/com_android_server_VibratorService.cpp
+++ b/services/core/jni/com_android_server_VibratorService.cpp
@@ -73,11 +73,13 @@
         ret = (sHal == nullptr) ? NullptrStatus<R>()
                 : (*sHal.*fn)(std::forward<Args1>(args1)...);
 
-        if (!ret.isOk()) {
-            ALOGE("Failed to issue command to vibrator HAL. Retrying.");
-            // Restoring connection to the HAL.
-            sHal = I::tryGetService();
+        if (ret.isOk()) {
+            break;
         }
+
+        ALOGE("Failed to issue command to vibrator HAL. Retrying.");
+        // Restoring connection to the HAL.
+        sHal = I::tryGetService();
     }
     return ret;
 }
diff --git a/services/core/jni/com_android_server_am_BatteryStatsService.cpp b/services/core/jni/com_android_server_am_BatteryStatsService.cpp
index 37ae782..2dfd8b9 100644
--- a/services/core/jni/com_android_server_am_BatteryStatsService.cpp
+++ b/services/core/jni/com_android_server_am_BatteryStatsService.cpp
@@ -59,7 +59,7 @@
 
 static bool wakeup_init = false;
 static sem_t wakeup_sem;
-extern sp<IPower> gPowerHal;
+extern sp<android::hardware::power::V1_0::IPower> gPowerHalV1_0;
 extern std::mutex gPowerHalMutex;
 extern bool getPowerHal();
 
@@ -203,7 +203,7 @@
             return -1;
         }
 
-        Return<void> ret = gPowerHal->getPlatformLowPowerStats(
+        Return<void> ret = gPowerHalV1_0->getPlatformLowPowerStats(
             [&offset, &remaining, &total_added](hidl_vec<PowerStatePlatformSleepState> states,
                     Status status) {
                 if (status != Status::SUCCESS)
@@ -257,7 +257,7 @@
 
         if (!ret.isOk()) {
             ALOGE("getPlatformLowPowerStats() failed: power HAL service not available");
-            gPowerHal = nullptr;
+            gPowerHalV1_0 = nullptr;
             return -1;
         }
     }
@@ -288,7 +288,7 @@
         }
 
         //Trying to cast to 1.1, this will succeed only for devices supporting 1.1
-        gPowerHal_1_1 = android::hardware::power::V1_1::IPower::castFrom(gPowerHal);
+        gPowerHal_1_1 = android::hardware::power::V1_1::IPower::castFrom(gPowerHalV1_0);
     	if (gPowerHal_1_1 == nullptr) {
             //This device does not support IPower@1.1, exiting gracefully
             return 0;
@@ -351,7 +351,7 @@
 
         if (!ret.isOk()) {
             ALOGE("getSubsystemLowPowerStats() failed: power HAL service not available");
-            gPowerHal = nullptr;
+            gPowerHalV1_0 = nullptr;
             return -1;
         }
     }
diff --git a/services/core/jni/com_android_server_location_GnssLocationProvider.cpp b/services/core/jni/com_android_server_location_GnssLocationProvider.cpp
index edd7d53..4fb2ae3 100644
--- a/services/core/jni/com_android_server_location_GnssLocationProvider.cpp
+++ b/services/core/jni/com_android_server_location_GnssLocationProvider.cpp
@@ -1523,6 +1523,10 @@
                           << " satellites:: " << std::endl;
         }
 
+        internalState << "constellation: 1=GPS, 2=SBAS, 3=GLO, 4=QZSS, 5=BDS, 6=GAL; "
+                      << "ephemerisType: 0=Eph, 1=Alm, 2=?; "
+                      << "ephemerisSource: 0=Demod, 1=Supl, 2=Server, 3=?; "
+                      << "ephemerisHealth: 0=Good, 1=Bad, 2=?" << std::endl;
         for (size_t i = 0; i < data.satelliteDataArray.size(); i++) {
             internalState << "svid: " << data.satelliteDataArray[i].svid
                           << ", constellation: "
diff --git a/services/core/jni/com_android_server_power_PowerManagerService.cpp b/services/core/jni/com_android_server_power_PowerManagerService.cpp
index 29924dd..2db7dbe 100644
--- a/services/core/jni/com_android_server_power_PowerManagerService.cpp
+++ b/services/core/jni/com_android_server_power_PowerManagerService.cpp
@@ -18,7 +18,7 @@
 
 //#define LOG_NDEBUG 0
 
-#include <android/hardware/power/1.0/IPower.h>
+#include <android/hardware/power/1.1/IPower.h>
 #include "JNIHelp.h"
 #include "jni.h"
 
@@ -41,7 +41,7 @@
 
 using android::hardware::Return;
 using android::hardware::Void;
-using android::hardware::power::V1_0::IPower;
+using android::hardware::power::V1_1::IPower;
 using android::hardware::power::V1_0::PowerHint;
 using android::hardware::power::V1_0::Feature;
 using android::String8;
@@ -57,13 +57,14 @@
 // ----------------------------------------------------------------------------
 
 static jobject gPowerManagerServiceObj;
-sp<IPower> gPowerHal = nullptr;
+sp<android::hardware::power::V1_0::IPower> gPowerHalV1_0 = nullptr;
+sp<android::hardware::power::V1_1::IPower> gPowerHalV1_1 = nullptr;
 bool gPowerHalExists = true;
 std::mutex gPowerHalMutex;
 static nsecs_t gLastEventTime[USER_ACTIVITY_EVENT_LAST + 1];
 
 // Throttling interval for user activity calls.
-static const nsecs_t MIN_TIME_BETWEEN_USERACTIVITIES = 500 * 1000000L; // 500ms
+static const nsecs_t MIN_TIME_BETWEEN_USERACTIVITIES = 100 * 1000000L; // 100ms
 
 // ----------------------------------------------------------------------------
 
@@ -80,16 +81,17 @@
 // Check validity of current handle to the power HAL service, and call getService() if necessary.
 // The caller must be holding gPowerHalMutex.
 bool getPowerHal() {
-    if (gPowerHalExists && gPowerHal == nullptr) {
-        gPowerHal = IPower::getService();
-        if (gPowerHal != nullptr) {
+    if (gPowerHalExists && gPowerHalV1_0 == nullptr) {
+        gPowerHalV1_0 = android::hardware::power::V1_0::IPower::getService();
+        if (gPowerHalV1_0 != nullptr) {
+            gPowerHalV1_1 =  android::hardware::power::V1_1::IPower::castFrom(gPowerHalV1_0);
             ALOGI("Loaded power HAL service");
         } else {
             ALOGI("Couldn't load power HAL service");
             gPowerHalExists = false;
         }
     }
-    return gPowerHal != nullptr;
+    return gPowerHalV1_0 != nullptr;
 }
 
 // Check if a call to a power HAL function failed; if so, log the failure and invalidate the
@@ -97,19 +99,11 @@
 static void processReturn(const Return<void> &ret, const char* functionName) {
     if (!ret.isOk()) {
         ALOGE("%s() failed: power HAL service not available.", functionName);
-        gPowerHal = nullptr;
+        gPowerHalV1_0 = nullptr;
     }
 }
 
 void android_server_PowerManagerService_userActivity(nsecs_t eventTime, int32_t eventType) {
-    // Tell the power HAL when user activity occurs.
-    gPowerHalMutex.lock();
-    if (getPowerHal()) {
-        Return<void> ret = gPowerHal->powerHint(PowerHint::INTERACTION, 0);
-        processReturn(ret, "powerHint");
-    }
-    gPowerHalMutex.unlock();
-
     if (gPowerManagerServiceObj) {
         // Throttle calls into user activity by event type.
         // We're a little conservative about argument checking here in case the caller
@@ -124,6 +118,21 @@
                 return;
             }
             gLastEventTime[eventType] = eventTime;
+
+
+            // Tell the power HAL when user activity occurs.
+            gPowerHalMutex.lock();
+            if (getPowerHal()) {
+              Return<void> ret;
+              if (gPowerHalV1_1 != nullptr) {
+                ret = gPowerHalV1_1->powerHintAsync(PowerHint::INTERACTION, 0);
+              } else {
+                ret = gPowerHalV1_0->powerHint(PowerHint::INTERACTION, 0);
+              }
+              processReturn(ret, "powerHint");
+            }
+            gPowerHalMutex.unlock();
+
         }
 
         JNIEnv* env = AndroidRuntime::getJNIEnv();
@@ -159,7 +168,7 @@
     std::lock_guard<std::mutex> lock(gPowerHalMutex);
     if (getPowerHal()) {
         android::base::Timer t;
-        Return<void> ret = gPowerHal->setInteractive(enable);
+        Return<void> ret = gPowerHalV1_0->setInteractive(enable);
         processReturn(ret, "setInteractive");
         if (t.duration() > 20ms) {
             ALOGD("Excessive delay in setInteractive(%s) while turning screen %s",
@@ -187,7 +196,12 @@
 static void nativeSendPowerHint(JNIEnv *env, jclass clazz, jint hintId, jint data) {
     std::lock_guard<std::mutex> lock(gPowerHalMutex);
     if (getPowerHal()) {
-        Return<void> ret =  gPowerHal->powerHint((PowerHint)hintId, data);
+        Return<void> ret;
+        if (gPowerHalV1_1 != nullptr) {
+            ret =  gPowerHalV1_1->powerHintAsync((PowerHint)hintId, data);
+        } else {
+            ret =  gPowerHalV1_0->powerHint((PowerHint)hintId, data);
+        }
         processReturn(ret, "powerHint");
     }
 }
@@ -195,7 +209,7 @@
 static void nativeSetFeature(JNIEnv *env, jclass clazz, jint featureId, jint data) {
     std::lock_guard<std::mutex> lock(gPowerHalMutex);
     if (getPowerHal()) {
-        Return<void> ret = gPowerHal->setFeature((Feature)featureId, static_cast<bool>(data));
+        Return<void> ret = gPowerHalV1_0->setFeature((Feature)featureId, static_cast<bool>(data));
         processReturn(ret, "setFeature");
     }
 }
diff --git a/services/core/jni/com_android_server_radio_RadioService.cpp b/services/core/jni/com_android_server_radio_RadioService.cpp
index fa64901..459c0d0 100644
--- a/services/core/jni/com_android_server_radio_RadioService.cpp
+++ b/services/core/jni/com_android_server_radio_RadioService.cpp
@@ -210,7 +210,7 @@
     BandConfig bandConfigHal = convert::BandConfigToHal(env, bandConfig, region);
 
     auto tuner = make_javaref(env, env->NewObject(gjni.Tuner.clazz, gjni.Tuner.cstor,
-            callback, halRev, region, withAudio));
+            callback, halRev, region, withAudio, bandConfigHal.type));
     if (tuner == nullptr) {
         ALOGE("Unable to create new tuner object.");
         return nullptr;
@@ -258,7 +258,7 @@
     auto tunerClass = FindClassOrDie(env, "com/android/server/radio/Tuner");
     gjni.Tuner.clazz = MakeGlobalRefOrDie(env, tunerClass);
     gjni.Tuner.cstor = GetMethodIDOrDie(env, tunerClass, "<init>",
-            "(Landroid/hardware/radio/ITunerCallback;IIZ)V");
+            "(Landroid/hardware/radio/ITunerCallback;IIZI)V");
 
     auto arrayListClass = FindClassOrDie(env, "java/util/ArrayList");
     gjni.ArrayList.clazz = MakeGlobalRefOrDie(env, arrayListClass);
diff --git a/services/core/jni/com_android_server_radio_Tuner.cpp b/services/core/jni/com_android_server_radio_Tuner.cpp
index 4b6f30b..d984a0c 100644
--- a/services/core/jni/com_android_server_radio_Tuner.cpp
+++ b/services/core/jni/com_android_server_radio_Tuner.cpp
@@ -22,12 +22,13 @@
 #include "com_android_server_radio_convert.h"
 #include "com_android_server_radio_TunerCallback.h"
 
+#include <JNIHelp.h>
+#include <Utils.h>
 #include <android/hardware/broadcastradio/1.1/IBroadcastRadioFactory.h>
 #include <binder/IPCThreadState.h>
 #include <core_jni_helpers.h>
 #include <media/AudioSystem.h>
 #include <utils/Log.h>
-#include <JNIHelp.h>
 
 namespace android {
 namespace server {
@@ -41,6 +42,7 @@
 namespace V1_0 = hardware::broadcastradio::V1_0;
 namespace V1_1 = hardware::broadcastradio::V1_1;
 
+using V1_0::Band;
 using V1_0::BandConfig;
 using V1_0::MetaData;
 using V1_0::Result;
@@ -79,6 +81,7 @@
     bool mIsClosed = false;
     HalRevision mHalRev;
     bool mWithAudio;
+    Band mBand;
     sp<V1_0::ITuner> mHalTuner;
     sp<V1_1::ITuner> mHalTuner11;
     sp<HalDeathRecipient> mHalDeathRecipient;
@@ -100,13 +103,14 @@
     return getNativeContext(env->GetLongField(jTuner.get(), gjni.Tuner.nativeContext));
 }
 
-static jlong nativeInit(JNIEnv *env, jobject obj, jint halRev, bool withAudio) {
+static jlong nativeInit(JNIEnv *env, jobject obj, jint halRev, bool withAudio, jint band) {
     ALOGV("nativeInit()");
     AutoMutex _l(gContextMutex);
 
     auto ctx = new TunerContext();
     ctx->mHalRev = static_cast<HalRevision>(halRev);
     ctx->mWithAudio = withAudio;
+    ctx->mBand = static_cast<Band>(band);
 
     static_assert(sizeof(jlong) >= sizeof(ctx), "jlong is smaller than a pointer");
     return reinterpret_cast<jlong>(ctx);
@@ -170,13 +174,17 @@
     notifyAudioService(ctx, true);
 }
 
-sp<V1_0::ITuner> getHalTuner(jlong nativeContext) {
-    AutoMutex _l(gContextMutex);
-    auto tuner = getNativeContext(nativeContext).mHalTuner;
+static sp<V1_0::ITuner> getHalTuner(const TunerContext& ctx) {
+    auto tuner = ctx.mHalTuner;
     LOG_ALWAYS_FATAL_IF(tuner == nullptr, "HAL tuner is not open");
     return tuner;
 }
 
+sp<V1_0::ITuner> getHalTuner(jlong nativeContext) {
+    AutoMutex _l(gContextMutex);
+    return getHalTuner(getNativeContext(nativeContext));
+}
+
 sp<V1_1::ITuner> getHalTuner11(jlong nativeContext) {
     AutoMutex _l(gContextMutex);
     return getNativeContext(nativeContext).mHalTuner11;
@@ -215,13 +223,17 @@
 
 static void nativeSetConfiguration(JNIEnv *env, jobject obj, jlong nativeContext, jobject config) {
     ALOGV("nativeSetConfiguration()");
-    auto halTuner = getHalTuner(nativeContext);
+    AutoMutex _l(gContextMutex);
+    auto& ctx = getNativeContext(nativeContext);
+    auto halTuner = getHalTuner(ctx);
     if (halTuner == nullptr) return;
 
     Region region_unused;
     BandConfig bandConfigHal = convert::BandConfigToHal(env, config, region_unused);
 
-    convert::ThrowIfFailed(env, halTuner->setConfiguration(bandConfigHal));
+    if (convert::ThrowIfFailed(env, halTuner->setConfiguration(bandConfigHal))) return;
+
+    ctx.mBand = bandConfigHal.type;
 }
 
 static jobject nativeGetConfiguration(JNIEnv *env, jobject obj, jlong nativeContext,
@@ -263,13 +275,26 @@
     convert::ThrowIfFailed(env, halTuner->scan(dir, skipSubChannel));
 }
 
-static void nativeTune(JNIEnv *env, jobject obj, jlong nativeContext,
-        jint channel, jint subChannel) {
-    ALOGV("nativeTune(%d, %d)", channel, subChannel);
-    auto halTuner = getHalTuner(nativeContext);
-    if (halTuner == nullptr) return;
+static void nativeTune(JNIEnv *env, jobject obj, jlong nativeContext, jobject jSelector) {
+    ALOGV("nativeTune()");
+    AutoMutex _l(gContextMutex);
+    auto& ctx = getNativeContext(nativeContext);
+    auto halTuner10 = getHalTuner(ctx);
+    auto halTuner11 = ctx.mHalTuner11;
+    if (halTuner10 == nullptr) return;
 
-    convert::ThrowIfFailed(env, halTuner->tune(channel, subChannel));
+    auto selector = convert::ProgramSelectorToHal(env, jSelector);
+    if (halTuner11 != nullptr) {
+        convert::ThrowIfFailed(env, halTuner11->tune_1_1(selector));
+    } else {
+        uint32_t channel, subChannel;
+        if (!V1_1::utils::getLegacyChannel(selector, &channel, &subChannel)) {
+            jniThrowException(env, "java/lang/IllegalArgumentException",
+                    "Can't tune to non-AM/FM channel with HAL<1.1");
+            return;
+        }
+        convert::ThrowIfFailed(env, halTuner10->tune(channel, subChannel));
+    }
 }
 
 static void nativeCancel(JNIEnv *env, jobject obj, jlong nativeContext) {
@@ -280,10 +305,23 @@
     convert::ThrowIfFailed(env, halTuner->cancel());
 }
 
+static void nativeCancelAnnouncement(JNIEnv *env, jobject obj, jlong nativeContext) {
+    ALOGV("%s", __func__);
+    auto halTuner = getHalTuner11(nativeContext);
+    if (halTuner == nullptr) {
+        ALOGI("cancelling announcements is not supported with HAL < 1.1");
+        return;
+    }
+
+    convert::ThrowIfFailed(env, halTuner->cancelAnnouncement());
+}
+
 static jobject nativeGetProgramInformation(JNIEnv *env, jobject obj, jlong nativeContext) {
     ALOGV("nativeGetProgramInformation()");
-    auto halTuner10 = getHalTuner(nativeContext);
-    auto halTuner11 = getHalTuner11(nativeContext);
+    AutoMutex _l(gContextMutex);
+    auto& ctx = getNativeContext(nativeContext);
+    auto halTuner10 = getHalTuner(ctx);
+    auto halTuner11 = ctx.mHalTuner11;
     if (halTuner10 == nullptr) return nullptr;
 
     JavaRef<jobject> jInfo;
@@ -301,7 +339,7 @@
                 const V1_0::ProgramInfo& info) {
             halResult = result;
             if (result != Result::OK) return;
-            jInfo = convert::ProgramInfoFromHal(env, info);
+            jInfo = convert::ProgramInfoFromHal(env, info, ctx.mBand);
         });
     }
 
@@ -402,7 +440,7 @@
 }
 
 static const JNINativeMethod gTunerMethods[] = {
-    { "nativeInit", "(IZ)J", (void*)nativeInit },
+    { "nativeInit", "(IZI)J", (void*)nativeInit },
     { "nativeFinalize", "(J)V", (void*)nativeFinalize },
     { "nativeClose", "(J)V", (void*)nativeClose },
     { "nativeSetConfiguration", "(JLandroid/hardware/radio/RadioManager$BandConfig;)V",
@@ -411,8 +449,9 @@
             (void*)nativeGetConfiguration },
     { "nativeStep", "(JZZ)V", (void*)nativeStep },
     { "nativeScan", "(JZZ)V", (void*)nativeScan },
-    { "nativeTune", "(JII)V", (void*)nativeTune },
+    { "nativeTune", "(JLandroid/hardware/radio/ProgramSelector;)V", (void*)nativeTune },
     { "nativeCancel", "(J)V", (void*)nativeCancel },
+    { "nativeCancelAnnouncement", "(J)V", (void*)nativeCancelAnnouncement },
     { "nativeGetProgramInformation", "(J)Landroid/hardware/radio/RadioManager$ProgramInfo;",
             (void*)nativeGetProgramInformation },
     { "nativeStartBackgroundScan", "(J)Z", (void*)nativeStartBackgroundScan },
diff --git a/services/core/jni/com_android_server_radio_TunerCallback.cpp b/services/core/jni/com_android_server_radio_TunerCallback.cpp
index 8df92ae7..815a212 100644
--- a/services/core/jni/com_android_server_radio_TunerCallback.cpp
+++ b/services/core/jni/com_android_server_radio_TunerCallback.cpp
@@ -22,9 +22,10 @@
 #include "com_android_server_radio_convert.h"
 #include "com_android_server_radio_Tuner.h"
 
+#include <JNIHelp.h>
+#include <Utils.h>
 #include <core_jni_helpers.h>
 #include <utils/Log.h>
-#include <JNIHelp.h>
 
 namespace android {
 namespace server {
@@ -37,11 +38,13 @@
 namespace V1_0 = hardware::broadcastradio::V1_0;
 namespace V1_1 = hardware::broadcastradio::V1_1;
 
+using V1_0::Band;
 using V1_0::BandConfig;
 using V1_0::MetaData;
 using V1_0::Result;
 using V1_1::ITunerCallback;
 using V1_1::ProgramListResult;
+using V1_1::ProgramSelector;
 
 static JavaVM *gvm = nullptr;
 
@@ -53,7 +56,6 @@
         jmethodID onError;
         jmethodID onConfigurationChanged;
         jmethodID onProgramInfoChanged;
-        jmethodID onMetadataChanged;
         jmethodID onTrafficAnnouncement;
         jmethodID onEmergencyAnnouncement;
         jmethodID onAntennaState;
@@ -82,6 +84,8 @@
     NativeCallbackThread mCallbackThread;
     HalRevision mHalRev;
 
+    Band mBand;
+
     DISALLOW_COPY_AND_ASSIGN(NativeCallback);
 
 public:
@@ -99,11 +103,12 @@
     virtual Return<void> emergencyAnnouncement(bool active);
     virtual Return<void> newMetadata(uint32_t channel, uint32_t subChannel,
             const hidl_vec<MetaData>& metadata);
-    virtual Return<void> tuneComplete_1_1(Result result, const V1_1::ProgramInfo& info);
-    virtual Return<void> afSwitch_1_1(const V1_1::ProgramInfo& info);
+    virtual Return<void> tuneComplete_1_1(Result result, const ProgramSelector& selector);
+    virtual Return<void> afSwitch_1_1(const ProgramSelector& selector);
     virtual Return<void> backgroundScanAvailable(bool isAvailable);
     virtual Return<void> backgroundScanComplete(ProgramListResult result);
     virtual Return<void> programListChanged();
+    virtual Return<void> programInfoChanged();
 };
 
 struct TunerCallbackContext {
@@ -171,24 +176,20 @@
     ALOGV("tuneComplete(%d)", result);
 
     if (mHalRev > HalRevision::V1_0) {
-        ALOGD("1.0 callback was ignored");
+        ALOGW("1.0 callback was ignored");
         return Return<void>();
     }
 
-    V1_1::ProgramInfo info_1_1 {
-        .base = info,
-    };
-    return tuneComplete_1_1(result, info_1_1);
+    auto selector = V1_1::utils::make_selector(mBand, info.channel, info.subChannel);
+    return tuneComplete_1_1(result, selector);
 }
 
-Return<void> NativeCallback::tuneComplete_1_1(Result result, const V1_1::ProgramInfo& info) {
+Return<void> NativeCallback::tuneComplete_1_1(Result result, const ProgramSelector& selector) {
     ALOGV("tuneComplete_1_1(%d)", result);
 
-    mCallbackThread.enqueue([result, info, this](JNIEnv *env) {
+    mCallbackThread.enqueue([result, this](JNIEnv *env) {
         if (result == Result::OK) {
-            auto jInfo = convert::ProgramInfoFromHal(env, info);
-            if (jInfo == nullptr) return;
-            env->CallVoidMethod(mJCallback, gjni.TunerCallback.onProgramInfoChanged, jInfo.get());
+            env->CallVoidMethod(mJCallback, gjni.TunerCallback.onProgramInfoChanged);
         } else {
             TunerError cause = TunerError::CANCELLED;
             if (result == Result::TIMEOUT) cause = TunerError::SCAN_TIMEOUT;
@@ -204,9 +205,9 @@
     return tuneComplete(Result::OK, info);
 }
 
-Return<void> NativeCallback::afSwitch_1_1(const V1_1::ProgramInfo& info) {
+Return<void> NativeCallback::afSwitch_1_1(const ProgramSelector& selector) {
     ALOGV("afSwitch_1_1()");
-    return tuneComplete_1_1(Result::OK, info);
+    return tuneComplete_1_1(Result::OK, selector);
 }
 
 Return<void> NativeCallback::antennaStateChange(bool connected) {
@@ -244,10 +245,13 @@
     // channel and subChannel are not used
     ALOGV("newMetadata(%d, %d)", channel, subChannel);
 
+    if (mHalRev > HalRevision::V1_0) {
+        ALOGW("1.0 callback was ignored");
+        return Return<void>();
+    }
+
     mCallbackThread.enqueue([this, metadata](JNIEnv *env) {
-        auto jMetadata = convert::MetadataFromHal(env, metadata);
-        if (jMetadata == nullptr) return;
-        env->CallVoidMethod(mJCallback, gjni.TunerCallback.onMetadataChanged, jMetadata.get());
+        env->CallVoidMethod(mJCallback, gjni.TunerCallback.onProgramInfoChanged);
     });
 
     return Return<void>();
@@ -290,6 +294,16 @@
     return Return<void>();
 }
 
+Return<void> NativeCallback::programInfoChanged() {
+    ALOGV("programInfoChanged()");
+
+    mCallbackThread.enqueue([this](JNIEnv *env) {
+        env->CallVoidMethod(mJCallback, gjni.TunerCallback.onProgramInfoChanged);
+    });
+
+    return Return<void>();
+}
+
 static TunerCallbackContext& getNativeContext(jlong nativeContextHandle) {
     auto nativeContext = reinterpret_cast<TunerCallbackContext*>(nativeContextHandle);
     LOG_ALWAYS_FATAL_IF(nativeContext == nullptr, "Native context not initialized");
@@ -363,9 +377,7 @@
     gjni.TunerCallback.onConfigurationChanged = GetMethodIDOrDie(env, tunerCbClass,
             "onConfigurationChanged", "(Landroid/hardware/radio/RadioManager$BandConfig;)V");
     gjni.TunerCallback.onProgramInfoChanged = GetMethodIDOrDie(env, tunerCbClass,
-            "onProgramInfoChanged", "(Landroid/hardware/radio/RadioManager$ProgramInfo;)V");
-    gjni.TunerCallback.onMetadataChanged = GetMethodIDOrDie(env, tunerCbClass,
-            "onMetadataChanged", "(Landroid/hardware/radio/RadioMetadata;)V");
+            "onProgramInfoChanged", "()V");
     gjni.TunerCallback.onTrafficAnnouncement = GetMethodIDOrDie(env, tunerCbClass,
             "onTrafficAnnouncement", "(Z)V");
     gjni.TunerCallback.onEmergencyAnnouncement = GetMethodIDOrDie(env, tunerCbClass,
diff --git a/services/core/jni/com_android_server_radio_convert.cpp b/services/core/jni/com_android_server_radio_convert.cpp
index 55fb10f..6cad7f8 100644
--- a/services/core/jni/com_android_server_radio_convert.cpp
+++ b/services/core/jni/com_android_server_radio_convert.cpp
@@ -19,9 +19,10 @@
 
 #include "com_android_server_radio_convert.h"
 
+#include <JNIHelp.h>
+#include <Utils.h>
 #include <core_jni_helpers.h>
 #include <utils/Log.h>
-#include <JNIHelp.h>
 
 namespace android {
 namespace server {
@@ -37,7 +38,9 @@
 using V1_0::MetadataType;
 using V1_0::Result;
 using V1_0::Rds;
+using V1_1::ProgramIdentifier;
 using V1_1::ProgramListResult;
+using V1_1::ProgramSelector;
 
 static JavaRef<jobject> BandDescriptorFromHal(JNIEnv *env, const V1_0::BandConfig &config, Region region);
 
@@ -90,6 +93,22 @@
     struct {
         jclass clazz;
         jmethodID cstor;
+        jfieldID programType;
+        jfieldID primaryId;
+        jfieldID secondaryIds;
+        jfieldID vendorIds;
+
+        struct {
+            jclass clazz;
+            jmethodID cstor;
+            jfieldID type;
+            jfieldID value;
+        } Identifier;
+    } ProgramSelector;
+
+    struct {
+        jclass clazz;
+        jmethodID cstor;
         jmethodID putIntFromNative;
         jmethodID putStringFromNative;
         jmethodID putBitmapFromNative;
@@ -166,6 +185,44 @@
     ALOGE_IF(res != JNI_OK, "Couldn't throw parcelable runtime exception");
 }
 
+static JavaRef<jintArray> ArrayFromHal(JNIEnv *env, const hidl_vec<uint32_t>& vec) {
+    auto jArr = make_javaref(env, env->NewIntArray(vec.size()));
+    auto jArrElements = env->GetIntArrayElements(jArr.get(), nullptr);
+    for (size_t i = 0; i < vec.size(); i++) {
+        jArrElements[i] = vec[i];
+    }
+    env->ReleaseIntArrayElements(jArr.get(), jArrElements, 0);
+    return jArr;
+}
+
+static JavaRef<jlongArray> ArrayFromHal(JNIEnv *env, const hidl_vec<uint64_t>& vec) {
+    auto jArr = make_javaref(env, env->NewLongArray(vec.size()));
+    auto jArrElements = env->GetLongArrayElements(jArr.get(), nullptr);
+    for (size_t i = 0; i < vec.size(); i++) {
+        jArrElements[i] = vec[i];
+    }
+    env->ReleaseLongArrayElements(jArr.get(), jArrElements, 0);
+    return jArr;
+}
+
+template <typename T>
+static JavaRef<jobjectArray> ArrayFromHal(JNIEnv *env, const hidl_vec<T>& vec,
+        jclass jElementClass, std::function<JavaRef<jobject>(JNIEnv*, const T&)> converter) {
+    auto jArr = make_javaref(env, env->NewObjectArray(vec.size(), jElementClass, nullptr));
+    for (size_t i = 0; i < vec.size(); i++) {
+        auto jElement = converter(env, vec[i]);
+        env->SetObjectArrayElement(jArr.get(), i, jElement.get());
+    }
+    return jArr;
+}
+
+template <typename T>
+static JavaRef<jobjectArray> ArrayFromHal(JNIEnv *env, const hidl_vec<T>& vec,
+        jclass jElementClass, JavaRef<jobject>(*converter)(JNIEnv*, const T&)) {
+    return ArrayFromHal(env, vec, jElementClass,
+            std::function<JavaRef<jobject>(JNIEnv*, const T&)>(converter));
+}
+
 static Rds RdsForRegion(bool rds, Region region) {
     if (!rds) return Rds::NONE;
 
@@ -201,6 +258,7 @@
 static JavaRef<jobject> ModulePropertiesFromHal(JNIEnv *env, const V1_0::Properties &prop10,
         const V1_1::Properties *prop11, jint moduleId, const std::string& serviceName) {
     ALOGV("ModulePropertiesFromHal()");
+    using namespace std::placeholders;
 
     auto jServiceName = make_javastr(env, serviceName);
     auto jImplementor = make_javastr(env, prop10.implementor);
@@ -209,21 +267,19 @@
     auto jSerial = make_javastr(env, prop10.serial);
     bool isBgScanSupported = prop11 ? prop11->supportsBackgroundScanning : false;
     auto jVendorExtension = prop11 ? make_javastr(env, prop11->vendorExension) : nullptr;
-
-    auto jBands = make_javaref(env, env->NewObjectArray(prop10.bands.size(),
-            gjni.BandDescriptor.clazz, nullptr));
-    int i = 0;
-    for (auto &&band : prop10.bands) {
-        // ITU_1 is the default region just because its index is 0.
-        auto jBand = BandDescriptorFromHal(env, band, Region::ITU_1);
-        env->SetObjectArrayElement(jBands.get(), i++, jBand.get());
-    }
+    // ITU_1 is the default region just because its index is 0.
+    auto jBands = ArrayFromHal<V1_0::BandConfig>(env, prop10.bands, gjni.BandDescriptor.clazz,
+        std::bind(BandDescriptorFromHal, _1, _2, Region::ITU_1));
+    auto jSupportedProgramTypes =
+            prop11 ? ArrayFromHal(env, prop11->supportedProgramTypes) : nullptr;
+    auto jSupportedIdentifierTypes =
+            prop11 ? ArrayFromHal(env, prop11->supportedIdentifierTypes) : nullptr;
 
     return make_javaref(env, env->NewObject(gjni.ModuleProperties.clazz,
             gjni.ModuleProperties.cstor, moduleId, jServiceName.get(), prop10.classId,
             jImplementor.get(), jProduct.get(), jVersion.get(), jSerial.get(), prop10.numTuners,
             prop10.numAudioSources, prop10.supportsCapture, jBands.get(), isBgScanSupported,
-            jVendorExtension.get()));
+            jSupportedProgramTypes.get(), jSupportedIdentifierTypes.get(), jVendorExtension.get()));
 }
 
 JavaRef<jobject> ModulePropertiesFromHal(JNIEnv *env, const V1_0::Properties &properties,
@@ -383,25 +439,89 @@
     return jMetadata;
 }
 
+static JavaRef<jobject> ProgramIdentifierFromHal(JNIEnv *env, const ProgramIdentifier &id) {
+    ALOGV("ProgramIdentifierFromHal()");
+    return make_javaref(env, env->NewObject(gjni.ProgramSelector.Identifier.clazz,
+            gjni.ProgramSelector.Identifier.cstor, id.type, id.value));
+}
+
+static JavaRef<jobject> ProgramSelectorFromHal(JNIEnv *env, const ProgramSelector &selector) {
+    ALOGV("ProgramSelectorFromHal()");
+    auto jPrimary = ProgramIdentifierFromHal(env, selector.primaryId);
+    auto jSecondary = ArrayFromHal(env, selector.secondaryIds,
+            gjni.ProgramSelector.Identifier.clazz, ProgramIdentifierFromHal);
+    auto jVendor = ArrayFromHal(env, selector.vendorIds);
+
+    return make_javaref(env, env->NewObject(gjni.ProgramSelector.clazz, gjni.ProgramSelector.cstor,
+            selector.programType, jPrimary.get(), jSecondary.get(), jVendor.get()));
+}
+
+static ProgramIdentifier ProgramIdentifierToHal(JNIEnv *env, jobject jId) {
+    ALOGV("ProgramIdentifierToHal()");
+
+    ProgramIdentifier id = {};
+    id.type = env->GetIntField(jId, gjni.ProgramSelector.Identifier.type);
+    id.value = env->GetLongField(jId, gjni.ProgramSelector.Identifier.value);
+    return id;
+}
+
+ProgramSelector ProgramSelectorToHal(JNIEnv *env, jobject jSelector) {
+    ALOGV("ProgramSelectorToHal()");
+
+    ProgramSelector selector = {};
+
+    selector.programType = env->GetIntField(jSelector, gjni.ProgramSelector.programType);
+
+    auto jPrimary = env->GetObjectField(jSelector, gjni.ProgramSelector.primaryId);
+    auto jSecondary = reinterpret_cast<jobjectArray>(
+            env->GetObjectField(jSelector, gjni.ProgramSelector.secondaryIds));
+    auto jVendor = reinterpret_cast<jlongArray>(
+            env->GetObjectField(jSelector, gjni.ProgramSelector.vendorIds));
+
+    if (jPrimary == nullptr || jSecondary == nullptr || jVendor == nullptr) {
+        ALOGE("ProgramSelector object is incomplete");
+        return {};
+    }
+
+    selector.primaryId = ProgramIdentifierToHal(env, jPrimary);
+    auto count = env->GetArrayLength(jSecondary);
+    selector.secondaryIds.resize(count);
+    for (jsize i = 0; i < count; i++) {
+        auto jId = env->GetObjectArrayElement(jSecondary, i);
+        selector.secondaryIds[i] = ProgramIdentifierToHal(env, jId);
+    }
+
+    count = env->GetArrayLength(jVendor);
+    selector.vendorIds.resize(count);
+    auto jVendorElements = env->GetLongArrayElements(jVendor, nullptr);
+    for (jint i = 0; i < count; i++) {
+        selector.vendorIds[i] = jVendorElements[i];
+    }
+    env->ReleaseLongArrayElements(jVendor, jVendorElements, 0);
+
+    return selector;
+}
+
 static JavaRef<jobject> ProgramInfoFromHal(JNIEnv *env, const V1_0::ProgramInfo &info10,
-        const V1_1::ProgramInfo *info11) {
+        const V1_1::ProgramInfo *info11, const ProgramSelector &selector) {
     ALOGV("ProgramInfoFromHal()");
 
     auto jMetadata = MetadataFromHal(env, info10.metadata);
     auto jVendorExtension = info11 ? make_javastr(env, info11->vendorExension) : nullptr;
+    auto jSelector = ProgramSelectorFromHal(env, selector);
 
     return make_javaref(env, env->NewObject(gjni.ProgramInfo.clazz, gjni.ProgramInfo.cstor,
-            info10.channel, info10.subChannel, info10.tuned, info10.stereo, info10.digital,
-            info10.signalStrength, jMetadata.get(), info11 ? info11->flags : 0,
-            jVendorExtension.get()));
+            jSelector.get(), info10.tuned, info10.stereo, info10.digital, info10.signalStrength,
+            jMetadata.get(), info11 ? info11->flags : 0, jVendorExtension.get()));
 }
 
-JavaRef<jobject> ProgramInfoFromHal(JNIEnv *env, const V1_0::ProgramInfo &info) {
-    return ProgramInfoFromHal(env, info, nullptr);
+JavaRef<jobject> ProgramInfoFromHal(JNIEnv *env, const V1_0::ProgramInfo &info, V1_0::Band band) {
+    auto selector = V1_1::utils::make_selector(band, info.channel, info.subChannel);
+    return ProgramInfoFromHal(env, info, nullptr, selector);
 }
 
 JavaRef<jobject> ProgramInfoFromHal(JNIEnv *env, const V1_1::ProgramInfo &info) {
-    return ProgramInfoFromHal(env, info.base, &info);
+    return ProgramInfoFromHal(env, info.base, &info, info.selector);
 }
 
 } // namespace convert
@@ -460,12 +580,36 @@
     gjni.ModuleProperties.cstor = GetMethodIDOrDie(env, modulePropertiesClass, "<init>",
             "(ILjava/lang/String;ILjava/lang/String;Ljava/lang/String;Ljava/lang/String;"
             "Ljava/lang/String;IIZ[Landroid/hardware/radio/RadioManager$BandDescriptor;Z"
-            "Ljava/lang/String;)V");
+            "[I[ILjava/lang/String;)V");
 
     auto programInfoClass = FindClassOrDie(env, "android/hardware/radio/RadioManager$ProgramInfo");
     gjni.ProgramInfo.clazz = MakeGlobalRefOrDie(env, programInfoClass);
     gjni.ProgramInfo.cstor = GetMethodIDOrDie(env, programInfoClass, "<init>",
-            "(IIZZZILandroid/hardware/radio/RadioMetadata;ILjava/lang/String;)V");
+            "(Landroid/hardware/radio/ProgramSelector;ZZZILandroid/hardware/radio/RadioMetadata;I"
+            "Ljava/lang/String;)V");
+
+    auto programSelectorClass = FindClassOrDie(env, "android/hardware/radio/ProgramSelector");
+    gjni.ProgramSelector.clazz = MakeGlobalRefOrDie(env, programSelectorClass);
+    gjni.ProgramSelector.cstor = GetMethodIDOrDie(env, programSelectorClass, "<init>",
+            "(ILandroid/hardware/radio/ProgramSelector$Identifier;"
+            "[Landroid/hardware/radio/ProgramSelector$Identifier;[J)V");
+    gjni.ProgramSelector.programType = GetFieldIDOrDie(env, programSelectorClass,
+            "mProgramType", "I");
+    gjni.ProgramSelector.primaryId = GetFieldIDOrDie(env, programSelectorClass,
+            "mPrimaryId", "Landroid/hardware/radio/ProgramSelector$Identifier;");
+    gjni.ProgramSelector.secondaryIds = GetFieldIDOrDie(env, programSelectorClass,
+            "mSecondaryIds", "[Landroid/hardware/radio/ProgramSelector$Identifier;");
+    gjni.ProgramSelector.vendorIds = GetFieldIDOrDie(env, programSelectorClass,
+            "mVendorIds", "[J");
+
+    auto progSelIdClass = FindClassOrDie(env, "android/hardware/radio/ProgramSelector$Identifier");
+    gjni.ProgramSelector.Identifier.clazz = MakeGlobalRefOrDie(env, progSelIdClass);
+    gjni.ProgramSelector.Identifier.cstor = GetMethodIDOrDie(env, progSelIdClass,
+            "<init>", "(IJ)V");
+    gjni.ProgramSelector.Identifier.type = GetFieldIDOrDie(env, progSelIdClass,
+            "mType", "I");
+    gjni.ProgramSelector.Identifier.value = GetFieldIDOrDie(env, progSelIdClass,
+            "mValue", "J");
 
     auto radioMetadataClass = FindClassOrDie(env, "android/hardware/radio/RadioMetadata");
     gjni.RadioMetadata.clazz = MakeGlobalRefOrDie(env, radioMetadataClass);
diff --git a/services/core/jni/com_android_server_radio_convert.h b/services/core/jni/com_android_server_radio_convert.h
index 3ba6fed..8b91ced 100644
--- a/services/core/jni/com_android_server_radio_convert.h
+++ b/services/core/jni/com_android_server_radio_convert.h
@@ -45,9 +45,10 @@
 V1_0::Direction DirectionToHal(bool directionDown);
 
 JavaRef<jobject> MetadataFromHal(JNIEnv *env, const hardware::hidl_vec<V1_0::MetaData> &metadata);
-JavaRef<jobject> ProgramInfoFromHal(JNIEnv *env, const V1_0::ProgramInfo &info);
+JavaRef<jobject> ProgramInfoFromHal(JNIEnv *env, const V1_0::ProgramInfo &info, V1_0::Band band);
 JavaRef<jobject> ProgramInfoFromHal(JNIEnv *env, const V1_1::ProgramInfo &info);
 
+V1_1::ProgramSelector ProgramSelectorToHal(JNIEnv *env, jobject jSelector);
 
 void ThrowParcelableRuntimeException(JNIEnv *env, const std::string& msg);
 
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index d5e0eb0..d10cc02 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -763,13 +763,6 @@
 
             mContentResolver = context.getContentResolver();
 
-            if (!disableCameraService) {
-                Slog.i(TAG, "Camera Service Proxy");
-                traceBeginAndSlog("StartCameraServiceProxy");
-                mSystemServiceManager.startService(CameraServiceProxy.class);
-                traceEnd();
-            }
-
             // The AccountManager must come before the ContentService
             traceBeginAndSlog("StartAccountManagerService");
             mSystemServiceManager.startService(ACCOUNT_SERVICE_CLASS);
@@ -1213,8 +1206,8 @@
             mSystemServiceManager.startService(AudioService.Lifecycle.class);
             traceEnd();
 
-            if (mPackageManager.hasSystemFeature(PackageManager.FEATURE_RADIO)) {
-                traceBeginAndSlog("StartRadioService");
+            if (mPackageManager.hasSystemFeature(PackageManager.FEATURE_BROADCAST_RADIO)) {
+                traceBeginAndSlog("StartBroadcastRadioService");
                 mSystemServiceManager.startService(RadioService.class);
                 traceEnd();
             }
@@ -1516,6 +1509,12 @@
             }
         }
 
+        if (!disableCameraService) {
+            traceBeginAndSlog("StartCameraServiceProxy");
+            mSystemServiceManager.startService(CameraServiceProxy.class);
+            traceEnd();
+        }
+
         // Before things start rolling, be sure we have decided whether
         // we are in safe mode.
         final boolean safeMode = wm.detectSafeMode();
diff --git a/services/net/java/android/net/ip/IpManager.java b/services/net/java/android/net/ip/IpManager.java
index 41fccdb..5aaec9e 100644
--- a/services/net/java/android/net/ip/IpManager.java
+++ b/services/net/java/android/net/ip/IpManager.java
@@ -23,6 +23,7 @@
 import android.net.DhcpResults;
 import android.net.INetd;
 import android.net.InterfaceConfiguration;
+import android.net.IpPrefix;
 import android.net.LinkAddress;
 import android.net.LinkProperties.ProvisioningChange;
 import android.net.LinkProperties;
@@ -35,6 +36,7 @@
 import android.net.metrics.IpConnectivityLog;
 import android.net.metrics.IpManagerEvent;
 import android.net.util.MultinetworkPolicyTracker;
+import android.net.util.NetworkConstants;
 import android.net.util.SharedLog;
 import android.os.INetworkManagementService;
 import android.os.Message;
@@ -51,17 +53,25 @@
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.IndentingPrintWriter;
 import com.android.internal.util.IState;
+import com.android.internal.util.Preconditions;
 import com.android.internal.util.State;
 import com.android.internal.util.StateMachine;
 import com.android.server.net.NetlinkTracker;
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
+import java.net.Inet4Address;
+import java.net.Inet6Address;
 import java.net.InetAddress;
 import java.net.NetworkInterface;
 import java.net.SocketException;
+import java.util.Collection;
+import java.util.HashSet;
 import java.util.Objects;
+import java.util.Set;
 import java.util.StringJoiner;
+import java.util.function.Predicate;
+import java.util.stream.Collectors;
 
 
 /**
@@ -308,6 +318,11 @@
                 return this;
             }
 
+            public Builder withInitialConfiguration(InitialConfiguration initialConfig) {
+                mConfig.mInitialConfig = initialConfig;
+                return this;
+            }
+
             public Builder withStaticConfiguration(StaticIpConfiguration staticConfig) {
                 mConfig.mStaticIpConfig = staticConfig;
                 return this;
@@ -342,18 +357,20 @@
         /* package */ boolean mEnableIPv6 = true;
         /* package */ boolean mUsingIpReachabilityMonitor = true;
         /* package */ int mRequestedPreDhcpActionMs;
+        /* package */ InitialConfiguration mInitialConfig;
         /* package */ StaticIpConfiguration mStaticIpConfig;
         /* package */ ApfCapabilities mApfCapabilities;
         /* package */ int mProvisioningTimeoutMs = DEFAULT_TIMEOUT_MS;
         /* package */ int mIPv6AddrGenMode = INetd.IPV6_ADDR_GEN_MODE_STABLE_PRIVACY;
 
-        public ProvisioningConfiguration() {}
+        public ProvisioningConfiguration() {} // used by Builder
 
         public ProvisioningConfiguration(ProvisioningConfiguration other) {
             mEnableIPv4 = other.mEnableIPv4;
             mEnableIPv6 = other.mEnableIPv6;
             mUsingIpReachabilityMonitor = other.mUsingIpReachabilityMonitor;
             mRequestedPreDhcpActionMs = other.mRequestedPreDhcpActionMs;
+            mInitialConfig = InitialConfiguration.copy(other.mInitialConfig);
             mStaticIpConfig = other.mStaticIpConfig;
             mApfCapabilities = other.mApfCapabilities;
             mProvisioningTimeoutMs = other.mProvisioningTimeoutMs;
@@ -366,12 +383,124 @@
                     .add("mEnableIPv6: " + mEnableIPv6)
                     .add("mUsingIpReachabilityMonitor: " + mUsingIpReachabilityMonitor)
                     .add("mRequestedPreDhcpActionMs: " + mRequestedPreDhcpActionMs)
+                    .add("mInitialConfig: " + mInitialConfig)
                     .add("mStaticIpConfig: " + mStaticIpConfig)
                     .add("mApfCapabilities: " + mApfCapabilities)
                     .add("mProvisioningTimeoutMs: " + mProvisioningTimeoutMs)
                     .add("mIPv6AddrGenMode: " + mIPv6AddrGenMode)
                     .toString();
         }
+
+        public boolean isValid() {
+            return (mInitialConfig == null) || mInitialConfig.isValid();
+        }
+    }
+
+    public static class InitialConfiguration {
+        public final Set<LinkAddress> ipAddresses = new HashSet<>();
+        public final Set<IpPrefix> directlyConnectedRoutes = new HashSet<>();
+        public final Set<InetAddress> dnsServers = new HashSet<>();
+        public Inet4Address gateway; // WiFi legacy behavior with static ipv4 config
+
+        public static InitialConfiguration copy(InitialConfiguration config) {
+            if (config == null) {
+                return null;
+            }
+            InitialConfiguration configCopy = new InitialConfiguration();
+            configCopy.ipAddresses.addAll(config.ipAddresses);
+            configCopy.directlyConnectedRoutes.addAll(config.directlyConnectedRoutes);
+            configCopy.dnsServers.addAll(config.dnsServers);
+            return configCopy;
+        }
+
+        @Override
+        public String toString() {
+            return String.format(
+                    "InitialConfiguration(IPs: {%s}, prefixes: {%s}, DNS: {%s}, v4 gateway: %s)",
+                    join(", ", ipAddresses), join(", ", directlyConnectedRoutes),
+                    join(", ", dnsServers), gateway);
+        }
+
+        public boolean isValid() {
+            // For every IP address, there must be at least one prefix containing that address.
+            for (LinkAddress addr : ipAddresses) {
+                if (!any(directlyConnectedRoutes, (p) -> p.contains(addr.getAddress()))) {
+                    return false;
+                }
+            }
+            // For every dns server, there must be at least one prefix containing that address.
+            for (InetAddress addr : dnsServers) {
+                if (!any(directlyConnectedRoutes, (p) -> p.contains(addr))) {
+                    return false;
+                }
+            }
+            // All IPv6 LinkAddresses have an RFC7421-suitable prefix length
+            // (read: compliant with RFC4291#section2.5.4).
+            if (any(ipAddresses, not(InitialConfiguration::isPrefixLengthCompliant))) {
+                return false;
+            }
+            // If directlyConnectedRoutes contains an IPv6 default route
+            // then ipAddresses MUST contain at least one non-ULA GUA.
+            if (any(directlyConnectedRoutes, InitialConfiguration::isIPv6DefaultRoute)
+                    && all(ipAddresses, not(InitialConfiguration::isIPv6GUA))) {
+                return false;
+            }
+            // The prefix length of routes in directlyConnectedRoutes be within reasonable
+            // bounds for IPv6: /48-/64 just as we’d accept in RIOs.
+            if (any(directlyConnectedRoutes, not(InitialConfiguration::isPrefixLengthCompliant))) {
+                return false;
+            }
+            // There no more than one IPv4 address
+            if (ipAddresses.stream().filter(Inet4Address.class::isInstance).count() > 1) {
+                return false;
+            }
+
+            return true;
+        }
+
+        private static boolean isPrefixLengthCompliant(LinkAddress addr) {
+            return (addr.getAddress() instanceof Inet4Address)
+                    || isCompliantIPv6PrefixLength(addr.getPrefixLength());
+        }
+
+        private static boolean isPrefixLengthCompliant(IpPrefix prefix) {
+            return (prefix.getAddress() instanceof Inet4Address)
+                    || isCompliantIPv6PrefixLength(prefix.getPrefixLength());
+        }
+
+        private static boolean isCompliantIPv6PrefixLength(int prefixLength) {
+            return (NetworkConstants.RFC6177_MIN_PREFIX_LENGTH <= prefixLength)
+                    && (prefixLength <= NetworkConstants.RFC7421_PREFIX_LENGTH);
+        }
+
+        private static boolean isIPv6DefaultRoute(IpPrefix prefix) {
+            return prefix.getAddress().equals(Inet6Address.ANY);
+        }
+
+        private static boolean isIPv6GUA(LinkAddress addr) {
+            return (addr.getAddress() instanceof Inet6Address) && addr.isGlobalPreferred();
+        }
+
+        private static <T> boolean any(Iterable<T> coll, Predicate<T> fn) {
+            for (T t : coll) {
+                if (fn.test(t)) {
+                    return true;
+                }
+            }
+            return false;
+        }
+
+        private static <T> boolean all(Iterable<T> coll, Predicate<T> fn) {
+            return !any(coll, not(fn));
+        }
+
+        private static <T> Predicate<T> not(Predicate<T> fn) {
+            return (t) -> !fn.test(t);
+        }
+
+        private static <T> String join(String delimiter, Collection<T> coll) {
+            return coll.stream().map(Object::toString).collect(Collectors.joining(delimiter));
+        }
     }
 
     public static final String DUMP_ARG = "ipmanager";
@@ -436,8 +565,7 @@
     private boolean mMulticastFiltering;
     private long mStartTimeMillis;
 
-    public IpManager(Context context, String ifName, Callback callback)
-                throws IllegalArgumentException {
+    public IpManager(Context context, String ifName, Callback callback) {
         this(context, ifName, callback, INetworkManagementService.Stub.asInterface(
                 ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE)));
     }
@@ -446,7 +574,7 @@
      * An expanded constructor, useful for dependency injection.
      */
     public IpManager(Context context, String ifName, Callback callback,
-            INetworkManagementService nwService) throws IllegalArgumentException {
+            INetworkManagementService nwService) {
         super(IpManager.class.getSimpleName() + "." + ifName);
         mTag = getName();
 
@@ -563,6 +691,11 @@
     }
 
     public void startProvisioning(ProvisioningConfiguration req) {
+        if (!req.isValid()) {
+            doImmediateProvisioningFailure(IpManagerEvent.ERROR_INVALID_PROVISIONING);
+            return;
+        }
+
         getNetworkInterface();
 
         mCallback.setNeighborDiscoveryOffload(true);
diff --git a/services/net/java/android/net/util/NetworkConstants.java b/services/net/java/android/net/util/NetworkConstants.java
index a012e0c..9b3bc3f 100644
--- a/services/net/java/android/net/util/NetworkConstants.java
+++ b/services/net/java/android/net/util/NetworkConstants.java
@@ -102,6 +102,7 @@
     public static final int IPV6_ADDR_LEN = 16;
     public static final int IPV6_MIN_MTU = 1280;
     public static final int RFC7421_PREFIX_LENGTH = 64;
+    public static final int RFC6177_MIN_PREFIX_LENGTH = 48;
 
     /**
      * ICMPv6 constants.
diff --git a/services/net/java/android/net/util/PrefixUtils.java b/services/net/java/android/net/util/PrefixUtils.java
new file mode 100644
index 0000000..962aab4
--- /dev/null
+++ b/services/net/java/android/net/util/PrefixUtils.java
@@ -0,0 +1,74 @@
+/*
+ * 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.net.util;
+
+import android.net.IpPrefix;
+import android.net.LinkAddress;
+import android.net.LinkProperties;
+
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Set;
+
+
+/**
+ * @hide
+ */
+public class PrefixUtils {
+    private static final IpPrefix[] MIN_NON_FORWARDABLE_PREFIXES = {
+            pfx("127.0.0.0/8"),     // IPv4 loopback
+            pfx("169.254.0.0/16"),  // IPv4 link-local, RFC3927#section-8
+            pfx("::/3"),
+            pfx("fe80::/64"),       // IPv6 link-local
+            pfx("fc00::/7"),        // IPv6 ULA
+            pfx("ff02::/8"),        // IPv6 link-local multicast
+    };
+
+    public static final IpPrefix DEFAULT_WIFI_P2P_PREFIX = pfx("192.168.49.0/24");
+
+    public static Set<IpPrefix> getNonForwardablePrefixes() {
+        final HashSet<IpPrefix> prefixes = new HashSet<>();
+        addNonForwardablePrefixes(prefixes);
+        return prefixes;
+    }
+
+    public static void addNonForwardablePrefixes(Set<IpPrefix> prefixes) {
+        Collections.addAll(prefixes, MIN_NON_FORWARDABLE_PREFIXES);
+    }
+
+    public static Set<IpPrefix> localPrefixesFrom(LinkProperties lp) {
+        final HashSet<IpPrefix> localPrefixes = new HashSet<>();
+        if (lp == null) return localPrefixes;
+
+        for (LinkAddress addr : lp.getAllLinkAddresses()) {
+            if (addr.getAddress().isLinkLocalAddress()) continue;
+            localPrefixes.add(asIpPrefix(addr));
+        }
+        // TODO: Add directly-connected routes as well (ones from which we did
+        // not also form a LinkAddress)?
+
+        return localPrefixes;
+    }
+
+    public static IpPrefix asIpPrefix(LinkAddress addr) {
+        return new IpPrefix(addr.getAddress(), addr.getPrefixLength());
+    }
+
+    private static IpPrefix pfx(String prefixStr) {
+        return new IpPrefix(prefixStr);
+    }
+}
diff --git a/services/print/java/com/android/server/print/PrintManagerService.java b/services/print/java/com/android/server/print/PrintManagerService.java
index 3ec8380..6c417a9 100644
--- a/services/print/java/com/android/server/print/PrintManagerService.java
+++ b/services/print/java/com/android/server/print/PrintManagerService.java
@@ -263,6 +263,8 @@
             Preconditions.checkFlagsArgument(selectionFlags,
                     PrintManager.DISABLED_SERVICES | PrintManager.ENABLED_SERVICES);
 
+            mContext.enforceCallingOrSelfPermission(
+                    android.Manifest.permission.READ_PRINT_SERVICES, null);
             final int resolvedUserId = resolveCallingUserEnforcingPermissions(userId);
             final UserState userState;
             synchronized (mLock) {
@@ -316,6 +318,8 @@
 
         @Override
         public List<RecommendationInfo> getPrintServiceRecommendations(int userId) {
+            mContext.enforceCallingOrSelfPermission(
+                    android.Manifest.permission.READ_PRINT_SERVICE_RECOMMENDATIONS, null);
             final int resolvedUserId = resolveCallingUserEnforcingPermissions(userId);
             final UserState userState;
             synchronized (mLock) {
@@ -538,6 +542,8 @@
                 int userId) throws RemoteException {
             listener = Preconditions.checkNotNull(listener);
 
+            mContext.enforceCallingOrSelfPermission(android.Manifest.permission.READ_PRINT_SERVICES,
+                    null);
             final int resolvedUserId = resolveCallingUserEnforcingPermissions(userId);
             final UserState userState;
             synchronized (mLock) {
@@ -560,6 +566,8 @@
                 int userId) {
             listener = Preconditions.checkNotNull(listener);
 
+            mContext.enforceCallingOrSelfPermission(android.Manifest.permission.READ_PRINT_SERVICES,
+                    null);
             final int resolvedUserId = resolveCallingUserEnforcingPermissions(userId);
             final UserState userState;
             synchronized (mLock) {
@@ -583,6 +591,8 @@
                 throws RemoteException {
             listener = Preconditions.checkNotNull(listener);
 
+            mContext.enforceCallingOrSelfPermission(
+                    android.Manifest.permission.READ_PRINT_SERVICE_RECOMMENDATIONS, null);
             final int resolvedUserId = resolveCallingUserEnforcingPermissions(userId);
             final UserState userState;
             synchronized (mLock) {
@@ -605,6 +615,8 @@
                 IRecommendationsChangeListener listener, int userId) {
             listener = Preconditions.checkNotNull(listener);
 
+            mContext.enforceCallingOrSelfPermission(
+                    android.Manifest.permission.READ_PRINT_SERVICE_RECOMMENDATIONS, null);
             final int resolvedUserId = resolveCallingUserEnforcingPermissions(userId);
             final UserState userState;
             synchronized (mLock) {
@@ -888,12 +900,12 @@
 
         private int resolveCallingAppEnforcingPermissions(int appId) {
             final int callingUid = Binder.getCallingUid();
-            if (callingUid == 0 || callingUid == Process.SYSTEM_UID
-                    || callingUid == Process.SHELL_UID) {
+            if (callingUid == 0) {
                 return appId;
             }
             final int callingAppId = UserHandle.getAppId(callingUid);
-            if (appId == callingAppId) {
+            if (appId == callingAppId || callingAppId == Process.SHELL_UID
+                    || callingAppId == Process.SYSTEM_UID) {
                 return appId;
             }
             if (mContext.checkCallingPermission(
diff --git a/services/tests/notification/src/com/android/server/notification/BuzzBeepBlinkTest.java b/services/tests/notification/src/com/android/server/notification/BuzzBeepBlinkTest.java
index 807703b..f7d2a02 100644
--- a/services/tests/notification/src/com/android/server/notification/BuzzBeepBlinkTest.java
+++ b/services/tests/notification/src/com/android/server/notification/BuzzBeepBlinkTest.java
@@ -76,7 +76,8 @@
     @Mock Vibrator mVibrator;
     @Mock android.media.IRingtonePlayer mRingtonePlayer;
     @Mock Light mLight;
-    @Mock Handler mHandler;
+    @Mock
+    NotificationManagerService.WorkerHandler mHandler;
     @Mock
     NotificationUsageStats mUsageStats;
 
diff --git a/services/tests/notification/src/com/android/server/notification/NotificationAdjustmentExtractorTest.java b/services/tests/notification/src/com/android/server/notification/NotificationAdjustmentExtractorTest.java
new file mode 100644
index 0000000..e527644
--- /dev/null
+++ b/services/tests/notification/src/com/android/server/notification/NotificationAdjustmentExtractorTest.java
@@ -0,0 +1,115 @@
+/*
+ * 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.app.NotificationManager.IMPORTANCE_LOW;
+
+import static junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.assertFalse;
+import static junit.framework.Assert.assertNull;
+import static junit.framework.Assert.assertTrue;
+
+import android.app.Notification;
+import android.app.NotificationChannel;
+import android.os.Bundle;
+import android.os.UserHandle;
+import android.service.notification.Adjustment;
+import android.service.notification.SnoozeCriterion;
+import android.service.notification.StatusBarNotification;
+
+import org.junit.Test;
+
+import java.util.ArrayList;
+import java.util.Objects;
+
+public class NotificationAdjustmentExtractorTest extends NotificationTestCase {
+
+    @Test
+    public void testExtractsAdjustment() {
+        NotificationAdjustmentExtractor extractor = new NotificationAdjustmentExtractor();
+
+        NotificationRecord r = generateRecord();
+
+        Bundle signals = new Bundle();
+        signals.putString(Adjustment.KEY_GROUP_KEY, GroupHelper.AUTOGROUP_KEY);
+        ArrayList<SnoozeCriterion> snoozeCriteria = new ArrayList<>();
+        snoozeCriteria.add(new SnoozeCriterion("n", "n", "n"));
+        signals.putParcelableArrayList(Adjustment.KEY_SNOOZE_CRITERIA, snoozeCriteria);
+        ArrayList<String> people = new ArrayList<>();
+        people.add("you");
+        signals.putStringArrayList(Adjustment.KEY_PEOPLE, people);
+        Adjustment adjustment = new Adjustment("pkg", r.getKey(), signals, "", 0);
+        r.addAdjustment(adjustment);
+
+        assertFalse(r.getGroupKey().contains(GroupHelper.AUTOGROUP_KEY));
+        assertFalse(Objects.equals(people, r.getPeopleOverride()));
+        assertFalse(Objects.equals(snoozeCriteria, r.getSnoozeCriteria()));
+
+        assertNull(extractor.process(r));
+
+        assertTrue(r.getGroupKey().contains(GroupHelper.AUTOGROUP_KEY));
+        assertEquals(people, r.getPeopleOverride());
+        assertEquals(snoozeCriteria, r.getSnoozeCriteria());
+    }
+
+    @Test
+    public void testExtractsAdjustments() {
+        NotificationAdjustmentExtractor extractor = new NotificationAdjustmentExtractor();
+
+        NotificationRecord r = generateRecord();
+
+        Bundle pSignals = new Bundle();
+        ArrayList<String> people = new ArrayList<>();
+        people.add("you");
+        pSignals.putStringArrayList(Adjustment.KEY_PEOPLE, people);
+        Adjustment pAdjustment = new Adjustment("pkg", r.getKey(), pSignals, "", 0);
+        r.addAdjustment(pAdjustment);
+
+        Bundle sSignals = new Bundle();
+        ArrayList<SnoozeCriterion> snoozeCriteria = new ArrayList<>();
+        snoozeCriteria.add(new SnoozeCriterion("n", "n", "n"));
+        sSignals.putParcelableArrayList(Adjustment.KEY_SNOOZE_CRITERIA, snoozeCriteria);
+        Adjustment sAdjustment = new Adjustment("pkg", r.getKey(), sSignals, "", 0);
+        r.addAdjustment(sAdjustment);
+
+        Bundle gSignals = new Bundle();
+        gSignals.putString(Adjustment.KEY_GROUP_KEY, GroupHelper.AUTOGROUP_KEY);
+        Adjustment gAdjustment = new Adjustment("pkg", r.getKey(), gSignals, "", 0);
+        r.addAdjustment(gAdjustment);
+
+        assertFalse(r.getGroupKey().contains(GroupHelper.AUTOGROUP_KEY));
+        assertFalse(Objects.equals(people, r.getPeopleOverride()));
+        assertFalse(Objects.equals(snoozeCriteria, r.getSnoozeCriteria()));
+
+        assertNull(extractor.process(r));
+
+        assertTrue(r.getGroupKey().contains(GroupHelper.AUTOGROUP_KEY));
+        assertEquals(people, r.getPeopleOverride());
+        assertEquals(snoozeCriteria, r.getSnoozeCriteria());
+    }
+
+    private NotificationRecord generateRecord() {
+        NotificationChannel channel = new NotificationChannel("a", "a", IMPORTANCE_LOW);
+        final Notification.Builder builder = new Notification.Builder(getContext())
+                .setContentTitle("foo")
+                .setSmallIcon(android.R.drawable.sym_def_app_icon);
+        Notification n = builder.build();
+        StatusBarNotification sbn = new StatusBarNotification("", "", 0, "", 0,
+                0, n, UserHandle.ALL, null, System.currentTimeMillis());
+       return new NotificationRecord(getContext(), sbn, channel);
+    }
+}
diff --git a/services/tests/notification/src/com/android/server/notification/NotificationChannelExtractorTest.java b/services/tests/notification/src/com/android/server/notification/NotificationChannelExtractorTest.java
new file mode 100644
index 0000000..d75213c
--- /dev/null
+++ b/services/tests/notification/src/com/android/server/notification/NotificationChannelExtractorTest.java
@@ -0,0 +1,76 @@
+/*
+ * 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.app.NotificationManager.IMPORTANCE_DEFAULT;
+import static android.app.NotificationManager.IMPORTANCE_HIGH;
+import static android.app.NotificationManager.IMPORTANCE_LOW;
+
+import static junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.assertNotNull;
+import static junit.framework.Assert.assertNull;
+
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.anyInt;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.when;
+
+import android.app.Notification;
+import android.app.NotificationChannel;
+import android.app.PendingIntent;
+import android.content.Intent;
+import android.os.UserHandle;
+import android.service.notification.StatusBarNotification;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+public class NotificationChannelExtractorTest extends NotificationTestCase {
+
+    @Mock RankingConfig mConfig;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+    }
+
+    @Test
+    public void testExtractsUpdatedChannel() {
+        NotificationChannelExtractor extractor = new NotificationChannelExtractor();
+        extractor.setConfig(mConfig);
+
+        NotificationChannel channel = new NotificationChannel("a", "a", IMPORTANCE_LOW);
+        final Notification.Builder builder = new Notification.Builder(getContext())
+                .setContentTitle("foo")
+                .setSmallIcon(android.R.drawable.sym_def_app_icon);
+        Notification n = builder.build();
+        StatusBarNotification sbn = new StatusBarNotification("", "", 0, "", 0,
+                0, n, UserHandle.ALL, null, System.currentTimeMillis());
+        NotificationRecord r = new NotificationRecord(getContext(), sbn, channel);
+
+        NotificationChannel updatedChannel =
+                new NotificationChannel("a", "", IMPORTANCE_HIGH);
+        when(mConfig.getNotificationChannel(any(), anyInt(), eq("a"), eq(false)))
+                .thenReturn(updatedChannel);
+
+        assertNull(extractor.process(r));
+        assertEquals(updatedChannel, r.getChannel());
+    }
+
+}
diff --git a/services/tests/notification/src/com/android/server/notification/NotificationChannelTest.java b/services/tests/notification/src/com/android/server/notification/NotificationChannelTest.java
index 3007cb1..f457f6a 100644
--- a/services/tests/notification/src/com/android/server/notification/NotificationChannelTest.java
+++ b/services/tests/notification/src/com/android/server/notification/NotificationChannelTest.java
@@ -25,8 +25,14 @@
 import android.support.test.runner.AndroidJUnit4;
 import android.test.suitebuilder.annotation.SmallTest;
 
+import com.android.internal.util.FastXmlSerializer;
+
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.xmlpull.v1.XmlSerializer;
+
+import java.io.BufferedOutputStream;
+import java.io.ByteArrayOutputStream;
 
 @SmallTest
 @RunWith(AndroidJUnit4.class)
@@ -50,4 +56,15 @@
         channel.setBlockableSystem(true);
         assertEquals(true, channel.isBlockableSystem());
     }
+
+    @Test
+    public void testEmptyVibration_noException() throws Exception {
+        NotificationChannel channel = new NotificationChannel("a", "ab", IMPORTANCE_DEFAULT);
+        channel.setVibrationPattern(new long[0]);
+
+        XmlSerializer serializer = new FastXmlSerializer();
+        ByteArrayOutputStream baos = new ByteArrayOutputStream();
+        serializer.setOutput(new BufferedOutputStream(baos), "utf-8");
+        channel.writeXml(serializer);
+    }
 }
diff --git a/services/tests/notification/src/com/android/server/notification/NotificationIntrusivenessExtractorTest.java b/services/tests/notification/src/com/android/server/notification/NotificationIntrusivenessExtractorTest.java
new file mode 100644
index 0000000..d2f608e
--- /dev/null
+++ b/services/tests/notification/src/com/android/server/notification/NotificationIntrusivenessExtractorTest.java
@@ -0,0 +1,67 @@
+/*
+ * 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.app.NotificationManager.IMPORTANCE_DEFAULT;
+import static android.app.NotificationManager.IMPORTANCE_LOW;
+
+import static junit.framework.Assert.assertNotNull;
+import static junit.framework.Assert.assertNull;
+
+import android.app.Notification;
+import android.app.NotificationChannel;
+import android.app.PendingIntent;
+import android.content.Intent;
+import android.os.UserHandle;
+import android.service.notification.StatusBarNotification;
+
+import org.junit.Test;
+
+public class NotificationIntrusivenessExtractorTest extends NotificationTestCase {
+
+    @Test
+    public void testNonIntrusive() {
+        NotificationChannel channel = new NotificationChannel("a", "a", IMPORTANCE_LOW);
+        final Notification.Builder builder = new Notification.Builder(getContext())
+                .setContentTitle("foo")
+                .setSmallIcon(android.R.drawable.sym_def_app_icon);
+
+        Notification n = builder.build();
+        StatusBarNotification sbn = new StatusBarNotification("", "", 0, "", 0,
+                0, n, UserHandle.ALL, null, System.currentTimeMillis());
+        NotificationRecord r = new NotificationRecord(getContext(), sbn, channel);
+
+        assertNull(new NotificationIntrusivenessExtractor().process(r));
+    }
+
+    @Test
+    public void testIntrusive_fillScreen() {
+        NotificationChannel channel = new NotificationChannel("a", "a", IMPORTANCE_DEFAULT);
+        final Notification.Builder builder = new Notification.Builder(getContext())
+                .setContentTitle("foo")
+                .setFullScreenIntent(PendingIntent.getActivity(
+                        getContext(), 0, new Intent(""), 0), true)
+                .setSmallIcon(android.R.drawable.sym_def_app_icon);
+
+        Notification n = builder.build();
+        StatusBarNotification sbn = new StatusBarNotification("", "", 0, "", 0,
+                0, n, UserHandle.ALL, null, System.currentTimeMillis());
+        NotificationRecord r = new NotificationRecord(getContext(), sbn, channel);
+
+        assertNotNull(new NotificationIntrusivenessExtractor().process(r));
+    }
+}
diff --git a/services/tests/notification/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/notification/src/com/android/server/notification/NotificationManagerServiceTest.java
index e799427..ae2253e 100644
--- a/services/tests/notification/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/notification/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -20,6 +20,9 @@
 import static android.app.NotificationManager.IMPORTANCE_NONE;
 import static android.content.pm.PackageManager.PERMISSION_DENIED;
 
+import static com.android.server.notification.NotificationManagerService
+        .MESSAGE_SEND_RANKING_UPDATE;
+
 import static junit.framework.Assert.assertEquals;
 import static junit.framework.Assert.assertFalse;
 import static junit.framework.Assert.assertTrue;
@@ -31,9 +34,12 @@
 import static org.mockito.Matchers.eq;
 import static org.mockito.Mockito.any;
 import static org.mockito.Mockito.anyInt;
+import static org.mockito.Mockito.atLeastOnce;
+import static org.mockito.Mockito.doAnswer;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.reset;
+import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
@@ -54,6 +60,9 @@
 import android.graphics.Color;
 import android.media.AudioManager;
 import android.os.Binder;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
 import android.os.Process;
 import android.os.UserHandle;
 import android.provider.Settings.Secure;
@@ -63,7 +72,22 @@
 import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper;
 import android.testing.TestableLooper.RunWithLooper;
+import android.util.ArrayMap;
 import android.util.AtomicFile;
+import android.util.Log;
+import android.util.Slog;
+
+import com.android.server.lights.Light;
+import com.android.server.lights.LightsManager;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.mockito.stubbing.Answer;
 
 import java.io.File;
 import java.io.FileInputStream;
@@ -71,16 +95,7 @@
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.List;
-
-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;
-
-import com.android.server.lights.Light;
-import com.android.server.lights.LightsManager;
+import java.util.Map;
 
 @SmallTest
 @RunWith(AndroidTestingRunner.class)
@@ -109,6 +124,7 @@
     private AudioManager mAudioManager;
     @Mock
     ActivityManager mActivityManager;
+    NotificationManagerService.WorkerHandler mHandler;
 
     private NotificationChannel mTestNotificationChannel = new NotificationChannel(
             TEST_CHANNEL_ID, TEST_CHANNEL_ID, NotificationManager.IMPORTANCE_DEFAULT);
@@ -152,6 +168,9 @@
 
         mNotificationManagerService = new TestableNotificationManagerService(mContext);
 
+        // Use this testable looper.
+        mTestableLooper = TestableLooper.get(this);
+        mHandler = mNotificationManagerService.new WorkerHandler(mTestableLooper.getLooper());
         // MockPackageManager - default returns ApplicationInfo with matching calling UID
         final ApplicationInfo applicationInfo = new ApplicationInfo();
         applicationInfo.uid = uid;
@@ -162,8 +181,6 @@
         final LightsManager mockLightsManager = mock(LightsManager.class);
         when(mockLightsManager.getLight(anyInt())).thenReturn(mock(Light.class));
         when(mAudioManager.getRingerModeInternal()).thenReturn(AudioManager.RINGER_MODE_NORMAL);
-        // Use this testable looper.
-        mTestableLooper = TestableLooper.get(this);
 
         mFile = new File(mContext.getCacheDir(), "test.xml");
         mFile.createNewFile();
@@ -174,10 +191,11 @@
                 null, new ComponentName(PKG, "test_class"), uid, true, null, 0);
         when(mNotificationListeners.checkServiceTokenLocked(any())).thenReturn(mListener);
         try {
-            mNotificationManagerService.init(mTestableLooper.getLooper(), mPackageManager,
-                    mPackageManagerClient, mockLightsManager, mNotificationListeners,
-                    mNotificationAssistants, mConditionProviders, mCompanionMgr,
-                    mSnoozeHelper, mUsageStats, mPolicyFile, mActivityManager, mGroupHelper);
+            mNotificationManagerService.init(mTestableLooper.getLooper(),
+                    mPackageManager, mPackageManagerClient, mockLightsManager,
+                    mNotificationListeners, mNotificationAssistants, mConditionProviders,
+                    mCompanionMgr, mSnoozeHelper, mUsageStats, mPolicyFile, mActivityManager,
+                    mGroupHelper);
         } catch (SecurityException e) {
             if (!e.getMessage().contains("Permission Denial: not allowed to send broadcast")) {
                 throw e;
@@ -234,6 +252,43 @@
         return new NotificationRecord(mContext, sbn, channel);
     }
 
+    private Map<String, Answer> getSignalExtractorSideEffects() {
+        Map<String, Answer> answers = new ArrayMap<>();
+
+        answers.put("override group key", invocationOnMock -> {
+            ((NotificationRecord) invocationOnMock.getArguments()[0])
+                    .setOverrideGroupKey("bananas");
+            return null;
+        });
+        answers.put("override people", invocationOnMock -> {
+            ((NotificationRecord) invocationOnMock.getArguments()[0])
+                    .setPeopleOverride(new ArrayList<>());
+            return null;
+        });
+        answers.put("snooze criteria", invocationOnMock -> {
+            ((NotificationRecord) invocationOnMock.getArguments()[0])
+                    .setSnoozeCriteria(new ArrayList<>());
+            return null;
+        });
+        answers.put("notification channel", invocationOnMock -> {
+            ((NotificationRecord) invocationOnMock.getArguments()[0])
+                    .updateNotificationChannel(new NotificationChannel("a", "", IMPORTANCE_LOW));
+            return null;
+        });
+        answers.put("badging", invocationOnMock -> {
+            NotificationRecord r = (NotificationRecord) invocationOnMock.getArguments()[0];
+            r.setShowBadge(!r.canShowBadge());
+            return null;
+        });
+        answers.put("package visibility", invocationOnMock -> {
+            ((NotificationRecord) invocationOnMock.getArguments()[0]).setPackageVisibilityOverride(
+                    Notification.VISIBILITY_SECRET);
+            return null;
+        });
+
+        return answers;
+    }
+
     @Test
     public void testCreateNotificationChannels_SingleChannel() throws Exception {
         final NotificationChannel channel =
@@ -341,9 +396,9 @@
         mBinderService.enqueueNotificationWithTag(PKG, "opPkg", "tag", 0,
                 generateNotificationRecord(null).getNotification(), 0);
         waitForIdle();
-        StatusBarNotification[] notifs =
-                mBinderService.getActiveNotifications(PKG);
+        StatusBarNotification[] notifs = mBinderService.getActiveNotifications(PKG);
         assertEquals(1, notifs.length);
+        assertEquals(1, mNotificationManagerService.getNotificationRecordCount());
     }
 
     @Test
@@ -355,6 +410,7 @@
         StatusBarNotification[] notifs =
                 mBinderService.getActiveNotifications(PKG);
         assertEquals(0, notifs.length);
+        assertEquals(0, mNotificationManagerService.getNotificationRecordCount());
     }
 
     @Test
@@ -369,6 +425,7 @@
         StatusBarNotification[] notifs =
                 mBinderService.getActiveNotifications(PKG);
         assertEquals(0, notifs.length);
+        assertEquals(0, mNotificationManagerService.getNotificationRecordCount());
     }
 
     @Test
@@ -381,6 +438,7 @@
         StatusBarNotification[] notifs =
                 mBinderService.getActiveNotifications(sbn.getPackageName());
         assertEquals(0, notifs.length);
+        assertEquals(0, mNotificationManagerService.getNotificationRecordCount());
     }
 
     @Test
@@ -393,6 +451,43 @@
         StatusBarNotification[] notifs =
                 mBinderService.getActiveNotifications(sbn.getPackageName());
         assertEquals(0, notifs.length);
+        assertEquals(0, mNotificationManagerService.getNotificationRecordCount());
+    }
+
+    @Test
+    public void testUserInitiatedClearAll_noLeak() throws Exception {
+        final NotificationRecord n = generateNotificationRecord(
+                mTestNotificationChannel, 1, "group", true);
+
+        mBinderService.enqueueNotificationWithTag(PKG, "opPkg", "tag",
+                n.sbn.getId(), n.sbn.getNotification(), n.sbn.getUserId());
+        waitForIdle();
+
+        mNotificationManagerService.mNotificationDelegate.onClearAll(uid, Binder.getCallingPid(),
+                n.getUserId());
+        waitForIdle();
+        StatusBarNotification[] notifs =
+                mBinderService.getActiveNotifications(n.sbn.getPackageName());
+        assertEquals(0, notifs.length);
+        assertEquals(0, mNotificationManagerService.getNotificationRecordCount());
+    }
+
+    @Test
+    public void testCancelAllNotificationsCancelsChildren() throws Exception {
+        final NotificationRecord parent = generateNotificationRecord(
+                mTestNotificationChannel, 1, "group1", true);
+        final NotificationRecord child = generateNotificationRecord(
+                mTestNotificationChannel, 2, "group1", false);
+
+        mBinderService.enqueueNotificationWithTag(PKG, "opPkg", "tag",
+                parent.sbn.getId(), parent.sbn.getNotification(), parent.sbn.getUserId());
+        mBinderService.enqueueNotificationWithTag(PKG, "opPkg", "tag",
+                child.sbn.getId(), child.sbn.getNotification(), child.sbn.getUserId());
+        waitForIdle();
+
+        mBinderService.cancelAllNotifications(PKG, parent.sbn.getUserId());
+        waitForIdle();
+        assertEquals(0, mNotificationManagerService.getNotificationRecordCount());
     }
 
     @Test
@@ -404,6 +499,8 @@
         }
         mBinderService.cancelAllNotifications(PKG, sbn.getUserId());
         waitForIdle();
+
+        assertEquals(0, mNotificationManagerService.getNotificationRecordCount());
     }
 
     @Test
@@ -430,6 +527,8 @@
                 parentAsChild.sbn.getId(), parentAsChild.sbn.getNotification(),
                 parentAsChild.sbn.getUserId());
         waitForIdle();
+
+        assertEquals(0, mNotificationManagerService.getNotificationRecordCount());
     }
 
     @Test
@@ -443,6 +542,7 @@
         StatusBarNotification[] notifs =
                 mBinderService.getActiveNotifications(sbn.getPackageName());
         assertEquals(1, notifs.length);
+        assertEquals(1, mNotificationManagerService.getNotificationRecordCount());
     }
 
     @Test
@@ -456,6 +556,7 @@
         StatusBarNotification[] notifs =
                 mBinderService.getActiveNotifications(sbn.getPackageName());
         assertEquals(1, notifs.length);
+        assertEquals(1, mNotificationManagerService.getNotificationRecordCount());
     }
 
     @Test
@@ -468,6 +569,7 @@
         StatusBarNotification[] notifs =
                 mBinderService.getActiveNotifications(sbn.getPackageName());
         assertEquals(0, notifs.length);
+        assertEquals(0, mNotificationManagerService.getNotificationRecordCount());
     }
 
     @Test
@@ -481,6 +583,7 @@
         StatusBarNotification[] notifs =
                 mBinderService.getActiveNotifications(sbn.getPackageName());
         assertEquals(1, notifs.length);
+        assertEquals(1, mNotificationManagerService.getNotificationRecordCount());
     }
 
     @Test
@@ -597,6 +700,7 @@
         mBinderService.cancelNotificationWithTag(PKG, "tag", sbn.getId(), sbn.getUserId());
         waitForIdle();
         assertEquals(0, mBinderService.getActiveNotifications(sbn.getPackageName()).length);
+        assertEquals(0, mNotificationManagerService.getNotificationRecordCount());
     }
 
     @Test
@@ -1268,11 +1372,59 @@
         mNotificationManagerService.addNotification(otherPackage);
 
         // Same notifications are enqueued as posted, everything counts b/c id and tag don't match
-        assertEquals(40, mNotificationManagerService.getNotificationCountLocked(PKG, new UserHandle(uid).getIdentifier(), 0, null));
-        assertEquals(40, mNotificationManagerService.getNotificationCountLocked(PKG, new UserHandle(uid).getIdentifier(), 0, "tag2"));
-        assertEquals(2, mNotificationManagerService.getNotificationCountLocked("a", new UserHandle(uid).getIdentifier(), 0, "banana"));
+        int userId = new UserHandle(uid).getIdentifier();
+        assertEquals(40, mNotificationManagerService.getNotificationCountLocked(PKG, userId, 0, null));
+        assertEquals(40, mNotificationManagerService.getNotificationCountLocked(PKG, userId, 0, "tag2"));
+        assertEquals(2, mNotificationManagerService.getNotificationCountLocked("a", userId, 0, "banana"));
 
         // exclude a known notification - it's excluded from only the posted list, not enqueued
-        assertEquals(39, mNotificationManagerService.getNotificationCountLocked(PKG, new UserHandle(uid).getIdentifier(), 0, "tag"));
+        assertEquals(39, mNotificationManagerService.getNotificationCountLocked(PKG, userId, 0, "tag"));
+    }
+
+    @Test
+    public void testModifyAutogroup_requestsSort() throws Exception {
+        RankingHandler rh = mock(RankingHandler.class);
+        mNotificationManagerService.setRankingHandler(rh);
+
+        final NotificationRecord r = generateNotificationRecord(mTestNotificationChannel);
+        mNotificationManagerService.addNotification(r);
+        mNotificationManagerService.addAutogroupKeyLocked(r.getKey());
+        mNotificationManagerService.removeAutogroupKeyLocked(r.getKey());
+
+        verify(rh, times(2)).requestSort();
+    }
+
+    @Test
+    public void testHandleRankingSort_sendsUpdateOnSignalExtractorChange() throws Exception {
+        mNotificationManagerService.setRankingHelper(mRankingHelper);
+        NotificationManagerService.WorkerHandler handler = mock(
+                NotificationManagerService.WorkerHandler.class);
+        mNotificationManagerService.setHandler(handler);
+
+        Map<String, Answer> answers = getSignalExtractorSideEffects();
+        for (String message : answers.keySet()) {
+            mNotificationManagerService.clearNotifications();
+            final NotificationRecord r = generateNotificationRecord(mTestNotificationChannel);
+            mNotificationManagerService.addNotification(r);
+
+            doAnswer(answers.get(message)).when(mRankingHelper).extractSignals(r);
+
+            mNotificationManagerService.handleRankingSort();
+        }
+        verify(handler, times(answers.size())).scheduleSendRankingUpdate();
+    }
+
+    @Test
+    public void testHandleRankingSort_noUpdateWhenNoSignalChange() throws Exception {
+        mNotificationManagerService.setRankingHelper(mRankingHelper);
+        NotificationManagerService.WorkerHandler handler = mock(
+                NotificationManagerService.WorkerHandler.class);
+        mNotificationManagerService.setHandler(handler);
+
+        final NotificationRecord r = generateNotificationRecord(mTestNotificationChannel);
+        mNotificationManagerService.addNotification(r);
+
+        mNotificationManagerService.handleRankingSort();
+        verify(handler, never()).scheduleSendRankingUpdate();
     }
 }
diff --git a/services/tests/notification/src/com/android/server/notification/RankingHelperTest.java b/services/tests/notification/src/com/android/server/notification/RankingHelperTest.java
index 5a72e6b..801479b 100644
--- a/services/tests/notification/src/com/android/server/notification/RankingHelperTest.java
+++ b/services/tests/notification/src/com/android/server/notification/RankingHelperTest.java
@@ -78,6 +78,9 @@
 import static org.mockito.Matchers.anyInt;
 import static org.mockito.Matchers.anyString;
 import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
 @SmallTest
@@ -630,6 +633,8 @@
 
         // all fields should be changed
         assertEquals(channel2, mHelper.getNotificationChannel(PKG, UID, channel.getId(), false));
+
+        verify(mHandler, times(1)).requestSort();
     }
 
     @Test
@@ -712,6 +717,8 @@
         assertFalse(savedChannel.canBypassDnd());
         assertFalse(Notification.VISIBILITY_SECRET == savedChannel.getLockscreenVisibility());
         assertEquals(channel.canShowBadge(), savedChannel.canShowBadge());
+
+        verify(mHandler, never()).requestSort();
     }
 
     @Test
@@ -1058,6 +1065,8 @@
 
         // notDeleted
         assertEquals(1, mHelper.getNotificationChannelGroups(PKG, UID).size());
+
+        verify(mHandler, never()).requestSort();
     }
 
     @Test
@@ -1159,6 +1168,7 @@
         NotificationChannelGroup ncg = new NotificationChannelGroup("group1", "name1");
         mHelper.createNotificationChannelGroup(PKG, UID, ncg, true);
         assertEquals(ncg, mHelper.getNotificationChannelGroups(PKG, UID).iterator().next());
+        verify(mHandler, never()).requestSort();
     }
 
     @Test
@@ -1275,6 +1285,8 @@
         actual = mHelper.getNotificationChannel(PKG, UID, "id", false);
         assertEquals("goodbye", actual.getName());
         assertEquals(IMPORTANCE_DEFAULT, actual.getImportance());
+
+        verify(mHandler, times(1)).requestSort();
     }
 
     @Test
diff --git a/services/tests/servicestests/AndroidManifest.xml b/services/tests/servicestests/AndroidManifest.xml
index e12032d..59d205e 100644
--- a/services/tests/servicestests/AndroidManifest.xml
+++ b/services/tests/servicestests/AndroidManifest.xml
@@ -35,7 +35,6 @@
     <uses-permission android:name="android.permission.REORDER_TASKS" />
     <uses-permission android:name="android.permission.MANAGE_NETWORK_POLICY" />
     <uses-permission android:name="android.permission.READ_NETWORK_USAGE_HISTORY" />
-    <uses-permission android:name="android.permission.MODIFY_NETWORK_ACCOUNTING" />
     <uses-permission android:name="android.permission.CONNECTIVITY_INTERNAL" />
     <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
     <uses-permission android:name="android.permission.MANAGE_USERS" />
diff --git a/services/tests/servicestests/src/com/android/server/NetworkPolicyManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/NetworkPolicyManagerServiceTest.java
index ad8303a..11e383c 100644
--- a/services/tests/servicestests/src/com/android/server/NetworkPolicyManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/NetworkPolicyManagerServiceTest.java
@@ -24,15 +24,13 @@
 import static android.net.NetworkPolicyManager.POLICY_ALLOW_METERED_BACKGROUND;
 import static android.net.NetworkPolicyManager.POLICY_NONE;
 import static android.net.NetworkPolicyManager.POLICY_REJECT_METERED_BACKGROUND;
-import static android.net.NetworkPolicyManager.computeLastCycleBoundary;
-import static android.net.NetworkPolicyManager.computeNextCycleBoundary;
 import static android.net.NetworkPolicyManager.uidPoliciesToString;
 import static android.net.NetworkTemplate.buildTemplateMobileAll;
 import static android.net.TrafficStats.KB_IN_BYTES;
 import static android.net.TrafficStats.MB_IN_BYTES;
 import static android.telephony.CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED;
-import static android.telephony.CarrierConfigManager.DATA_CYCLE_USE_PLATFORM_DEFAULT;
 import static android.telephony.CarrierConfigManager.DATA_CYCLE_THRESHOLD_DISABLED;
+import static android.telephony.CarrierConfigManager.DATA_CYCLE_USE_PLATFORM_DEFAULT;
 import static android.telephony.CarrierConfigManager.KEY_DATA_LIMIT_THRESHOLD_BYTES_LONG;
 import static android.telephony.CarrierConfigManager.KEY_DATA_WARNING_THRESHOLD_BYTES_LONG;
 import static android.telephony.CarrierConfigManager.KEY_MONTHLY_DATA_CYCLE_DAY_INT;
@@ -41,10 +39,10 @@
 import static android.text.format.Time.TIMEZONE_UTC;
 
 import static com.android.server.net.NetworkPolicyManagerService.MAX_PROC_STATE_SEQ_HISTORY;
-import static com.android.server.net.NetworkPolicyManagerService.ProcStateSeqHistory;
 import static com.android.server.net.NetworkPolicyManagerService.TYPE_LIMIT;
 import static com.android.server.net.NetworkPolicyManagerService.TYPE_LIMIT_SNOOZED;
 import static com.android.server.net.NetworkPolicyManagerService.TYPE_WARNING;
+
 import static com.google.common.truth.Truth.assertThat;
 
 import static org.junit.Assert.assertEquals;
@@ -53,22 +51,19 @@
 import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
-import static org.mockito.Matchers.any;
-import static org.mockito.Matchers.anyInt;
-import static org.mockito.Matchers.anyLong;
-import static org.mockito.Matchers.anyString;
-import static org.mockito.Matchers.eq;
-import static org.mockito.Matchers.isA;
-import static org.mockito.Matchers.isNull;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyLong;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.ArgumentMatchers.isA;
+import static org.mockito.ArgumentMatchers.isNull;
 import static org.mockito.Mockito.atLeast;
 import static org.mockito.Mockito.atLeastOnce;
 import static org.mockito.Mockito.doAnswer;
-import static org.mockito.Mockito.doNothing;
 import static org.mockito.Mockito.doReturn;
-import static org.mockito.Mockito.doThrow;
 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;
 
@@ -97,6 +92,7 @@
 import android.net.NetworkInfo;
 import android.net.NetworkInfo.DetailedState;
 import android.net.NetworkPolicy;
+import android.net.NetworkPolicyManager;
 import android.net.NetworkState;
 import android.net.NetworkStats;
 import android.net.NetworkTemplate;
@@ -112,18 +108,21 @@
 import android.support.test.runner.AndroidJUnit4;
 import android.telephony.CarrierConfigManager;
 import android.telephony.SubscriptionManager;
+import android.telephony.SubscriptionPlan;
 import android.telephony.TelephonyManager;
 import android.text.TextUtils;
 import android.text.format.Time;
 import android.util.Log;
+import android.util.Pair;
 import android.util.TrustedTime;
 
-import com.android.internal.util.IndentingPrintWriter;
 import com.android.internal.telephony.PhoneConstants;
+import com.android.internal.util.IndentingPrintWriter;
 import com.android.internal.util.test.BroadcastInterceptingContext;
 import com.android.internal.util.test.BroadcastInterceptingContext.FutureIntent;
 import com.android.server.net.NetworkPolicyManagerInternal;
 import com.android.server.net.NetworkPolicyManagerService;
+import com.android.server.net.NetworkPolicyManagerService.ProcStateSeqHistory;
 
 import libcore.io.IoUtils;
 import libcore.io.Streams;
@@ -132,7 +131,6 @@
 
 import org.junit.After;
 import org.junit.Before;
-import org.junit.BeforeClass;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.rules.MethodRule;
@@ -156,8 +154,11 @@
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 import java.lang.annotation.Target;
+import java.time.Instant;
+import java.time.ZonedDateTime;
 import java.util.Arrays;
 import java.util.Calendar;
+import java.util.Iterator;
 import java.util.LinkedHashSet;
 import java.util.List;
 import java.util.concurrent.CountDownLatch;
@@ -393,6 +394,11 @@
         LocalServices.removeServiceForTest(UsageStatsManagerInternal.class);
     }
 
+    @After
+    public void resetClock() throws Exception {
+        SubscriptionPlan.sNowOverride = -1;
+    }
+
     @Test
     public void testTurnRestrictBackgroundOn() throws Exception {
         assertRestrictBackgroundOff(); // Sanity check.
@@ -778,6 +784,25 @@
         assertTrue(mService.isUidForeground(UID_B));
     }
 
+    private static long computeLastCycleBoundary(long currentTime, NetworkPolicy policy) {
+        SubscriptionPlan.sNowOverride = currentTime;
+        final Iterator<Pair<ZonedDateTime, ZonedDateTime>> it = NetworkPolicyManager
+                .cycleIterator(policy);
+        while (it.hasNext()) {
+            final Pair<ZonedDateTime, ZonedDateTime> cycle = it.next();
+            if (cycle.first.toInstant().toEpochMilli() < currentTime) {
+                return cycle.first.toInstant().toEpochMilli();
+            }
+        }
+        throw new IllegalStateException(
+                "Failed to find current cycle for " + policy + " at " + currentTime);
+    }
+
+    private static long computeNextCycleBoundary(long currentTime, NetworkPolicy policy) {
+        SubscriptionPlan.sNowOverride = currentTime;
+        return NetworkPolicyManager.cycleIterator(policy).next().second.toInstant().toEpochMilli();
+    }
+
     @Test
     public void testLastCycleBoundaryThisMonth() throws Exception {
         // assume cycle day of "5th", which should be in same month
@@ -818,7 +843,7 @@
     public void testLastCycleBoundaryLastMonthFebruary() throws Exception {
         // assume cycle day of "30th" in february, which should clamp
         final long currentTime = parseTime("2007-03-14T00:00:00.000Z");
-        final long expectedCycle = parseTime("2007-02-28T23:59:59.000Z");
+        final long expectedCycle = parseTime("2007-02-28T23:59:59.999Z");
 
         final NetworkPolicy policy = new NetworkPolicy(
                 sTemplateWifi, 30, TIMEZONE_UTC, 1024L, 1024L, false);
@@ -842,9 +867,9 @@
 
         assertTimeEquals(parseTime("2007-01-29T00:00:00.000Z"),
                 computeNextCycleBoundary(parseTime("2007-01-14T00:00:00.000Z"), policy));
-        assertTimeEquals(parseTime("2007-02-28T23:59:59.000Z"),
+        assertTimeEquals(parseTime("2007-02-28T23:59:59.999Z"),
                 computeNextCycleBoundary(parseTime("2007-02-14T00:00:00.000Z"), policy));
-        assertTimeEquals(parseTime("2007-02-28T23:59:59.000Z"),
+        assertTimeEquals(parseTime("2007-02-28T23:59:59.999Z"),
                 computeLastCycleBoundary(parseTime("2007-03-14T00:00:00.000Z"), policy));
         assertTimeEquals(parseTime("2007-03-29T00:00:00.000Z"),
                 computeNextCycleBoundary(parseTime("2007-03-14T00:00:00.000Z"), policy));
@@ -922,7 +947,7 @@
 
     @Test
     public void testLastCycleBoundaryDST() throws Exception {
-        final long currentTime = parseTime("1989-01-02T07:30:00.000");
+        final long currentTime = parseTime("1989-01-02T07:30:00.000Z");
         final long expectedCycle = parseTime("1988-12-03T02:00:00.000Z");
 
         final NetworkPolicy policy = new NetworkPolicy(
@@ -932,26 +957,16 @@
     }
 
     @Test
-    public void testLastCycleBoundaryJanuaryDST() throws Exception {
-        final long currentTime = parseTime("1989-01-26T21:00:00.000Z");
-        final long expectedCycle = parseTime("1989-01-01T01:59:59.000Z");
-
-        final NetworkPolicy policy = new NetworkPolicy(
-                sTemplateWifi, 32, "America/Argentina/Buenos_Aires", 1024L, 1024L, false);
-        final long actualCycle = computeLastCycleBoundary(currentTime, policy);
-        assertTimeEquals(expectedCycle, actualCycle);
-    }
-
-    @Test
     public void testNetworkPolicyAppliedCycleLastMonth() throws Exception {
         NetworkState[] state = null;
         NetworkStats stats = null;
 
-        final long TIME_FEB_15 = 1171497600000L;
-        final long TIME_MAR_10 = 1173484800000L;
         final int CYCLE_DAY = 15;
+        final long NOW = parseTime("2007-03-10T00:00Z");
+        final long CYCLE_START = parseTime("2007-02-15T00:00Z");
+        final long CYCLE_END = parseTime("2007-03-15T00:00Z");
 
-        setCurrentTimeMillis(TIME_MAR_10);
+        setCurrentTimeMillis(NOW);
 
         // first, pretend that wifi network comes online. no policy active,
         // which means we shouldn't push limit to interface.
@@ -971,7 +986,7 @@
         // pretend that 512 bytes total have happened
         stats = new NetworkStats(getElapsedRealtime(), 1)
                 .addIfaceValues(TEST_IFACE, 256L, 2L, 256L, 2L);
-        when(mStatsService.getNetworkTotalBytes(sTemplateWifi, TIME_FEB_15, TIME_MAR_10))
+        when(mStatsService.getNetworkTotalBytes(sTemplateWifi, CYCLE_START, CYCLE_END))
                 .thenReturn(stats.getTotalBytes());
 
         mPolicyListener.expect().onMeteredIfacesChanged(any());
@@ -991,11 +1006,12 @@
         NetworkStats stats = null;
         Future<String> tagFuture = null;
 
-        final long TIME_FEB_15 = 1171497600000L;
-        final long TIME_MAR_10 = 1173484800000L;
         final int CYCLE_DAY = 15;
+        final long NOW = parseTime("2007-03-10T00:00Z");
+        final long CYCLE_START = parseTime("2007-02-15T00:00Z");
+        final long CYCLE_END = parseTime("2007-03-15T00:00Z");
 
-        setCurrentTimeMillis(TIME_MAR_10);
+        setCurrentTimeMillis(NOW);
 
         // assign wifi policy
         state = new NetworkState[] {};
@@ -1005,8 +1021,8 @@
         {
             expectCurrentTime();
             when(mConnManager.getAllNetworkState()).thenReturn(state);
-            when(mStatsService.getNetworkTotalBytes(sTemplateWifi, TIME_FEB_15,
-                    currentTimeMillis())).thenReturn(stats.getTotalBytes());
+            when(mStatsService.getNetworkTotalBytes(sTemplateWifi, CYCLE_START,
+                    CYCLE_END)).thenReturn(stats.getTotalBytes());
 
             mPolicyListener.expect().onMeteredIfacesChanged(any());
             setNetworkPolicies(new NetworkPolicy(sTemplateWifi, CYCLE_DAY, TIMEZONE_UTC, 1
@@ -1024,8 +1040,8 @@
         {
             expectCurrentTime();
             when(mConnManager.getAllNetworkState()).thenReturn(state);
-            when(mStatsService.getNetworkTotalBytes(sTemplateWifi, TIME_FEB_15,
-                    currentTimeMillis())).thenReturn(stats.getTotalBytes());
+            when(mStatsService.getNetworkTotalBytes(sTemplateWifi, CYCLE_START,
+                    CYCLE_END)).thenReturn(stats.getTotalBytes());
 
             mPolicyListener.expect().onMeteredIfacesChanged(any());
             mServiceContext.sendBroadcast(new Intent(CONNECTIVITY_ACTION));
@@ -1043,8 +1059,8 @@
 
         {
             expectCurrentTime();
-            when(mStatsService.getNetworkTotalBytes(sTemplateWifi, TIME_FEB_15,
-                    currentTimeMillis())).thenReturn(stats.getTotalBytes());
+            when(mStatsService.getNetworkTotalBytes(sTemplateWifi, CYCLE_START,
+                    CYCLE_END)).thenReturn(stats.getTotalBytes());
             tagFuture = expectEnqueueNotification();
 
             mNetworkObserver.limitReached(null, TEST_IFACE);
@@ -1061,8 +1077,8 @@
 
         {
             expectCurrentTime();
-            when(mStatsService.getNetworkTotalBytes(sTemplateWifi, TIME_FEB_15,
-                    currentTimeMillis())).thenReturn(stats.getTotalBytes());
+            when(mStatsService.getNetworkTotalBytes(sTemplateWifi, CYCLE_START,
+                    CYCLE_END)).thenReturn(stats.getTotalBytes());
             tagFuture = expectEnqueueNotification();
 
             mNetworkObserver.limitReached(null, TEST_IFACE);
@@ -1077,8 +1093,8 @@
         {
             expectCurrentTime();
             when(mConnManager.getAllNetworkState()).thenReturn(state);
-            when(mStatsService.getNetworkTotalBytes(sTemplateWifi, TIME_FEB_15,
-                    currentTimeMillis())).thenReturn(stats.getTotalBytes());
+            when(mStatsService.getNetworkTotalBytes(sTemplateWifi, CYCLE_START,
+                    CYCLE_END)).thenReturn(stats.getTotalBytes());
             tagFuture = expectEnqueueNotification();
 
             mPolicyListener.expect().onMeteredIfacesChanged(any());
@@ -1129,6 +1145,15 @@
     }
 
     @Test
+    public void testConversion() throws Exception {
+        NetworkTemplate template = NetworkTemplate.buildTemplateMobileWildcard();
+        NetworkPolicy before = new NetworkPolicy(template, 12, "Israel", 123, 456, true);
+        NetworkPolicy after = SubscriptionPlan.convert(SubscriptionPlan.convert(before));
+        after.template = before.template;
+        assertEquals(before, after);
+    }
+
+    @Test
     public void testOnUidStateChanged_notifyAMS() throws Exception {
         final long procStateSeq = 222;
         callOnUidStateChanged(UID_A, ActivityManager.PROCESS_STATE_SERVICE, procStateSeq);
@@ -1470,9 +1495,7 @@
     }
 
     private static long parseTime(String time) {
-        final Time result = new Time();
-        result.parse3339(time);
-        return result.toMillis(true);
+        return ZonedDateTime.parse(time).toInstant().toEpochMilli();
     }
 
     private void setNetworkPolicies(NetworkPolicy... policies) {
@@ -1559,16 +1582,15 @@
     }
 
     private static String formatTime(long millis) {
-        final Time time = new Time(Time.TIMEZONE_UTC);
-        time.set(millis);
-        return time.format3339(false);
+        return Instant.ofEpochMilli(millis) + " [" + millis + "]";
     }
 
     private static void assertEqualsFuzzy(long expected, long actual, long fuzzy) {
         final long low = expected - fuzzy;
         final long high = expected + fuzzy;
         if (actual < low || actual > high) {
-            fail("value " + actual + " is outside [" + low + "," + high + "]");
+            fail("value " + formatTime(actual) + " is outside [" + formatTime(low) + ","
+                    + formatTime(high) + "]");
         }
     }
 
@@ -1643,6 +1665,7 @@
     }
 
     private void setCurrentTimeMillis(long currentTimeMillis) {
+        SubscriptionPlan.sNowOverride = currentTimeMillis;
         mStartTime = currentTimeMillis;
         mElapsedRealtime = 0L;
     }
diff --git a/services/tests/servicestests/src/com/android/server/am/ActivityRecordTests.java b/services/tests/servicestests/src/com/android/server/am/ActivityRecordTests.java
index f75d49c..2252c85 100644
--- a/services/tests/servicestests/src/com/android/server/am/ActivityRecordTests.java
+++ b/services/tests/servicestests/src/com/android/server/am/ActivityRecordTests.java
@@ -16,10 +16,15 @@
 
 package com.android.server.am;
 
+import static android.view.WindowManagerPolicy.NAV_BAR_BOTTOM;
+import static android.view.WindowManagerPolicy.NAV_BAR_LEFT;
+import static android.view.WindowManagerPolicy.NAV_BAR_RIGHT;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNull;
+import static org.mockito.Mockito.when;
 
 import android.content.ComponentName;
+import android.graphics.Rect;
 import android.platform.test.annotations.Presubmit;
 import android.support.test.filters.MediumTest;
 import android.support.test.runner.AndroidJUnit4;
@@ -94,4 +99,36 @@
 
         return -1;
     }
+
+    @Test
+    public void testPositionLimitedAspectRatioNavBarBottom() throws Exception {
+        verifyPositionWithLimitedAspectRatio(NAV_BAR_BOTTOM, new Rect(0, 0, 1000, 2000), 1.5f,
+                new Rect(0, 0, 1000, 1500));
+    }
+
+    @Test
+    public void testPositionLimitedAspectRatioNavBarLeft() throws Exception {
+        verifyPositionWithLimitedAspectRatio(NAV_BAR_LEFT, new Rect(0, 0, 2000, 1000), 1.5f,
+                new Rect(500, 0, 2000, 1000));
+    }
+
+    @Test
+    public void testPositionLimitedAspectRatioNavBarRight() throws Exception {
+        verifyPositionWithLimitedAspectRatio(NAV_BAR_RIGHT, new Rect(0, 0, 2000, 1000), 1.5f,
+                new Rect(0, 0, 1500, 1000));
+    }
+
+    private void verifyPositionWithLimitedAspectRatio(int navBarPosition, Rect taskBounds,
+            float aspectRatio, Rect expectedActivityBounds) {
+        final ActivityManagerService service = createActivityManagerService();
+        final TaskRecord task = createTask(service, testActivityComponent, TEST_STACK_ID);
+        final ActivityRecord record = createActivity(service, testActivityComponent, task);
+
+        // Verify with nav bar on the right.
+        when(service.mWindowManager.getNavBarPosition()).thenReturn(navBarPosition);
+        task.getConfiguration().setAppBounds(taskBounds);
+        record.info.maxAspectRatio = aspectRatio;
+        record.ensureActivityConfigurationLocked(0 /* globalChanges */, false /* preserveWindow */);
+        assertEquals(expectedActivityBounds, record.getBounds());
+    }
 }
diff --git a/services/tests/servicestests/src/com/android/server/am/ActivityTestsBase.java b/services/tests/servicestests/src/com/android/server/am/ActivityTestsBase.java
index bac1216..16bc011 100644
--- a/services/tests/servicestests/src/com/android/server/am/ActivityTestsBase.java
+++ b/services/tests/servicestests/src/com/android/server/am/ActivityTestsBase.java
@@ -120,6 +120,7 @@
                 null /*_taskDescription*/, new ActivityManager.TaskThumbnailInfo());
         final ActivityStack stack = service.mStackSupervisor.getStack(stackId,
                 true /*createStaticStackIfNeeded*/, true /*onTop*/);
+        service.mStackSupervisor.setFocusStackUnchecked("test", stack);
         stack.addTask(task, true, "creating test task");
         task.setStack(stack);
         task.setWindowContainerController(mock(TaskWindowContainerController.class));
diff --git a/services/tests/servicestests/src/com/android/server/backup/BackupPasswordManagerTest.java b/services/tests/servicestests/src/com/android/server/backup/BackupPasswordManagerTest.java
new file mode 100644
index 0000000..04c0251
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/backup/BackupPasswordManagerTest.java
@@ -0,0 +1,232 @@
+/*
+ * 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.backup;
+
+import static com.android.server.testutis.TestUtils.assertExpectException;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.Mockito.doThrow;
+
+import android.content.Context;
+import android.platform.test.annotations.Presubmit;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import com.android.server.backup.utils.PasswordUtils;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TemporaryFolder;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.io.DataOutputStream;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.security.SecureRandom;
+
+@SmallTest
+@Presubmit
+@RunWith(AndroidJUnit4.class)
+public class BackupPasswordManagerTest {
+    private static final String PASSWORD_VERSION_FILE_NAME = "pwversion";
+    private static final String PASSWORD_HASH_FILE_NAME = "pwhash";
+    private static final String V1_HASH_ALGORITHM = "PBKDF2WithHmacSHA1And8bit";
+
+    @Rule public TemporaryFolder mTemporaryFolder = new TemporaryFolder();
+
+    @Mock private Context mContext;
+
+    private File mStateFolder;
+    private BackupPasswordManager mPasswordManager;
+
+    @Before
+    public void setUp() throws Exception {
+        MockitoAnnotations.initMocks(this);
+        mStateFolder = mTemporaryFolder.newFolder();
+        mPasswordManager = new BackupPasswordManager(mContext, mStateFolder, new SecureRandom());
+    }
+
+    @Test
+    public void hasBackupPassword_isFalseIfFileDoesNotExist() {
+        assertThat(mPasswordManager.hasBackupPassword()).isFalse();
+    }
+
+    @Test
+    public void hasBackupPassword_isTrueIfFileExists() throws Exception {
+        mPasswordManager.setBackupPassword(null, "password1234");
+        assertThat(mPasswordManager.hasBackupPassword()).isTrue();
+    }
+
+    @Test
+    public void hasBackupPassword_throwsSecurityExceptionIfLacksPermission() {
+        setDoesNotHavePermission();
+
+        assertExpectException(
+                SecurityException.class,
+                /* expectedExceptionMessageRegex */ null,
+                () -> mPasswordManager.hasBackupPassword());
+    }
+
+    @Test
+    public void backupPasswordMatches_isTrueIfNoPassword() {
+        assertThat(mPasswordManager.backupPasswordMatches("anything")).isTrue();
+    }
+
+    @Test
+    public void backupPasswordMatches_isTrueForSamePassword() {
+        String password = "password1234";
+        mPasswordManager.setBackupPassword(null, password);
+        assertThat(mPasswordManager.backupPasswordMatches(password)).isTrue();
+    }
+
+    @Test
+    public void backupPasswordMatches_isFalseForDifferentPassword() {
+        mPasswordManager.setBackupPassword(null, "shiba");
+        assertThat(mPasswordManager.backupPasswordMatches("corgi")).isFalse();
+    }
+
+    @Test
+    public void backupPasswordMatches_worksForV1HashIfVersionIsV1() throws Exception {
+        String password = "corgi\uFFFF";
+        writePasswordVersionToFile(1);
+        writeV1HashToFile(password, saltFixture());
+
+        // Reconstruct so it reloads from filesystem
+        mPasswordManager = new BackupPasswordManager(mContext, mStateFolder, new SecureRandom());
+
+        assertThat(mPasswordManager.backupPasswordMatches(password)).isTrue();
+    }
+
+    @Test
+    public void backupPasswordMatches_failsForV1HashIfVersionIsV2() throws Exception {
+        // The algorithms produce identical hashes except if the password contains higher-order
+        // unicode. See
+        // https://android-developers.googleblog.com/2013/12/changes-to-secretkeyfactory-api-in.html
+        String password = "corgi\uFFFF";
+        writePasswordVersionToFile(2);
+        writeV1HashToFile(password, saltFixture());
+
+        // Reconstruct so it reloads from filesystem
+        mPasswordManager = new BackupPasswordManager(mContext, mStateFolder, new SecureRandom());
+
+        assertThat(mPasswordManager.backupPasswordMatches(password)).isFalse();
+    }
+
+    @Test
+    public void backupPasswordMatches_throwsSecurityExceptionIfLacksPermission() {
+        setDoesNotHavePermission();
+
+        assertExpectException(
+                SecurityException.class,
+                /* expectedExceptionMessageRegex */ null,
+                () -> mPasswordManager.backupPasswordMatches("password123"));
+    }
+
+    @Test
+    public void setBackupPassword_persistsPasswordToFile() {
+        String password = "shiba";
+
+        mPasswordManager.setBackupPassword(null, password);
+
+        BackupPasswordManager newManager = new BackupPasswordManager(
+                mContext, mStateFolder, new SecureRandom());
+        assertThat(newManager.backupPasswordMatches(password)).isTrue();
+    }
+
+    @Test
+    public void setBackupPassword_failsIfCurrentPasswordIsWrong() {
+        String secondPassword = "second password";
+        mPasswordManager.setBackupPassword(null, "first password");
+
+        boolean result = mPasswordManager.setBackupPassword(
+                "incorrect pass", secondPassword);
+
+        BackupPasswordManager newManager = new BackupPasswordManager(
+                mContext, mStateFolder, new SecureRandom());
+        assertThat(result).isFalse();
+        assertThat(newManager.backupPasswordMatches(secondPassword)).isFalse();
+    }
+
+    @Test
+    public void setBackupPassword_throwsSecurityExceptionIfLacksPermission() {
+        setDoesNotHavePermission();
+
+        assertExpectException(
+                SecurityException.class,
+                /* expectedExceptionMessageRegex */ null,
+                () -> mPasswordManager.setBackupPassword(
+                        "password123", "password111"));
+    }
+
+    private byte[] saltFixture() {
+        byte[] bytes = new byte[64];
+        for (int i = 0; i < 64; i++) {
+            bytes[i] = (byte) i;
+        }
+        return bytes;
+    }
+
+    private void setDoesNotHavePermission() {
+        doThrow(new SecurityException()).when(mContext)
+                .enforceCallingOrSelfPermission(anyString(), anyString());
+    }
+
+    private void writeV1HashToFile(String password, byte[] salt) throws Exception {
+        String hash = PasswordUtils.buildPasswordHash(
+                V1_HASH_ALGORITHM, password, salt, PasswordUtils.PBKDF2_HASH_ROUNDS);
+        writeHashAndSaltToFile(hash, salt);
+    }
+
+    private void writeHashAndSaltToFile(String hash, byte[] salt) throws Exception {
+        FileOutputStream fos = null;
+        DataOutputStream dos = null;
+
+        try {
+            File passwordHash = new File(mStateFolder, PASSWORD_HASH_FILE_NAME);
+            fos = new FileOutputStream(passwordHash);
+            dos = new DataOutputStream(fos);
+            dos.writeInt(salt.length);
+            dos.write(salt);
+            dos.writeUTF(hash);
+            dos.flush();
+        } finally {
+            if (dos != null) dos.close();
+            if (fos != null) fos.close();
+        }
+    }
+
+    private void writePasswordVersionToFile(int version) throws Exception {
+        FileOutputStream fos = null;
+        DataOutputStream dos = null;
+
+        try {
+            File passwordVersion = new File(mStateFolder, PASSWORD_VERSION_FILE_NAME);
+            fos = new FileOutputStream(passwordVersion);
+            dos = new DataOutputStream(fos);
+            dos.writeInt(version);
+            dos.flush();
+        } finally {
+            if (dos != null) dos.close();
+            if (fos != null) fos.close();
+        }
+    }
+}
diff --git a/services/tests/servicestests/src/com/android/server/backup/DataChangedJournalTest.java b/services/tests/servicestests/src/com/android/server/backup/DataChangedJournalTest.java
new file mode 100644
index 0000000..c27fd07
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/backup/DataChangedJournalTest.java
@@ -0,0 +1,132 @@
+/*
+ * 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.backup;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.platform.test.annotations.Presubmit;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TemporaryFolder;
+import org.junit.runner.RunWith;
+import org.mockito.InOrder;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.MockitoAnnotations;
+
+import java.io.DataInputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.util.ArrayList;
+
+@SmallTest
+@Presubmit
+@RunWith(AndroidJUnit4.class)
+public class DataChangedJournalTest {
+    private static final String GMAIL = "com.google.gmail";
+    private static final String DOCS = "com.google.docs";
+    private static final String GOOGLE_PLUS = "com.google.plus";
+
+    @Rule public TemporaryFolder mTemporaryFolder = new TemporaryFolder();
+
+    @Mock private DataChangedJournal.Consumer mConsumer;
+
+    private File mFile;
+    private DataChangedJournal mJournal;
+
+    @Before
+    public void setUp() throws Exception {
+        MockitoAnnotations.initMocks(this);
+        mFile = mTemporaryFolder.newFile();
+        mJournal = new DataChangedJournal(mFile);
+    }
+
+    @Test
+    public void addPackage_addsPackagesToEndOfFile() throws Exception {
+        mJournal.addPackage(GMAIL);
+        mJournal.addPackage(DOCS);
+        mJournal.addPackage(GOOGLE_PLUS);
+
+        FileInputStream fos = new FileInputStream(mFile);
+        DataInputStream dos = new DataInputStream(fos);
+        assertThat(dos.readUTF()).isEqualTo(GMAIL);
+        assertThat(dos.readUTF()).isEqualTo(DOCS);
+        assertThat(dos.readUTF()).isEqualTo(GOOGLE_PLUS);
+        assertThat(dos.available()).isEqualTo(0);
+    }
+
+    @Test
+    public void delete_deletesTheFile() throws Exception {
+        mJournal.addPackage(GMAIL);
+
+        mJournal.delete();
+
+        assertThat(mFile.exists()).isFalse();
+    }
+
+    @Test
+    public void equals_isTrueForTheSameFile() throws Exception {
+        assertThat(mJournal.equals(new DataChangedJournal(mFile))).isTrue();
+    }
+
+    @Test
+    public void equals_isFalseForDifferentFiles() throws Exception {
+        assertThat(mJournal.equals(new DataChangedJournal(mTemporaryFolder.newFile()))).isFalse();
+    }
+
+    @Test
+    public void forEach_iteratesThroughPackagesInFileInOrder() throws Exception {
+        mJournal.addPackage(GMAIL);
+        mJournal.addPackage(DOCS);
+
+        mJournal.forEach(mConsumer);
+
+        InOrder inOrder = Mockito.inOrder(mConsumer);
+        inOrder.verify(mConsumer).accept(GMAIL);
+        inOrder.verify(mConsumer).accept(DOCS);
+        inOrder.verifyNoMoreInteractions();
+    }
+
+    @Test
+    public void listJournals_returnsJournalsForEveryFileInDirectory() throws Exception {
+        File folder = mTemporaryFolder.newFolder();
+        DataChangedJournal.newJournal(folder);
+        DataChangedJournal.newJournal(folder);
+
+        ArrayList<DataChangedJournal> journals = DataChangedJournal.listJournals(folder);
+
+        assertThat(journals).hasSize(2);
+    }
+
+    @Test
+    public void newJournal_createsANewTemporaryFile() throws Exception {
+        File folder = mTemporaryFolder.newFolder();
+
+        DataChangedJournal.newJournal(folder);
+
+        assertThat(folder.listFiles()).hasLength(1);
+    }
+
+    @Test
+    public void toString_isSameAsFileToString() throws Exception {
+        assertThat(mJournal.toString()).isEqualTo(mFile.toString());
+    }
+}
diff --git a/services/tests/servicestests/src/com/android/server/backup/utils/DataStreamFileCodecTest.java b/services/tests/servicestests/src/com/android/server/backup/utils/DataStreamFileCodecTest.java
new file mode 100644
index 0000000..bfb95c1
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/backup/utils/DataStreamFileCodecTest.java
@@ -0,0 +1,86 @@
+/*
+ * 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.backup.utils;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.platform.test.annotations.Presubmit;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TemporaryFolder;
+import org.junit.runner.RunWith;
+
+import java.io.DataInputStream;
+import java.io.DataOutputStream;
+import java.io.File;
+import java.io.IOException;
+
+@SmallTest
+@Presubmit
+@RunWith(AndroidJUnit4.class)
+public final class DataStreamFileCodecTest {
+    @Rule public TemporaryFolder mTemporaryFolder = new TemporaryFolder();
+
+    @Test
+    public void serialize_writesToTheFile() throws Exception {
+        File unicornFile = mTemporaryFolder.newFile();
+
+        DataStreamFileCodec<MythicalCreature> mythicalCreatureCodec = new DataStreamFileCodec<>(
+                unicornFile, new MythicalCreatureDataStreamCodec());
+        MythicalCreature unicorn = new MythicalCreature(
+                10000, "Unicorn");
+        mythicalCreatureCodec.serialize(unicorn);
+
+        DataStreamFileCodec<MythicalCreature> newCodecWithSameFile = new DataStreamFileCodec<>(
+                unicornFile, new MythicalCreatureDataStreamCodec());
+        MythicalCreature deserializedUnicorn = newCodecWithSameFile.deserialize();
+
+        assertThat(deserializedUnicorn.averageLifespanInYears)
+                .isEqualTo(unicorn.averageLifespanInYears);
+        assertThat(deserializedUnicorn.name).isEqualTo(unicorn.name);
+    }
+
+    private static class MythicalCreature {
+        int averageLifespanInYears;
+        String name;
+
+        MythicalCreature(int averageLifespanInYears, String name) {
+            this.averageLifespanInYears = averageLifespanInYears;
+            this.name = name;
+        }
+    }
+
+    private static class MythicalCreatureDataStreamCodec implements
+            DataStreamCodec<MythicalCreature> {
+        @Override
+        public void serialize(MythicalCreature mythicalCreature,
+                DataOutputStream dataOutputStream) throws IOException {
+            dataOutputStream.writeInt(mythicalCreature.averageLifespanInYears);
+            dataOutputStream.writeUTF(mythicalCreature.name);
+        }
+
+        @Override
+        public MythicalCreature deserialize(DataInputStream dataInputStream)
+                throws IOException {
+            int years = dataInputStream.readInt();
+            String name = dataInputStream.readUTF();
+            return new MythicalCreature(years, name);
+        }
+    }
+}
diff --git a/services/tests/servicestests/src/com/android/server/backup/utils/SparseArrayUtilsTest.java b/services/tests/servicestests/src/com/android/server/backup/utils/SparseArrayUtilsTest.java
new file mode 100644
index 0000000..db55120
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/backup/utils/SparseArrayUtilsTest.java
@@ -0,0 +1,55 @@
+/*
+ * 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.backup.utils;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.platform.test.annotations.Presubmit;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+import android.util.SparseArray;
+
+import com.google.android.collect.Sets;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.HashSet;
+
+@SmallTest
+@Presubmit
+@RunWith(AndroidJUnit4.class)
+public class SparseArrayUtilsTest {
+    @Test
+    public void union_mergesSets() {
+        SparseArray<HashSet<String>> sparseArray = new SparseArray<>();
+        sparseArray.append(12, Sets.newHashSet("a", "b", "c"));
+        sparseArray.append(45, Sets.newHashSet("d", "e"));
+        sparseArray.append(46, Sets.newHashSet());
+        sparseArray.append(66, Sets.newHashSet("a", "e", "f"));
+
+        assertThat(SparseArrayUtils.union(sparseArray)).isEqualTo(
+                Sets.newHashSet("a", "b", "c", "d", "e", "f"));
+    }
+
+    @Test
+    public void union_returnsEmptySetForEmptyList() {
+        SparseArray<HashSet<String>> sparseArray = new SparseArray<>();
+
+        assertThat(SparseArrayUtils.union(sparseArray)).isEqualTo(Sets.newHashSet());
+    }
+}
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 3866e0ed..dcca724 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
@@ -69,6 +69,7 @@
 import android.os.Process;
 import android.os.UserHandle;
 import android.os.UserManager;
+import android.platform.test.annotations.Presubmit;
 import android.provider.Settings;
 import android.security.KeyChain;
 import android.telephony.TelephonyManager;
@@ -112,6 +113,7 @@
  * runtest -c com.android.server.devicepolicy.DevicePolicyManagerTest frameworks-services
  */
 @SmallTest
+@Presubmit
 public class DevicePolicyManagerTest extends DpmTestBase {
     private static final List<String> OWNER_SETUP_PERMISSIONS = Arrays.asList(
             permission.MANAGE_DEVICE_ADMINS, permission.MANAGE_PROFILE_AND_DEVICE_OWNERS,
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsStorageTests.java b/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsStorageTests.java
index 4c77f62..b0325cb 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsStorageTests.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsStorageTests.java
@@ -347,11 +347,11 @@
     }
 
     public void testPersistentData_serializeUnserialize() {
-        byte[] serialized = PersistentData.toBytes(PersistentData.TYPE_GATEKEEPER, SOME_USER_ID,
+        byte[] serialized = PersistentData.toBytes(PersistentData.TYPE_SP, SOME_USER_ID,
                 DevicePolicyManager.PASSWORD_QUALITY_COMPLEX, PAYLOAD);
         PersistentData deserialized = PersistentData.fromBytes(serialized);
 
-        assertEquals(PersistentData.TYPE_GATEKEEPER, deserialized.type);
+        assertEquals(PersistentData.TYPE_SP, deserialized.type);
         assertEquals(DevicePolicyManager.PASSWORD_QUALITY_COMPLEX, deserialized.qualityForUi);
         assertArrayEquals(PAYLOAD, deserialized.payload);
     }
@@ -371,7 +371,7 @@
         // the wire format in the future.
         byte[] serializedVersion1 = new byte[] {
                 1, /* PersistentData.VERSION_1 */
-                2, /* PersistentData.TYPE_SP */
+                1, /* PersistentData.TYPE_SP */
                 0x00, 0x00, 0x04, 0x0A,  /* SOME_USER_ID */
                 0x00, 0x03, 0x00, 0x00,  /* PASSWORD_NUMERIC_COMPLEX */
                 1, 2, -1, -2, 33, /* PAYLOAD */
@@ -385,9 +385,8 @@
 
         // Make sure the constants we use on the wire do not change.
         assertEquals(0, PersistentData.TYPE_NONE);
-        assertEquals(1, PersistentData.TYPE_GATEKEEPER);
-        assertEquals(2, PersistentData.TYPE_SP);
-        assertEquals(3, PersistentData.TYPE_SP_WEAVER);
+        assertEquals(1, PersistentData.TYPE_SP);
+        assertEquals(2, PersistentData.TYPE_SP_WEAVER);
     }
 
     public void testCredentialHash_serializeUnserialize() {
diff --git a/services/tests/servicestests/src/com/android/server/pm/dex/PackageDexUsageTests.java b/services/tests/servicestests/src/com/android/server/pm/dex/PackageDexUsageTests.java
index 2e99433..e1ef41e 100644
--- a/services/tests/servicestests/src/com/android/server/pm/dex/PackageDexUsageTests.java
+++ b/services/tests/servicestests/src/com/android/server/pm/dex/PackageDexUsageTests.java
@@ -72,25 +72,25 @@
         String isa = VMRuntime.getInstructionSet(Build.SUPPORTED_ABIS[0]);
 
         mFooBaseUser0 = new TestData(fooPackageName,
-                fooCodeDir + "base.apk", 0, isa, false, true);
+                fooCodeDir + "base.apk", 0, isa, false, true, fooPackageName);
 
         mFooSplit1User0 = new TestData(fooPackageName,
-                fooCodeDir + "split-1.apk", 0, isa, false, true);
+                fooCodeDir + "split-1.apk", 0, isa, false, true, fooPackageName);
 
         mFooSplit2UsedByOtherApps0 = new TestData(fooPackageName,
-                fooCodeDir + "split-2.apk", 0, isa, true, true);
+                fooCodeDir + "split-2.apk", 0, isa, true, true, "used.by.other.com");
 
         mFooSecondary1User0 = new TestData(fooPackageName,
-                fooDataDir + "sec-1.dex", 0, isa, false, false);
+                fooDataDir + "sec-1.dex", 0, isa, false, false, fooPackageName);
 
         mFooSecondary1User1 = new TestData(fooPackageName,
-                fooDataDir + "sec-1.dex", 1, isa, false, false);
+                fooDataDir + "sec-1.dex", 1, isa, false, false, fooPackageName);
 
         mFooSecondary2UsedByOtherApps0 = new TestData(fooPackageName,
-                fooDataDir + "sec-2.dex", 0, isa, true, false);
+                fooDataDir + "sec-2.dex", 0, isa, true, false, "used.by.other.com");
 
         mInvalidIsa = new TestData(fooPackageName,
-                fooCodeDir + "base.apk", 0, "INVALID_ISA", false, true);
+                fooCodeDir + "base.apk", 0, "INVALID_ISA", false, true, "INALID_USER");
 
         String barPackageName = "com.google.bar";
         String barCodeDir = "/data/app/com.google.bar/";
@@ -98,11 +98,11 @@
         String barDataDir1 = "/data/user/1/com.google.bar/";
 
         mBarBaseUser0 = new TestData(barPackageName,
-                barCodeDir + "base.apk", 0, isa, false, true);
+                barCodeDir + "base.apk", 0, isa, false, true, barPackageName);
         mBarSecondary1User0 = new TestData(barPackageName,
-                barDataDir + "sec-1.dex", 0, isa, false, false);
+                barDataDir + "sec-1.dex", 0, isa, false, false, barPackageName);
         mBarSecondary2User1 = new TestData(barPackageName,
-                barDataDir1 + "sec-2.dex", 1, isa, false, false);
+                barDataDir1 + "sec-2.dex", 1, isa, false, false, barPackageName);
     }
 
     @Test
@@ -319,7 +319,8 @@
             mFooSplit2UsedByOtherApps0.mOwnerUserId,
             mFooSplit2UsedByOtherApps0.mLoaderIsa,
             /*mIsUsedByOtherApps*/false,
-            mFooSplit2UsedByOtherApps0.mPrimaryOrSplit);
+            mFooSplit2UsedByOtherApps0.mPrimaryOrSplit,
+            mFooSplit2UsedByOtherApps0.mUsedBy);
         assertPackageDexUsage(noLongerUsedByOtherApps);
     }
 
@@ -332,14 +333,65 @@
         assertFalse(mPackageDexUsage.clearUsedByOtherApps(mFooSplit2UsedByOtherApps0.mPackageName));
     }
 
+    @Test
+    public void testRecordDexFileUsers() {
+        PackageDexUsage packageDexUsageRecordUsers = new PackageDexUsage();
+        Set<String> users = new HashSet<>(Arrays.asList(
+                new String[] {"another.package.1"}));
+        Set<String> usersExtra = new HashSet<>(Arrays.asList(
+                new String[] {"another.package.2", "another.package.3"}));
+
+        assertTrue(record(packageDexUsageRecordUsers, mFooBaseUser0, users));
+        assertTrue(record(packageDexUsageRecordUsers, mFooBaseUser0, usersExtra));
+
+        assertTrue(record(packageDexUsageRecordUsers, mFooSecondary1User0, users));
+        assertTrue(record(packageDexUsageRecordUsers, mFooSecondary1User0, usersExtra));
+
+        packageDexUsageRecordUsers = writeAndReadBack(packageDexUsageRecordUsers);
+        // Verify that the users were recorded.
+        Set<String> userAll = new HashSet<>(users);
+        userAll.addAll(usersExtra);
+        assertPackageDexUsage(packageDexUsageRecordUsers, userAll, mFooBaseUser0,
+                mFooSecondary1User0);
+    }
+
+    @Test
+    public void testRecordDexFileUsersNotTheOwningPackage() {
+        PackageDexUsage packageDexUsageRecordUsers = new PackageDexUsage();
+        Set<String> users = new HashSet<>(Arrays.asList(
+                new String[] {mFooBaseUser0.mPackageName,}));
+        Set<String> usersExtra = new HashSet<>(Arrays.asList(
+                new String[] {"another.package.2", "another.package.3"}));
+
+        assertTrue(record(packageDexUsageRecordUsers, mFooBaseUser0, users));
+        assertTrue(record(packageDexUsageRecordUsers, mFooBaseUser0, usersExtra));
+
+        assertTrue(record(packageDexUsageRecordUsers, mFooSecondary1User0, users));
+        assertTrue(record(packageDexUsageRecordUsers, mFooSecondary1User0, usersExtra));
+
+        packageDexUsageRecordUsers = writeAndReadBack(packageDexUsageRecordUsers);
+        // Verify that only the non owning packages were recorded.
+        assertPackageDexUsage(packageDexUsageRecordUsers, usersExtra, mFooBaseUser0,
+                mFooSecondary1User0);
+    }
+
     private void assertPackageDexUsage(TestData primary, TestData... secondaries) {
+        assertPackageDexUsage(mPackageDexUsage, null, primary, secondaries);
+    }
+
+    private void assertPackageDexUsage(PackageDexUsage packageDexUsage, Set<String> users,
+            TestData primary, TestData... secondaries) {
         String packageName = primary == null ? secondaries[0].mPackageName : primary.mPackageName;
         boolean primaryUsedByOtherApps = primary == null ? false : primary.mUsedByOtherApps;
-        PackageUseInfo pInfo = mPackageDexUsage.getPackageUseInfo(packageName);
+        PackageUseInfo pInfo = packageDexUsage.getPackageUseInfo(packageName);
 
         // Check package use info
         assertNotNull(pInfo);
         assertEquals(primaryUsedByOtherApps, pInfo.isUsedByOtherApps());
+        if (users != null) {
+            assertEquals(pInfo.getLoadingPackages(), users);
+        }
+
         Map<String, DexUseInfo> dexUseInfoMap = pInfo.getDexUseInfoMap();
         assertEquals(secondaries.length, dexUseInfoMap.size());
 
@@ -351,24 +403,43 @@
             assertEquals(testData.mOwnerUserId, dInfo.getOwnerUserId());
             assertEquals(1, dInfo.getLoaderIsas().size());
             assertTrue(dInfo.getLoaderIsas().contains(testData.mLoaderIsa));
+            if (users != null) {
+                 assertEquals(dInfo.getLoadingPackages(), users);
+            }
         }
     }
 
     private boolean record(TestData testData) {
         return mPackageDexUsage.record(testData.mPackageName, testData.mDexFile,
-                testData.mOwnerUserId, testData.mLoaderIsa, testData.mUsedByOtherApps,
-                testData.mPrimaryOrSplit);
+               testData.mOwnerUserId, testData.mLoaderIsa, testData.mUsedByOtherApps,
+               testData.mPrimaryOrSplit, testData.mUsedBy);
+    }
+
+    private boolean record(PackageDexUsage packageDexUsage, TestData testData, Set<String> users) {
+        boolean result = true;
+        for (String user : users) {
+            result = result && packageDexUsage.record(testData.mPackageName, testData.mDexFile,
+                    testData.mOwnerUserId, testData.mLoaderIsa, testData.mUsedByOtherApps,
+                    testData.mPrimaryOrSplit, user);
+        }
+        return result;
     }
 
     private void writeAndReadBack() {
+        mPackageDexUsage = writeAndReadBack(mPackageDexUsage);
+    }
+
+    private PackageDexUsage writeAndReadBack(PackageDexUsage packageDexUsage) {
         try {
             StringWriter writer = new StringWriter();
-            mPackageDexUsage.write(writer);
+            packageDexUsage.write(writer);
 
-            mPackageDexUsage = new PackageDexUsage();
-            mPackageDexUsage.read(new StringReader(writer.toString()));
+            PackageDexUsage newPackageDexUsage = new PackageDexUsage();
+            newPackageDexUsage.read(new StringReader(writer.toString()));
+            return newPackageDexUsage;
         } catch (IOException e) {
             fail("Unexpected IOException: " + e.getMessage());
+            return null;
         }
     }
 
@@ -379,15 +450,17 @@
         private final String mLoaderIsa;
         private final boolean mUsedByOtherApps;
         private final boolean mPrimaryOrSplit;
+        private final String mUsedBy;
 
         private TestData(String packageName, String dexFile, int ownerUserId,
-                 String loaderIsa, boolean isUsedByOtherApps, boolean primaryOrSplit) {
+                 String loaderIsa, boolean isUsedByOtherApps, boolean primaryOrSplit, String usedBy) {
             mPackageName = packageName;
             mDexFile = dexFile;
             mOwnerUserId = ownerUserId;
             mLoaderIsa = loaderIsa;
             mUsedByOtherApps = isUsedByOtherApps;
             mPrimaryOrSplit = primaryOrSplit;
+            mUsedBy = usedBy;
         }
 
     }
diff --git a/services/tests/servicestests/src/com/android/server/timezone/PackageTrackerTest.java b/services/tests/servicestests/src/com/android/server/timezone/PackageTrackerTest.java
index a972e4f..38142d3 100644
--- a/services/tests/servicestests/src/com/android/server/timezone/PackageTrackerTest.java
+++ b/services/tests/servicestests/src/com/android/server/timezone/PackageTrackerTest.java
@@ -198,26 +198,25 @@
         mFakeIntentHelper.assertReliabilityTriggeringDisabled();
     }
 
-    // TODO(nfuller): Uncomment or delete when it's clear what will happen with http://b/35995024
-    // @Test
-    // public void trackingEnabled_updateAppNotPrivileged() throws Exception {
-    //     // Set up device configuration.
-    //     configureTrackingEnabled();
-    //     configureReliabilityConfigSettingsOk();
-    //     configureUpdateAppPackageNotPrivileged(UPDATE_APP_PACKAGE_NAME);
-    //     configureDataAppPackageOk(DATA_APP_PACKAGE_NAME);
-    //
-    //     try {
-    //         // Initialize the tracker.
-    //         mPackageTracker.start();
-    //         fail();
-    //     } catch (RuntimeException expected) {}
-    //
-    //     mFakeIntentHelper.assertNotInitialized();
-    //
-    //     // Check reliability triggering state.
-    //     mFakeIntentHelper.assertReliabilityTriggeringDisabled();
-    // }
+    @Test
+    public void trackingEnabled_updateAppNotPrivileged() throws Exception {
+        // Set up device configuration.
+        configureTrackingEnabled();
+        configureReliabilityConfigSettingsOk();
+        configureUpdateAppPackageNotPrivileged(UPDATE_APP_PACKAGE_NAME);
+        configureDataAppPackageOk(DATA_APP_PACKAGE_NAME);
+
+        try {
+            // Initialize the tracker.
+            mPackageTracker.start();
+            fail();
+        } catch (RuntimeException expected) {}
+
+        mFakeIntentHelper.assertNotInitialized();
+
+        // Check reliability triggering state.
+        mFakeIntentHelper.assertReliabilityTriggeringDisabled();
+    }
 
     @Test
     public void trackingEnabled_dataAppConfigMissing() throws Exception {
@@ -239,26 +238,25 @@
         mFakeIntentHelper.assertReliabilityTriggeringDisabled();
     }
 
-    // TODO(nfuller): Uncomment or delete when it's clear what will happen with http://b/35995024
-    // @Test
-    // public void trackingEnabled_dataAppNotPrivileged() throws Exception {
-    //     // Set up device configuration.
-    //     configureTrackingEnabled();
-    //     configureReliabilityConfigSettingsOk();
-    //     configureUpdateAppPackageOk(UPDATE_APP_PACKAGE_NAME);
-    //     configureDataAppPackageNotPrivileged(DATA_APP_PACKAGE_NAME);
-    //
-    //     try {
-    //         // Initialize the tracker.
-    //         mPackageTracker.start();
-    //         fail();
-    //     } catch (RuntimeException expected) {}
-    //
-    //     mFakeIntentHelper.assertNotInitialized();
-    //
-    //     // Check reliability triggering state.
-    //     mFakeIntentHelper.assertReliabilityTriggeringDisabled();
-    // }
+    @Test
+    public void trackingEnabled_dataAppNotPrivileged() throws Exception {
+        // Set up device configuration.
+        configureTrackingEnabled();
+        configureReliabilityConfigSettingsOk();
+        configureUpdateAppPackageOk(UPDATE_APP_PACKAGE_NAME);
+        configureDataAppPackageNotPrivileged(DATA_APP_PACKAGE_NAME);
+
+        try {
+            // Initialize the tracker.
+            mPackageTracker.start();
+            fail();
+        } catch (RuntimeException expected) {}
+
+        mFakeIntentHelper.assertNotInitialized();
+
+        // Check reliability triggering state.
+        mFakeIntentHelper.assertReliabilityTriggeringDisabled();
+     }
 
     @Test
     public void trackingEnabled_packageUpdate_badUpdateAppManifestEntry() throws Exception {
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 a4e56fc..0a7a5f2 100644
--- a/services/tests/servicestests/src/com/android/server/wm/TestWindowManagerPolicy.java
+++ b/services/tests/servicestests/src/com/android/server/wm/TestWindowManagerPolicy.java
@@ -17,6 +17,7 @@
 package com.android.server.wm;
 
 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING;
+import static android.view.WindowManagerPolicy.NAV_BAR_BOTTOM;
 import static org.mockito.Matchers.anyInt;
 import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.mock;
@@ -614,6 +615,11 @@
     }
 
     @Override
+    public int getNavBarPosition() {
+        return NAV_BAR_BOTTOM;
+    }
+
+    @Override
     public void getNonDecorInsetsLw(int displayRotation, int displayWidth, int displayHeight,
             Rect outInsets) {
 
diff --git a/services/usb/java/com/android/server/usb/UsbDeviceManager.java b/services/usb/java/com/android/server/usb/UsbDeviceManager.java
index a7180c9..a5797c0 100644
--- a/services/usb/java/com/android/server/usb/UsbDeviceManager.java
+++ b/services/usb/java/com/android/server/usb/UsbDeviceManager.java
@@ -1032,7 +1032,9 @@
                     if (DEBUG) {
                         Slog.v(TAG, "Accessory mode enter timeout: " + mConnected);
                     }
-                    if (!mConnected) {
+                    if (!mConnected || !UsbManager.containsFunction(
+                            mCurrentFunctions,
+                            UsbManager.USB_FUNCTION_ACCESSORY)) {
                         notifyAccessoryModeExit();
                     }
                     break;
diff --git a/services/usb/java/com/android/server/usb/descriptors/UsbDescriptorParser.java b/services/usb/java/com/android/server/usb/descriptors/UsbDescriptorParser.java
index 7c074da..303a577 100644
--- a/services/usb/java/com/android/server/usb/descriptors/UsbDescriptorParser.java
+++ b/services/usb/java/com/android/server/usb/descriptors/UsbDescriptorParser.java
@@ -118,27 +118,28 @@
     /**
      * @hide
      */
-    public boolean parseDescriptors(byte[] descriptors) {
-        try {
-            mDescriptors.clear();
+    public void parseDescriptors(byte[] descriptors) {
+        mDescriptors.clear();
 
-            ByteStream stream = new ByteStream(descriptors);
-            while (stream.available() > 0) {
-                UsbDescriptor descriptor = allocDescriptor(stream);
-                if (descriptor != null) {
-                    // Parse
+        ByteStream stream = new ByteStream(descriptors);
+        while (stream.available() > 0) {
+            UsbDescriptor descriptor = allocDescriptor(stream);
+            if (descriptor != null) {
+                // Parse
+                try {
                     descriptor.parseRawDescriptors(stream);
-                    mDescriptors.add(descriptor);
-
-                    // Clean up
-                    descriptor.postParse(stream);
+                } catch (Exception ex) {
+                    Log.e(TAG, "Exception parsing USB descriptors.", ex);
                 }
+
+                // Its OK to add the invalid descriptor as the postParse()
+                // routine will mark it as invalid.
+                mDescriptors.add(descriptor);
+
+                // Clean up
+                descriptor.postParse(stream);
             }
-            return true;
-        } catch (Exception ex) {
-            Log.e(TAG, "Exception parsing USB descriptors.", ex);
         }
-        return false;
     }
 
     /**
@@ -146,7 +147,11 @@
      */
     public boolean parseDevice(String deviceAddr) {
         byte[] rawDescriptors = getRawDescriptors(deviceAddr);
-        return rawDescriptors != null && parseDescriptors(rawDescriptors);
+        if (rawDescriptors != null) {
+            parseDescriptors(rawDescriptors);
+            return true;
+        }
+        return false;
     }
 
     private native byte[] getRawDescriptors(String deviceAddr);
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index 9c712f4..8b3a804 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -1066,6 +1066,13 @@
             "carrier_app_no_wake_signal_config";
 
     /**
+     * Default value for {@link Settings.Global#DATA_ROAMING}
+     * @hide
+     */
+    public static final String KEY_CARRIER_DEFAULT_DATA_ROAMING_ENABLED_BOOL =
+            "carrier_default_data_roaming_enabled_bool";
+
+    /**
      * Determines whether the carrier supports making non-emergency phone calls while the phone is
      * in emergency callback mode.  Default value is {@code true}, meaning that non-emergency calls
      * are allowed in emergency callback mode.
@@ -1495,6 +1502,17 @@
     public static final String IMSI_KEY_EXPIRATION_DAYS_TIME_INT =
             "imsi_key_expiration_days_time_int";
 
+    /**
+     * Key identifying if the CDMA Caller ID presentation and suppression MMI codes
+     * should be converted to 3GPP CLIR codes when a multimode (CDMA+UMTS+LTE) device is roaming
+     * on a 3GPP network. Specifically *67<number> will be converted to #31#<number> and
+     * *82<number> will be converted to *31#<number> before dialing a call when this key is
+     * set TRUE and device is roaming on a 3GPP network.
+     * @hide
+     */
+    public static final String KEY_CONVERT_CDMA_CALLER_ID_MMI_CODES_WHILE_ROAMING_ON_3GPP_BOOL =
+            "convert_cdma_caller_id_mmi_codes_while_roaming_on_3gpp_bool";
+
     /** The default value for every variable. */
     private final static PersistableBundle sDefaults;
 
@@ -1645,6 +1663,7 @@
         sDefaults.putBoolean(KEY_CARRIER_NAME_OVERRIDE_BOOL, false);
         sDefaults.putString(KEY_CARRIER_NAME_STRING, "");
         sDefaults.putBoolean(KEY_SUPPORT_DIRECT_FDN_DIALING_BOOL, false);
+        sDefaults.putBoolean(KEY_CARRIER_DEFAULT_DATA_ROAMING_ENABLED_BOOL, false);
 
         // MMS defaults
         sDefaults.putBoolean(KEY_MMS_ALIAS_ENABLED_BOOL, false);
@@ -1745,10 +1764,10 @@
         sDefaults.putBoolean(KEY_DISABLE_VOICE_BARRING_NOTIFICATION_BOOL, false);
         sDefaults.putInt(IMSI_KEY_EXPIRATION_DAYS_TIME_INT, IMSI_ENCRYPTION_DAYS_TIME_DISABLED);
         sDefaults.putString(IMSI_KEY_DOWNLOAD_URL_STRING, null);
+        sDefaults.putBoolean(KEY_CONVERT_CDMA_CALLER_ID_MMI_CODES_WHILE_ROAMING_ON_3GPP_BOOL,
+                false);
         sDefaults.putStringArray(KEY_NON_ROAMING_OPERATOR_STRING_ARRAY, null);
         sDefaults.putStringArray(KEY_ROAMING_OPERATOR_STRING_ARRAY, null);
-        sDefaults.putInt(IMSI_KEY_EXPIRATION_DAYS_TIME_INT, IMSI_ENCRYPTION_DAYS_TIME_DISABLED);
-        sDefaults.putString(IMSI_KEY_DOWNLOAD_URL_STRING, null);
     }
 
     /**
diff --git a/telephony/java/android/telephony/MbmsDownloadManager.java b/telephony/java/android/telephony/MbmsDownloadManager.java
index 231f2c8..4eeabb0 100644
--- a/telephony/java/android/telephony/MbmsDownloadManager.java
+++ b/telephony/java/android/telephony/MbmsDownloadManager.java
@@ -16,6 +16,7 @@
 
 package android.telephony;
 
+import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.content.ComponentName;
 import android.content.Context;
@@ -26,9 +27,9 @@
 import android.net.Uri;
 import android.os.IBinder;
 import android.os.RemoteException;
+import android.telephony.mbms.FileInfo;
 import android.telephony.mbms.IDownloadCallback;
 import android.telephony.mbms.DownloadRequest;
-import android.telephony.mbms.DownloadStatus;
 import android.telephony.mbms.IMbmsDownloadManagerCallback;
 import android.telephony.mbms.MbmsDownloadManagerCallback;
 import android.telephony.mbms.MbmsDownloadReceiver;
@@ -40,6 +41,8 @@
 
 import java.io.File;
 import java.io.IOException;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
 import java.util.List;
 import java.util.concurrent.atomic.AtomicReference;
 
@@ -192,6 +195,18 @@
     public static final int RESULT_EXPIRED    = 3;
     // TODO - more results!
 
+    /** @hide */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef({STATUS_UNKNOWN, STATUS_ACTIVELY_DOWNLOADING, STATUS_PENDING_DOWNLOAD,
+            STATUS_PENDING_REPAIR, STATUS_PENDING_DOWNLOAD_WINDOW})
+    public @interface DownloadStatus {}
+
+    public static final int STATUS_UNKNOWN = 0;
+    public static final int STATUS_ACTIVELY_DOWNLOADING = 1;
+    public static final int STATUS_PENDING_DOWNLOAD = 2;
+    public static final int STATUS_PENDING_REPAIR = 3;
+    public static final int STATUS_PENDING_DOWNLOAD_WINDOW = 4;
+
     private final Context mContext;
     private int mSubscriptionId = INVALID_SUBSCRIPTION_ID;
 
@@ -271,17 +286,19 @@
      * The serviceClasses argument lets the app filter on types of programming and is opaque data
      * negotiated beforehand between the app and the carrier.
      *
-     * Multiple calls replace the list of serviceClasses of interest.
-     *
      * This may throw an {@link MbmsException} containing one of the following errors:
      * {@link MbmsException#ERROR_MIDDLEWARE_NOT_BOUND}
-     * {@link MbmsException#ERROR_CONCURRENT_SERVICE_LIMIT_REACHED}
-     * {@link MbmsException#ERROR_SERVICE_LOST}
+     * {@link MbmsException#ERROR_MIDDLEWARE_LOST}
      *
      * Asynchronous error codes via the {@link MbmsDownloadManagerCallback#error(int, String)}
      * callback can include any of the errors except:
-     * {@link MbmsException#ERROR_UNABLE_TO_START_SERVICE}
-     * {@link MbmsException#ERROR_END_OF_SESSION}
+     * {@link MbmsException.StreamingErrors#ERROR_UNABLE_TO_START_SERVICE}
+     *
+     * @param classList A list of service classes which the app wishes to receive
+     *                  {@link IMbmsDownloadManagerCallback#fileServicesUpdated(List)} callbacks
+     *                  about. Subsequent calls to this method will replace this list of service
+     *                  classes (i.e. the middleware will no longer send updates for services
+     *                  matching classes only in the old list).
      */
     public void getFileServices(List<String> classList) throws MbmsException {
         IMbmsDownloadService downloadService = mService.get();
@@ -296,7 +313,7 @@
         } catch (RemoteException e) {
             Log.w(LOG_TAG, "Remote process died");
             mService.set(null);
-            throw new MbmsException(MbmsException.ERROR_SERVICE_LOST);
+            throw new MbmsException(MbmsException.ERROR_MIDDLEWARE_LOST);
         }
     }
 
@@ -312,9 +329,10 @@
      * will default to a directory formed by the concatenation of the app's files directory and
      * {@link android.telephony.mbms.MbmsTempFileProvider#DEFAULT_TOP_LEVEL_TEMP_DIRECTORY}.
      *
-     * This method may not be called while any download requests are still active. If this is
-     * the case, an {@link MbmsException} will be thrown with code
-     * {@link MbmsException#ERROR_CANNOT_CHANGE_TEMP_FILE_ROOT}
+     * Before calling this method, the app must cancel all of its pending
+     * {@link DownloadRequest}s via {@link #cancelDownload(DownloadRequest)}. If this is not done,
+     * an {@link MbmsException} will be thrown with code
+     * {@link MbmsException.DownloadErrors#ERROR_CANNOT_CHANGE_TEMP_FILE_ROOT}
      *
      * The {@link File} supplied as a root temp file directory must already exist. If not, an
      * {@link IllegalArgumentException} will be thrown.
@@ -346,7 +364,7 @@
             }
         } catch (RemoteException e) {
             mService.set(null);
-            throw new MbmsException(MbmsException.ERROR_SERVICE_LOST);
+            throw new MbmsException(MbmsException.ERROR_MIDDLEWARE_LOST);
         }
 
         SharedPreferences prefs = mContext.getSharedPreferences(
@@ -397,29 +415,36 @@
             downloadService.download(request, callback);
         } catch (RemoteException e) {
             mService.set(null);
-            throw new MbmsException(MbmsException.ERROR_SERVICE_LOST);
+            throw new MbmsException(MbmsException.ERROR_MIDDLEWARE_LOST);
         }
     }
 
     /**
-     * Returns a list DownloadRequests that originated from this application (UID).
-     *
-     * May throw a RemoteException.
-     *
-     * Asynchronous errors through the listener include any of the errors except
-     * <li>ERROR_UNABLED_TO_START_SERVICE</li>
-     * <li>ERROR_MSDC_INVALID_SERVICE_ID</li>
-     * <li>ERROR_MSDC_END_OF_SESSION</li>
+     * Returns a list of pending {@link DownloadRequest}s that originated from this application.
+     * A pending request is one that was issued via
+     * {@link #download(DownloadRequest, IDownloadCallback)} but not cancelled through
+     * {@link #cancelDownload(DownloadRequest)}.
+     * @return A list, possibly empty, of {@link DownloadRequest}s
      */
-    public List<DownloadRequest> listPendingDownloads() {
-        return null;
+    public @NonNull List<DownloadRequest> listPendingDownloads() throws MbmsException {
+        IMbmsDownloadService downloadService = mService.get();
+        if (downloadService == null) {
+            throw new MbmsException(MbmsException.ERROR_MIDDLEWARE_NOT_BOUND);
+        }
+
+        try {
+            return downloadService.listPendingDownloads(mSubscriptionId);
+        } catch (RemoteException e) {
+            mService.set(null);
+            throw new MbmsException(MbmsException.ERROR_MIDDLEWARE_LOST);
+        }
     }
 
     /**
      * Attempts to cancel the specified {@link DownloadRequest}.
      *
      * If the middleware is not aware of the specified download request, an MbmsException will be
-     * thrown with error code {@link MbmsException#ERROR_UNKNOWN_DOWNLOAD_REQUEST}.
+     * thrown with error code {@link MbmsException.DownloadErrors#ERROR_UNKNOWN_DOWNLOAD_REQUEST}.
      *
      * If this method returns without throwing an exception, you may assume that cancellation
      * was successful.
@@ -438,45 +463,71 @@
             }
         } catch (RemoteException e) {
             mService.set(null);
-            throw new MbmsException(MbmsException.ERROR_SERVICE_LOST);
+            throw new MbmsException(MbmsException.ERROR_MIDDLEWARE_LOST);
         }
         deleteDownloadRequestToken(downloadRequest);
     }
 
     /**
-     * Gets information about current and known upcoming downloads.
+     * Gets information about the status of a file pending download.
      *
-     * Current is a straightforward count of the files being downloaded "now"
-     * for some definition of now (may be racey).
-     * Future downloads include counts of files with pending repair operations, counts of
-     * files with future downloads and indication of scheduled download times with unknown
-     * file details.
+     * If the middleware has not yet been properly initialized or if it has no records of the
+     * file indicated by {@code fileInfo} being associated with {@code downloadRequest},
+     * {@link #STATUS_UNKNOWN} will be returned.
      *
-     * May throw an IllegalArgumentException or RemoteException.
-     *
-     * If the DownloadRequest is unknown the results will be null.
+     * @param downloadRequest The download request to query.
+     * @param fileInfo The particular file within the request to get information on.
+     * @return The status of the download.
      */
-    public DownloadStatus getDownloadStatus(DownloadRequest downloadRequest) {
-        return null;
+    @DownloadStatus
+    public int getDownloadStatus(DownloadRequest downloadRequest, FileInfo fileInfo)
+            throws MbmsException {
+        IMbmsDownloadService downloadService = mService.get();
+        if (downloadService == null) {
+            throw new MbmsException(MbmsException.ERROR_MIDDLEWARE_NOT_BOUND);
+        }
+
+        try {
+            return downloadService.getDownloadStatus(downloadRequest, fileInfo);
+        } catch (RemoteException e) {
+            mService.set(null);
+            throw new MbmsException(MbmsException.ERROR_MIDDLEWARE_LOST);
+        }
     }
 
     /**
-     * Resets middleware knowledge regarding this download request.
+     * Resets the middleware's knowledge of previously-downloaded files in this download request.
      *
-     * This state consists of knowledge of what files have already been downloaded.
-     * Normally the middleware won't download files who's hash matches previously downloaded
-     * content, even if that content has since been deleted.  If this function is called
-     * repeated content will be downloaded again when available.  This does not interrupt
-     * in-progress downloads.
+     * Normally, the middleware keeps track of the hashes of downloaded files and won't re-download
+     * files whose server-reported hash matches one of the already-downloaded files. This means
+     * that if the file is accidentally deleted by the user or by the app, the middleware will
+     * not try to download it again.
+     * This method will reset the middleware's cache of hashes for the provided
+     * {@link DownloadRequest}, so that previously downloaded content will be downloaded again
+     * when available.
+     * This will not interrupt in-progress downloads.
      *
-     * May throw an IllegalArgumentException or RemoteException.
+     * If the middleware is not aware of the specified download request, an MbmsException will be
+     * thrown with error code {@link MbmsException.DownloadErrors#ERROR_UNKNOWN_DOWNLOAD_REQUEST}.
      *
-     * <li>SUCCESS</li>
-     * <li>ERROR_MSDC_CONCURRENT_SERVICE_LIMIT_REACHED</li>
-     * <li>ERROR_MSDC_UNKNOWN_REQUEST</li>
+     * May throw a {@link MbmsException} with error code
+     * @param downloadRequest The request to re-download files for.
      */
-    public int resetDownloadKnowledge(DownloadRequest downloadRequest) {
-        return 0;
+    public void resetDownloadKnowledge(DownloadRequest downloadRequest) throws MbmsException {
+        IMbmsDownloadService downloadService = mService.get();
+        if (downloadService == null) {
+            throw new MbmsException(MbmsException.ERROR_MIDDLEWARE_NOT_BOUND);
+        }
+
+        try {
+            int result = downloadService.resetDownloadKnowledge(downloadRequest);
+            if (result != MbmsException.SUCCESS) {
+                throw new MbmsException(result);
+            }
+        } catch (RemoteException e) {
+            mService.set(null);
+            throw new MbmsException(MbmsException.ERROR_MIDDLEWARE_LOST);
+        }
     }
 
     public void dispose() {
diff --git a/telephony/java/android/telephony/MbmsStreamingManager.java b/telephony/java/android/telephony/MbmsStreamingManager.java
index 8cc447e..5b3503a 100644
--- a/telephony/java/android/telephony/MbmsStreamingManager.java
+++ b/telephony/java/android/telephony/MbmsStreamingManager.java
@@ -35,7 +35,10 @@
 
 import static android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID;
 
-/** @hide */
+/**
+ * This class provides functionality for streaming media over MBMS.
+ * @hide
+ */
 public class MbmsStreamingManager {
     private static final String LOG_TAG = "MbmsStreamingManager";
     public static final String MBMS_STREAMING_SERVICE_ACTION =
@@ -88,6 +91,8 @@
     /**
      * Terminates this instance, ending calls to the registered listener.  Also terminates
      * any streaming services spawned from this instance.
+     *
+     * May throw an {@link IllegalStateException}
      */
     public void dispose() {
         IMbmsStreamingService streamingService = mService.get();
@@ -111,15 +116,15 @@
      *
      * Multiple calls replace the list of serviceClasses of interest.
      *
-     * This may throw an {@link MbmsException} containing one of the following errors:
-     * {@link MbmsException#ERROR_MIDDLEWARE_NOT_BOUND}
-     * {@link MbmsException#ERROR_CONCURRENT_SERVICE_LIMIT_REACHED}
-     * {@link MbmsException#ERROR_SERVICE_LOST}
+     * This may throw an {@link MbmsException} containing any error in
+     * {@link android.telephony.mbms.MbmsException.GeneralErrors},
+     * {@link MbmsException#ERROR_MIDDLEWARE_NOT_BOUND}, or
+     * {@link MbmsException#ERROR_MIDDLEWARE_LOST}.
      *
-     * Asynchronous error codes via the {@link MbmsStreamingManagerCallback#error(int, String)}
-     * callback can include any of the errors except:
-     * {@link MbmsException#ERROR_UNABLE_TO_START_SERVICE}
-     * {@link MbmsException#ERROR_END_OF_SESSION}
+     * May also throw an unchecked {@link IllegalArgumentException} or an
+     * {@link IllegalStateException}
+     *
+     * @param classList A list of streaming service classes that the app would like updates on.
      */
     public void getStreamingServices(List<String> classList) throws MbmsException {
         IMbmsStreamingService streamingService = mService.get();
@@ -134,7 +139,7 @@
         } catch (RemoteException e) {
             Log.w(LOG_TAG, "Remote process died");
             mService.set(null);
-            throw new MbmsException(MbmsException.ERROR_SERVICE_LOST);
+            throw new MbmsException(MbmsException.ERROR_MIDDLEWARE_LOST);
         }
     }
 
@@ -145,14 +150,21 @@
      * reported via
      * {@link android.telephony.mbms.StreamingServiceCallback#streamStateUpdated(int, int)}
      *
-     * May throw an {@link MbmsException} containing any of the following error codes:
-     * {@link MbmsException#ERROR_MIDDLEWARE_NOT_BOUND}
-     * {@link MbmsException#ERROR_CONCURRENT_SERVICE_LIMIT_REACHED}
-     * {@link MbmsException#ERROR_SERVICE_LOST}
+     * May throw an
+     * {@link MbmsException} containing any of the error codes in
+     * {@link android.telephony.mbms.MbmsException.GeneralErrors},
+     * {@link MbmsException#ERROR_MIDDLEWARE_NOT_BOUND}, or
+     * {@link MbmsException#ERROR_MIDDLEWARE_LOST}.
      *
      * May also throw an {@link IllegalArgumentException} or an {@link IllegalStateException}
      *
-     * Asynchronous errors through the listener include any of the errors
+     * Asynchronous errors through the listener include any of the errors in
+     * {@link android.telephony.mbms.MbmsException.GeneralErrors} or
+     * {@link android.telephony.mbms.MbmsException.StreamingErrors}.
+     *
+     * @param serviceInfo The information about the service to stream.
+     * @param listener A listener that'll be called when something about the stream changes.
+     * @return An instance of {@link StreamingService} through which the stream can be controlled.
      */
     public StreamingService startStreaming(StreamingServiceInfo serviceInfo,
             StreamingServiceCallback listener) throws MbmsException {
@@ -170,7 +182,7 @@
         } catch (RemoteException e) {
             Log.w(LOG_TAG, "Remote process died");
             mService.set(null);
-            throw new MbmsException(MbmsException.ERROR_SERVICE_LOST);
+            throw new MbmsException(MbmsException.ERROR_MIDDLEWARE_LOST);
         }
 
         return new StreamingService(mSubscriptionId, streamingService, serviceInfo, listener);
diff --git a/telephony/java/android/telephony/SubscriptionManager.java b/telephony/java/android/telephony/SubscriptionManager.java
index 0d1764b..89c9134 100644
--- a/telephony/java/android/telephony/SubscriptionManager.java
+++ b/telephony/java/android/telephony/SubscriptionManager.java
@@ -24,6 +24,7 @@
 import android.content.Intent;
 import android.content.res.Configuration;
 import android.content.res.Resources;
+import android.net.INetworkPolicyManager;
 import android.net.Uri;
 import android.os.Handler;
 import android.os.Message;
@@ -37,6 +38,7 @@
 import com.android.internal.telephony.PhoneConstants;
 
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.List;
 
 /**
@@ -1539,4 +1541,39 @@
         }
         return false;
     }
+
+    /** {@pending} */
+    public @NonNull List<SubscriptionPlan> getSubscriptionPlans(int subId) {
+        final INetworkPolicyManager npm = INetworkPolicyManager.Stub
+                .asInterface(ServiceManager.getService(Context.NETWORK_POLICY_SERVICE));
+        try {
+            return Arrays.asList(npm.getSubscriptionPlans(subId,
+                    mContext.getOpPackageName()));
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /** {@pending} */
+    public void setSubscriptionPlans(int subId, @NonNull List<SubscriptionPlan> plans) {
+        final INetworkPolicyManager npm = INetworkPolicyManager.Stub
+                .asInterface(ServiceManager.getService(Context.NETWORK_POLICY_SERVICE));
+        try {
+            npm.setSubscriptionPlans(subId, plans.toArray(new SubscriptionPlan[plans.size()]),
+                    mContext.getOpPackageName());
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /** {@hide} */
+    public String getSubscriptionPlanOwner(int subId) {
+        final INetworkPolicyManager npm = INetworkPolicyManager.Stub
+                .asInterface(ServiceManager.getService(Context.NETWORK_POLICY_SERVICE));
+        try {
+            return npm.getSubscriptionPlanOwner(subId);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
 }
diff --git a/telephony/java/android/telephony/SubscriptionPlan.aidl b/telephony/java/android/telephony/SubscriptionPlan.aidl
new file mode 100755
index 0000000..655df3a
--- /dev/null
+++ b/telephony/java/android/telephony/SubscriptionPlan.aidl
@@ -0,0 +1,19 @@
+/*
+ * 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.telephony;
+
+parcelable SubscriptionPlan;
diff --git a/telephony/java/android/telephony/SubscriptionPlan.java b/telephony/java/android/telephony/SubscriptionPlan.java
new file mode 100644
index 0000000..da7661ae
--- /dev/null
+++ b/telephony/java/android/telephony/SubscriptionPlan.java
@@ -0,0 +1,478 @@
+/*
+ * 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.telephony;
+
+import android.annotation.BytesLong;
+import android.annotation.CurrentTimeMillisLong;
+import android.annotation.IntDef;
+import android.annotation.Nullable;
+import android.net.NetworkPolicy;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.util.Log;
+import android.util.Pair;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.time.Instant;
+import java.time.LocalTime;
+import java.time.ZoneId;
+import java.time.ZonedDateTime;
+import java.time.temporal.ChronoUnit;
+import java.time.temporal.TemporalUnit;
+import java.util.Iterator;
+
+/** {@pending} */
+public final class SubscriptionPlan implements Parcelable {
+    private static final String TAG = "SubscriptionPlan";
+    private static final boolean DEBUG = false;
+
+    /** {@hide} */
+    @IntDef(prefix = "TYPE_", value = {
+            TYPE_NONRECURRING,
+            TYPE_RECURRING_WEEKLY,
+            TYPE_RECURRING_MONTHLY,
+            TYPE_RECURRING_DAILY,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface Type {}
+
+    public static final int TYPE_NONRECURRING = 0;
+    public static final int TYPE_RECURRING_MONTHLY = 1;
+    public static final int TYPE_RECURRING_WEEKLY = 2;
+    public static final int TYPE_RECURRING_DAILY = 3;
+
+    /** {@hide} */
+    @IntDef(prefix = "LIMIT_BEHAVIOR_", value = {
+            LIMIT_BEHAVIOR_UNKNOWN,
+            LIMIT_BEHAVIOR_DISABLED,
+            LIMIT_BEHAVIOR_BILLED,
+            LIMIT_BEHAVIOR_THROTTLED,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface LimitBehavior {}
+
+    public static final int LIMIT_BEHAVIOR_UNKNOWN = -1;
+    public static final int LIMIT_BEHAVIOR_DISABLED = 0;
+    public static final int LIMIT_BEHAVIOR_BILLED = 1;
+    public static final int LIMIT_BEHAVIOR_THROTTLED = 2;
+
+    public static final long BYTES_UNKNOWN = -1;
+    public static final long TIME_UNKNOWN = -1;
+
+    private final int type;
+    private final ZonedDateTime start;
+    private final ZonedDateTime end;
+    private CharSequence title;
+    private CharSequence summary;
+    private long dataWarningBytes = BYTES_UNKNOWN;
+    private long dataWarningSnoozeTime = TIME_UNKNOWN;
+    private long dataLimitBytes = BYTES_UNKNOWN;
+    private long dataLimitSnoozeTime = TIME_UNKNOWN;
+    private int dataLimitBehavior = LIMIT_BEHAVIOR_UNKNOWN;
+    private long dataUsageBytes = BYTES_UNKNOWN;
+    private long dataUsageTime = TIME_UNKNOWN;
+
+    private SubscriptionPlan(@Type int type, ZonedDateTime start, ZonedDateTime end) {
+        this.type = type;
+        this.start = start;
+        this.end = end;
+    }
+
+    private SubscriptionPlan(Parcel source) {
+        type = source.readInt();
+        if (source.readInt() != 0) {
+            start = ZonedDateTime.parse(source.readString());
+        } else {
+            start = null;
+        }
+        if (source.readInt() != 0) {
+            end = ZonedDateTime.parse(source.readString());
+        } else {
+            end = null;
+        }
+        title = source.readCharSequence();
+        summary = source.readCharSequence();
+        dataWarningBytes = source.readLong();
+        dataWarningSnoozeTime = source.readLong();
+        dataLimitBytes = source.readLong();
+        dataLimitSnoozeTime = source.readLong();
+        dataLimitBehavior = source.readInt();
+        dataUsageBytes = source.readLong();
+        dataUsageTime = source.readLong();
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeInt(type);
+        if (start != null) {
+            dest.writeInt(1);
+            dest.writeString(start.toString());
+        } else {
+            dest.writeInt(0);
+        }
+        if (end != null) {
+            dest.writeInt(1);
+            dest.writeString(end.toString());
+        } else {
+            dest.writeInt(0);
+        }
+        dest.writeCharSequence(title);
+        dest.writeCharSequence(summary);
+        dest.writeLong(dataWarningBytes);
+        dest.writeLong(dataWarningSnoozeTime);
+        dest.writeLong(dataLimitBytes);
+        dest.writeLong(dataLimitSnoozeTime);
+        dest.writeInt(dataLimitBehavior);
+        dest.writeLong(dataUsageBytes);
+        dest.writeLong(dataUsageTime);
+    }
+
+    @Override
+    public String toString() {
+        return new StringBuilder("SubscriptionPlan:")
+                .append(" type=").append(type)
+                .append(" start=").append(start)
+                .append(" end=").append(end)
+                .append(" title=").append(title)
+                .append(" summary=").append(summary)
+                .append(" dataWarningBytes=").append(dataWarningBytes)
+                .append(" dataWarningSnoozeTime=").append(dataWarningSnoozeTime)
+                .append(" dataLimitBytes=").append(dataLimitBytes)
+                .append(" dataLimitSnoozeTime=").append(dataLimitSnoozeTime)
+                .append(" dataLimitBehavior=").append(dataLimitBehavior)
+                .append(" dataUsageBytes=").append(dataUsageBytes)
+                .append(" dataUsageTime=").append(dataUsageTime)
+                .toString();
+    }
+
+    public static final Parcelable.Creator<SubscriptionPlan> CREATOR = new Parcelable.Creator<SubscriptionPlan>() {
+        @Override
+        public SubscriptionPlan createFromParcel(Parcel source) {
+            return new SubscriptionPlan(source);
+        }
+
+        @Override
+        public SubscriptionPlan[] newArray(int size) {
+            return new SubscriptionPlan[size];
+        }
+    };
+
+    public @Type int getType() {
+        return type;
+    }
+
+    public ZonedDateTime getStart() {
+        return start;
+    }
+
+    public ZonedDateTime getEnd() {
+        return end;
+    }
+
+    public @Nullable CharSequence getTitle() {
+        return title;
+    }
+
+    public @Nullable CharSequence getSummary() {
+        return summary;
+    }
+
+    public @BytesLong long getDataWarningBytes() {
+        return dataWarningBytes;
+    }
+
+    public @BytesLong long getDataLimitBytes() {
+        return dataLimitBytes;
+    }
+
+    public @LimitBehavior int getDataLimitBehavior() {
+        return dataLimitBehavior;
+    }
+
+    public @BytesLong long getDataUsageBytes() {
+        return dataUsageBytes;
+    }
+
+    public @CurrentTimeMillisLong long getDataUsageTime() {
+        return dataUsageTime;
+    }
+
+    /** {@hide} */
+    @VisibleForTesting
+    public static long sNowOverride = -1;
+
+    private static ZonedDateTime now(ZoneId zone) {
+        return (sNowOverride != -1)
+                ? ZonedDateTime.ofInstant(Instant.ofEpochMilli(sNowOverride), zone)
+                : ZonedDateTime.now(zone);
+    }
+
+    /** {@hide} */
+    public static SubscriptionPlan convert(NetworkPolicy policy) {
+        final ZoneId zone = ZoneId.of(policy.cycleTimezone);
+        final ZonedDateTime now = now(zone);
+        final Builder builder;
+        if (policy.cycleDay != NetworkPolicy.CYCLE_NONE) {
+            // Assume we started last January, since it has all possible days
+            ZonedDateTime start = ZonedDateTime.of(
+                    now.toLocalDate().minusYears(1).withMonth(1).withDayOfMonth(policy.cycleDay),
+                    LocalTime.MIDNIGHT, zone);
+            builder = Builder.createRecurringMonthly(start);
+        } else {
+            Log.w(TAG, "Cycle not defined; assuming last 4 weeks non-recurring");
+            ZonedDateTime end = now;
+            ZonedDateTime start = end.minusWeeks(4);
+            builder = Builder.createNonrecurring(start, end);
+        }
+        if (policy.warningBytes != NetworkPolicy.WARNING_DISABLED) {
+            builder.setDataWarning(policy.warningBytes);
+        }
+        if (policy.lastWarningSnooze != NetworkPolicy.SNOOZE_NEVER) {
+            builder.setDataWarningSnooze(policy.lastWarningSnooze);
+        }
+        if (policy.limitBytes != NetworkPolicy.LIMIT_DISABLED) {
+            builder.setDataLimit(policy.limitBytes, LIMIT_BEHAVIOR_DISABLED);
+        }
+        if (policy.lastLimitSnooze != NetworkPolicy.SNOOZE_NEVER) {
+            builder.setDataLimitSnooze(policy.lastLimitSnooze);
+        }
+        return builder.build();
+    }
+
+    /** {@hide} */
+    public static NetworkPolicy convert(SubscriptionPlan plan) {
+        final NetworkPolicy policy = new NetworkPolicy();
+        switch (plan.type) {
+            case TYPE_RECURRING_MONTHLY:
+                policy.cycleDay = plan.start.getDayOfMonth();
+                policy.cycleTimezone = plan.start.getZone().getId();
+                break;
+            default:
+                policy.cycleDay = NetworkPolicy.CYCLE_NONE;
+                policy.cycleTimezone = "UTC";
+                break;
+        }
+        policy.warningBytes = plan.dataWarningBytes;
+        policy.limitBytes = plan.dataLimitBytes;
+        policy.lastWarningSnooze = plan.dataWarningSnoozeTime;
+        policy.lastLimitSnooze = plan.dataLimitSnoozeTime;
+        policy.metered = true;
+        policy.inferred = false;
+        return policy;
+    }
+
+    /** {@hide} */
+    public TemporalUnit getTemporalUnit() {
+        switch (type) {
+            case TYPE_RECURRING_DAILY: return ChronoUnit.DAYS;
+            case TYPE_RECURRING_WEEKLY: return ChronoUnit.WEEKS;
+            case TYPE_RECURRING_MONTHLY: return ChronoUnit.MONTHS;
+            default: throw new IllegalArgumentException();
+        }
+    }
+
+    /**
+     * Return an iterator that returns data usage cycles.
+     * <p>
+     * For recurring plans, it starts at the currently active cycle, and then
+     * walks backwards in time through each previous cycle, back to the defined
+     * starting point and no further.
+     * <p>
+     * For non-recurring plans, it returns one single cycle.
+     */
+    public Iterator<Pair<ZonedDateTime, ZonedDateTime>> cycleIterator() {
+        switch (type) {
+            case TYPE_NONRECURRING:
+                return new NonrecurringIterator();
+            case TYPE_RECURRING_WEEKLY:
+            case TYPE_RECURRING_MONTHLY:
+            case TYPE_RECURRING_DAILY:
+                return new RecurringIterator();
+            default:
+                throw new IllegalStateException("Unknown type: " + type);
+        }
+    }
+
+    private class NonrecurringIterator implements Iterator<Pair<ZonedDateTime, ZonedDateTime>> {
+        boolean hasNext = true;
+
+        @Override
+        public boolean hasNext() {
+            return hasNext;
+        }
+
+        @Override
+        public Pair<ZonedDateTime, ZonedDateTime> next() {
+            hasNext = false;
+            return new Pair<>(start, end);
+        }
+    }
+
+    private class RecurringIterator implements Iterator<Pair<ZonedDateTime, ZonedDateTime>> {
+        TemporalUnit unit;
+        long i;
+        ZonedDateTime cycleStart;
+        ZonedDateTime cycleEnd;
+
+        public RecurringIterator() {
+            final ZonedDateTime now = now(start.getZone());
+            if (DEBUG) Log.d(TAG, "Resolving using now " + now);
+
+            unit = getTemporalUnit();
+            i = unit.between(start, now);
+            updateCycle();
+
+            // Walk forwards until we find first cycle after now
+            while (cycleEnd.toEpochSecond() <= now.toEpochSecond()) {
+                i++;
+                updateCycle();
+            }
+
+            // Walk backwards until we find first cycle before now
+            while (cycleStart.toEpochSecond() > now.toEpochSecond()) {
+                i--;
+                updateCycle();
+            }
+        }
+
+        private void updateCycle() {
+            cycleStart = roundBoundaryTime(start.plus(i, unit));
+            cycleEnd = roundBoundaryTime(start.plus(i + 1, unit));
+        }
+
+        private ZonedDateTime roundBoundaryTime(ZonedDateTime boundary) {
+            if ((type == TYPE_RECURRING_MONTHLY)
+                    && (boundary.getDayOfMonth() < start.getDayOfMonth())) {
+                // When forced to end a monthly cycle early, we want to count
+                // that entire day against the boundary.
+                return ZonedDateTime.of(boundary.toLocalDate(), LocalTime.MAX, start.getZone());
+            } else {
+                return boundary;
+            }
+        }
+
+        @Override
+        public boolean hasNext() {
+            return cycleStart.toEpochSecond() >= start.toEpochSecond();
+        }
+
+        @Override
+        public Pair<ZonedDateTime, ZonedDateTime> next() {
+            if (DEBUG) Log.d(TAG, "Cycle " + i + " from " + cycleStart + " to " + cycleEnd);
+            Pair<ZonedDateTime, ZonedDateTime> p = new Pair<>(cycleStart, cycleEnd);
+            i--;
+            updateCycle();
+            return p;
+        }
+    }
+
+    public static class Builder {
+        private final SubscriptionPlan plan;
+
+        private Builder(@Type int type, ZonedDateTime start, ZonedDateTime end) {
+            plan = new SubscriptionPlan(type, start, end);
+        }
+
+        public static Builder createNonrecurring(ZonedDateTime start, ZonedDateTime end) {
+            if (!end.isAfter(start)) {
+                throw new IllegalArgumentException(
+                        "End " + end + " isn't after start " + start);
+            }
+            return new Builder(TYPE_NONRECURRING, start, end);
+        }
+
+        public static Builder createRecurringMonthly(ZonedDateTime start) {
+            return new Builder(TYPE_RECURRING_MONTHLY, start, null);
+        }
+
+        public static Builder createRecurringWeekly(ZonedDateTime start) {
+            return new Builder(TYPE_RECURRING_WEEKLY, start, null);
+        }
+
+        public static Builder createRecurringDaily(ZonedDateTime start) {
+            return new Builder(TYPE_RECURRING_DAILY, start, null);
+        }
+
+        public SubscriptionPlan build() {
+            return plan;
+        }
+
+        public Builder setTitle(@Nullable CharSequence title) {
+            plan.title = title;
+            return this;
+        }
+
+        public Builder setSummary(@Nullable CharSequence summary) {
+            plan.summary = summary;
+            return this;
+        }
+
+        public Builder setDataWarning(@BytesLong long dataWarningBytes) {
+            if (dataWarningBytes < BYTES_UNKNOWN) {
+                throw new IllegalArgumentException("Warning must be positive or BYTES_UNKNOWN");
+            }
+            plan.dataWarningBytes = dataWarningBytes;
+            return this;
+        }
+
+        /** {@hide} */
+        public Builder setDataWarningSnooze(@CurrentTimeMillisLong long dataWarningSnoozeTime) {
+            plan.dataWarningSnoozeTime = dataWarningSnoozeTime;
+            return this;
+        }
+
+        public Builder setDataLimit(@BytesLong long dataLimitBytes,
+                @LimitBehavior int dataLimitBehavior) {
+            if (dataLimitBytes < BYTES_UNKNOWN) {
+                throw new IllegalArgumentException("Limit must be positive or BYTES_UNKNOWN");
+            }
+            plan.dataLimitBytes = dataLimitBytes;
+            plan.dataLimitBehavior = dataLimitBehavior;
+            return this;
+        }
+
+        /** {@hide} */
+        public Builder setDataLimitSnooze(@CurrentTimeMillisLong long dataLimitSnoozeTime) {
+            plan.dataLimitSnoozeTime = dataLimitSnoozeTime;
+            return this;
+        }
+
+        public Builder setDataUsage(@BytesLong long dataUsageBytes,
+                @CurrentTimeMillisLong long dataUsageTime) {
+            if (dataUsageBytes < BYTES_UNKNOWN) {
+                throw new IllegalArgumentException("Usage must be positive or BYTES_UNKNOWN");
+            }
+            if (dataUsageTime < TIME_UNKNOWN) {
+                throw new IllegalArgumentException("Time must be positive or TIME_UNKNOWN");
+            }
+            if ((dataUsageBytes == BYTES_UNKNOWN) != (dataUsageTime == TIME_UNKNOWN)) {
+                throw new IllegalArgumentException("Must provide both usage and time or neither");
+            }
+            plan.dataUsageBytes = dataUsageBytes;
+            plan.dataUsageTime = dataUsageTime;
+            return this;
+        }
+    }
+}
diff --git a/telephony/java/android/telephony/mbms/DownloadStatus.aidl b/telephony/java/android/telephony/mbms/DownloadStatus.aidl
deleted file mode 100755
index e7cfd39..0000000
--- a/telephony/java/android/telephony/mbms/DownloadStatus.aidl
+++ /dev/null
@@ -1,19 +0,0 @@
-/*
-** 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.
-*/
-
-package android.telephony.mbms;
-
-parcelable DownloadStatus;
diff --git a/telephony/java/android/telephony/mbms/DownloadStatus.java b/telephony/java/android/telephony/mbms/DownloadStatus.java
deleted file mode 100644
index 90eb53f..0000000
--- a/telephony/java/android/telephony/mbms/DownloadStatus.java
+++ /dev/null
@@ -1,77 +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.telephony.mbms;
-
-import android.os.Parcel;
-import android.os.Parcelable;
-
-/**
- * A Parcelable class describing the status of a Cell-Broadcast download request
- * @hide
- */
-public class DownloadStatus implements Parcelable {
-    // includes downloads and active repair work
-    public final int activelyDownloading;
-
-    // files scheduled for future broadcast
-    public final int pendingDownloads;
-
-    // files scheduled for future repairs
-    public final int pendingRepairs;
-
-    // is a future download window scheduled with unknown
-    // number of files
-    public final boolean windowPending;
-
-    public DownloadStatus(int downloading, int downloads, int repairs, boolean window) {
-        activelyDownloading = downloading;
-        pendingDownloads = downloads;
-        pendingRepairs = repairs;
-        windowPending = window;
-    }
-
-    public static final Parcelable.Creator<DownloadStatus> CREATOR =
-            new Parcelable.Creator<DownloadStatus>() {
-        @Override
-        public DownloadStatus createFromParcel(Parcel in) {
-            return new DownloadStatus(in);
-        }
-
-        @Override
-        public DownloadStatus[] newArray(int size) {
-            return new DownloadStatus[size];
-        }
-    };
-
-    DownloadStatus(Parcel in) {
-        activelyDownloading = in.readInt();
-        pendingDownloads = in.readInt();
-        pendingRepairs = in.readInt();
-        windowPending = (in.readInt() == 1);
-    }
-
-    public void writeToParcel(Parcel dest, int flags) {
-        dest.writeInt(activelyDownloading);
-        dest.writeInt(pendingDownloads);
-        dest.writeInt(pendingRepairs);
-        dest.writeInt((windowPending ? 1 : 0));
-    }
-
-    public int describeContents() {
-        return 0;
-    }
-}
diff --git a/telephony/java/android/telephony/mbms/IMbmsStreamingManagerCallback.aidl b/telephony/java/android/telephony/mbms/IMbmsStreamingManagerCallback.aidl
index 8116a7f..007aee7 100755
--- a/telephony/java/android/telephony/mbms/IMbmsStreamingManagerCallback.aidl
+++ b/telephony/java/android/telephony/mbms/IMbmsStreamingManagerCallback.aidl
@@ -30,7 +30,5 @@
 
     void streamingServicesUpdated(in List<StreamingServiceInfo> services);
 
-    void activeStreamingServicesUpdated(in List<StreamingServiceInfo> services);
-
     void middlewareReady();
 }
diff --git a/telephony/java/android/telephony/mbms/MbmsDownloadManagerCallback.java b/telephony/java/android/telephony/mbms/MbmsDownloadManagerCallback.java
index 5b22199..ba25f66 100644
--- a/telephony/java/android/telephony/mbms/MbmsDownloadManagerCallback.java
+++ b/telephony/java/android/telephony/mbms/MbmsDownloadManagerCallback.java
@@ -55,7 +55,7 @@
      * Before this method is called, calling any method on an instance of
      * {@link android.telephony.MbmsDownloadManager} will result in an {@link MbmsException}
      * being thrown with error code {@link MbmsException#ERROR_MIDDLEWARE_NOT_BOUND}
-     * or {@link MbmsException#ERROR_MIDDLEWARE_NOT_YET_READY}
+     * or {@link MbmsException.GeneralErrors#ERROR_MIDDLEWARE_NOT_YET_READY}
      */
     @Override
     public void middlewareReady() {
diff --git a/telephony/java/android/telephony/mbms/MbmsException.java b/telephony/java/android/telephony/mbms/MbmsException.java
index e190623..8888119 100644
--- a/telephony/java/android/telephony/mbms/MbmsException.java
+++ b/telephony/java/android/telephony/mbms/MbmsException.java
@@ -18,27 +18,112 @@
 
 /** @hide */
 public class MbmsException extends Exception {
+    /** Indicates that the operation was successful. */
     public static final int SUCCESS = 0;
-    public static final int ERROR_NO_SERVICE_INSTALLED = 1;
-    public static final int ERROR_MULTIPLE_SERVICES_INSTALLED = 2;
-    public static final int ERROR_BIND_TIMEOUT_OR_FAILURE = 3;
-    public static final int ERROR_MIDDLEWARE_NOT_YET_READY = 4;
-    public static final int ERROR_ALREADY_INITIALIZED = 5;
-    public static final int ERROR_CONCURRENT_SERVICE_LIMIT_REACHED = 6;
-    public static final int ERROR_MIDDLEWARE_NOT_BOUND = 7;
-    public static final int ERROR_UNABLE_TO_START_SERVICE = 8;
-    public static final int ERROR_STREAM_ALREADY_STARTED = 9;
-    public static final int ERROR_END_OF_SESSION = 10;
-    public static final int ERROR_SERVICE_LOST = 11;
-    public static final int ERROR_APP_PERMISSIONS_NOT_GRANTED = 12;
-    public static final int ERROR_IN_E911 = 13;
-    public static final int ERROR_OUT_OF_MEMORY = 14;
-    public static final int ERROR_NOT_CONNECTED_TO_HOME_CARRIER_LTE = 15;
-    public static final int ERROR_UNABLE_TO_READ_SIM = 16;
-    public static final int ERROR_CARRIER_CHANGE_NOT_ALLOWED = 17;
-    public static final int ERROR_CANNOT_CHANGE_TEMP_FILE_ROOT = 18;
-    public static final int ERROR_UNKNOWN_DOWNLOAD_REQUEST = 19;
-    public static final int ERROR_UNABLE_TO_INITIALIZE = 20;
+
+    // Following errors are generated in the manager and should not be returned from the
+    // middleware
+    /**
+     * Indicates that either no MBMS middleware app is installed on the device or multiple
+     * middleware apps are installed on the device.
+     */
+    public static final int ERROR_NO_UNIQUE_MIDDLEWARE = 1;
+
+    /**
+     * Indicates that the app attempted to perform an operation on an instance of
+     * {@link android.telephony.MbmsDownloadManager} or
+     * {@link android.telephony.MbmsStreamingManager} without being bound to the middleware.
+     */
+    public static final int ERROR_MIDDLEWARE_NOT_BOUND = 2;
+
+    /** Indicates that the middleware has died and the requested operation was not completed.*/
+    public static final int ERROR_MIDDLEWARE_LOST = 3;
+
+    /**
+     * Indicates errors that may be generated during initialization by the
+     * middleware. They are applicable to both streaming and file-download use-cases.
+     */
+    public static class InitializationErrors {
+        /**
+         * Indicates that the app tried to create more than one instance each of
+         * {@link android.telephony.MbmsStreamingManager} or
+         * {@link android.telephony.MbmsDownloadManager}.
+         */
+        public static final int ERROR_DUPLICATE_INITIALIZE = 101;
+        /** Indicates that the app is not authorized to access media via MBMS.*/
+        public static final int ERROR_APP_PERMISSIONS_NOT_GRANTED = 102;
+        /** Indicates that the middleware was unable to initialize for this app. */
+        public static final int ERROR_UNABLE_TO_INITIALIZE = 103;
+    }
+
+    /**
+     * Indicates the errors that may occur at any point and are applicable to both
+     * streaming and file-download.
+     */
+    public static class GeneralErrors {
+        /**
+         * Indicates that the app attempted to perform an operation before receiving notification
+         * that the middleware is ready via {@link MbmsStreamingManagerCallback#middlewareReady()}
+         * or {@link MbmsDownloadManagerCallback#middlewareReady()}.
+         */
+        public static final int ERROR_MIDDLEWARE_NOT_YET_READY = 201;
+        /**
+         * Indicates that the middleware ran out of memory and was unable to complete the requested
+         * operation.
+         */
+        public static final int ERROR_OUT_OF_MEMORY = 202;
+        /**
+         * Indicates that the requested operation failed due to the middleware being unavailable due
+         * to a transient condition. The app may retry the operation at a later time.
+         */
+        public static final int ERROR_MIDDLEWARE_TEMPORARILY_UNAVAILABLE = 203;
+        /**
+         * Indicates that the requested operation was not performed due to being in emergency
+         * callback mode.
+         */
+        public static final int ERROR_IN_E911 = 204;
+        /** Indicates that MBMS is not available due to the device being in roaming. */
+        public static final int ERROR_NOT_CONNECTED_TO_HOME_CARRIER_LTE = 205;
+        /** Indicates that MBMS is not available due to a SIM read error. */
+        public static final int ERROR_UNABLE_TO_READ_SIM = 206;
+        /**
+         * Indicates that MBMS is not available due to the inserted SIM being from an unsupported
+         * carrier.
+         */
+        public static final int ERROR_CARRIER_CHANGE_NOT_ALLOWED = 207;
+    }
+
+    /**
+     * Indicates the errors that are applicable only to the streaming use-case
+     */
+    public static class StreamingErrors {
+        /** Indicates that the middleware cannot start a stream due to too many ongoing streams */
+        public static final int ERROR_CONCURRENT_SERVICE_LIMIT_REACHED = 301;
+
+        /** Indicates that the middleware was unable to start the streaming service */
+        public static final int ERROR_UNABLE_TO_START_SERVICE = 302;
+
+        /**
+         * Indicates that the app called
+         * {@link android.telephony.MbmsStreamingManager#startStreaming(StreamingServiceInfo, StreamingServiceCallback)}
+         * more than once for the same {@link StreamingServiceInfo}.
+         */
+        public static final int ERROR_DUPLICATE_START_STREAM = 303;
+    }
+
+    /**
+     * Indicates the errors that are applicable only to the file-download use-case
+     */
+    public static class DownloadErrors {
+        /**
+         * Indicates that the app is not allowed to change the temp file root at this time due to
+         * outstanding download requests.
+         */
+        public static final int ERROR_CANNOT_CHANGE_TEMP_FILE_ROOT = 401;
+
+        /** Indicates that the middleware has no record of the supplied {@link DownloadRequest}. */
+        public static final int ERROR_UNKNOWN_DOWNLOAD_REQUEST = 402;
+    }
 
     private final int mErrorCode;
 
diff --git a/telephony/java/android/telephony/mbms/MbmsStreamingManagerCallback.java b/telephony/java/android/telephony/mbms/MbmsStreamingManagerCallback.java
index 27d9878..2e91be9 100644
--- a/telephony/java/android/telephony/mbms/MbmsStreamingManagerCallback.java
+++ b/telephony/java/android/telephony/mbms/MbmsStreamingManagerCallback.java
@@ -50,25 +50,12 @@
     }
 
     /**
-     * Called to indicate the active Streaming Services have changed.
-     *
-     * This will be caused whenever a new service starts streaming or whenever
-     * MbmsStreamServiceManager.getActiveStreamingServices is called.
-     *
-     * @param services a list of StreamingServiceInfos.  May be empty if
-     *                 there are no active StreamingServices
-     */
-    public void activeStreamingServicesUpdated(List<StreamingServiceInfo> services) {
-        // default implementation empty
-    }
-
-    /**
      * Called to indicate that the middleware has been initialized and is ready.
      *
      * Before this method is called, calling any method on an instance of
      * {@link android.telephony.MbmsStreamingManager} will result in an {@link MbmsException}
      * being thrown with error code {@link MbmsException#ERROR_MIDDLEWARE_NOT_BOUND}
-     * or {@link MbmsException#ERROR_MIDDLEWARE_NOT_YET_READY}
+     * or {@link MbmsException.GeneralErrors#ERROR_MIDDLEWARE_NOT_YET_READY}
      */
     @Override
     public void middlewareReady() {
diff --git a/telephony/java/android/telephony/mbms/MbmsUtils.java b/telephony/java/android/telephony/mbms/MbmsUtils.java
index 1e03fb9..4b913f8 100644
--- a/telephony/java/android/telephony/mbms/MbmsUtils.java
+++ b/telephony/java/android/telephony/mbms/MbmsUtils.java
@@ -22,14 +22,11 @@
 import android.content.ServiceConnection;
 import android.content.pm.*;
 import android.content.pm.ServiceInfo;
-import android.telephony.MbmsDownloadManager;
 import android.util.Log;
 
 import java.io.File;
 import java.io.IOException;
 import java.util.List;
-import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.TimeUnit;
 
 /**
  * @hide
@@ -78,7 +75,7 @@
                 MbmsUtils.getMiddlewareServiceInfo(context, serviceAction);
 
         if (mbmsServiceInfo == null) {
-            throw new MbmsException(MbmsException.ERROR_NO_SERVICE_INSTALLED);
+            throw new MbmsException(MbmsException.ERROR_NO_UNIQUE_MIDDLEWARE);
         }
 
         bindIntent.setComponent(MbmsUtils.toComponentName(mbmsServiceInfo));
diff --git a/telephony/java/android/telephony/mbms/StreamingService.java b/telephony/java/android/telephony/mbms/StreamingService.java
index 475c93a..1a64189 100644
--- a/telephony/java/android/telephony/mbms/StreamingService.java
+++ b/telephony/java/android/telephony/mbms/StreamingService.java
@@ -18,7 +18,6 @@
 
 import android.annotation.IntDef;
 import android.net.Uri;
-import android.os.DeadObjectException;
 import android.os.RemoteException;
 import android.telephony.mbms.vendor.IMbmsStreamingService;
 import android.util.Log;
@@ -50,15 +49,42 @@
      */
     @Retention(RetentionPolicy.SOURCE)
     @IntDef({REASON_BY_USER_REQUEST, REASON_END_OF_SESSION, REASON_FREQUENCY_CONFLICT,
-            REASON_OUT_OF_MEMORY, REASON_NOT_CONNECTED_TO_HOMECARRIER_LTE})
+            REASON_OUT_OF_MEMORY, REASON_NOT_CONNECTED_TO_HOMECARRIER_LTE,
+            REASON_LEFT_MBMS_BROADCAST_AREA})
     public @interface StreamingStateChangeReason {}
+
+    /**
+     * State changed due to a call to {@link #stopStreaming()} or
+     * {@link android.telephony.MbmsStreamingManager#startStreaming(StreamingServiceInfo, StreamingServiceCallback)}
+     */
     public static final int REASON_BY_USER_REQUEST = 1;
+
+    /**
+     * State changed due to the streaming session ending at the carrier.
+     */
     public static final int REASON_END_OF_SESSION = 2;
+
+    /**
+     * State changed due to a frequency conflict with another requested stream.
+     */
     public static final int REASON_FREQUENCY_CONFLICT = 3;
+
+    /**
+     * State changed due to the middleware running out of memory
+     */
     public static final int REASON_OUT_OF_MEMORY = 4;
+
+    /**
+     * State changed due to the device leaving the home carrier's LTE network.
+     */
     public static final int REASON_NOT_CONNECTED_TO_HOMECARRIER_LTE = 5;
 
     /**
+     * State changed due to the device leaving the where this stream is being broadcast.
+     */
+    public static final int REASON_LEFT_MBMS_BROADCAST_AREA = 5;
+
+    /**
      * The method of transmission currently used for a stream,
      * reported via {@link StreamingServiceCallback#streamMethodUpdated}
      */
@@ -87,7 +113,9 @@
      * Retreive the Uri used to play this stream.
      *
      * This may throw a {@link MbmsException} with the error code
-     * {@link MbmsException#ERROR_SERVICE_LOST}
+     * {@link MbmsException#ERROR_MIDDLEWARE_LOST}
+     *
+     * May also throw an {@link IllegalArgumentException} or an {@link IllegalStateException}
      *
      * @return The {@link Uri} to pass to the streaming client.
      */
@@ -101,7 +129,7 @@
         } catch (RemoteException e) {
             Log.w(LOG_TAG, "Remote process died");
             mService = null;
-            throw new MbmsException(MbmsException.ERROR_SERVICE_LOST);
+            throw new MbmsException(MbmsException.ERROR_MIDDLEWARE_LOST);
         }
     }
 
@@ -115,7 +143,9 @@
     /**
      * Stop streaming this service.
      * This may throw a {@link MbmsException} with the error code
-     * {@link MbmsException#ERROR_SERVICE_LOST}
+     * {@link MbmsException#ERROR_MIDDLEWARE_LOST}
+     *
+     * May also throw an {@link IllegalArgumentException} or an {@link IllegalStateException}
      */
     public void stopStreaming() throws MbmsException {
         if (mService == null) {
@@ -127,10 +157,18 @@
         } catch (RemoteException e) {
             Log.w(LOG_TAG, "Remote process died");
             mService = null;
-            throw new MbmsException(MbmsException.ERROR_SERVICE_LOST);
+            throw new MbmsException(MbmsException.ERROR_MIDDLEWARE_LOST);
         }
     }
 
+    /**
+     * Disposes of this stream. Further operations on this object will fail with an
+     * {@link IllegalStateException}.
+     *
+     * This may throw a {@link MbmsException} with the error code
+     * {@link MbmsException#ERROR_MIDDLEWARE_LOST}
+     * May also throw an {@link IllegalArgumentException} or an {@link IllegalStateException}
+     */
     public void dispose() throws MbmsException {
         if (mService == null) {
             throw new IllegalStateException("No streaming service attached");
@@ -140,8 +178,9 @@
             mService.disposeStream(mSubscriptionId, mServiceInfo.getServiceId());
         } catch (RemoteException e) {
             Log.w(LOG_TAG, "Remote process died");
+            throw new MbmsException(MbmsException.ERROR_MIDDLEWARE_LOST);
+        } finally {
             mService = null;
-            throw new MbmsException(MbmsException.ERROR_SERVICE_LOST);
         }
     }
 }
diff --git a/telephony/java/android/telephony/mbms/vendor/IMbmsDownloadService.aidl b/telephony/java/android/telephony/mbms/vendor/IMbmsDownloadService.aidl
index 7112e13..725d11c 100755
--- a/telephony/java/android/telephony/mbms/vendor/IMbmsDownloadService.aidl
+++ b/telephony/java/android/telephony/mbms/vendor/IMbmsDownloadService.aidl
@@ -19,12 +19,11 @@
 import android.app.PendingIntent;
 import android.net.Uri;
 import android.telephony.mbms.DownloadRequest;
-import android.telephony.mbms.DownloadStatus;
+import android.telephony.mbms.FileInfo;
 import android.telephony.mbms.IMbmsDownloadManagerCallback;
 import android.telephony.mbms.IDownloadCallback;
 
 /**
- * The interface the opaque MbmsStreamingService will satisfy.
  * @hide
  */
 interface IMbmsDownloadService
@@ -41,14 +40,9 @@
 
     int cancelDownload(in DownloadRequest downloadRequest);
 
-    DownloadStatus getDownloadStatus(in DownloadRequest downloadRequest);
+    int getDownloadStatus(in DownloadRequest downloadRequest, in FileInfo fileInfo);
 
-    /*
-     * named this for 2 reasons:
-     *  1 don't want 'State' here as it conflicts with 'Status' of the previous function
-     *  2 want to perfect typing 'Knowledge'
-     */
-    void resetDownloadKnowledge(in DownloadRequest downloadRequest);
+    int resetDownloadKnowledge(in DownloadRequest downloadRequest);
 
     void dispose(int subId);
 }
diff --git a/telephony/java/android/telephony/mbms/vendor/IMbmsStreamingService.aidl b/telephony/java/android/telephony/mbms/vendor/IMbmsStreamingService.aidl
index 1370b83..04a53cb 100755
--- a/telephony/java/android/telephony/mbms/vendor/IMbmsStreamingService.aidl
+++ b/telephony/java/android/telephony/mbms/vendor/IMbmsStreamingService.aidl
@@ -22,12 +22,11 @@
 import android.telephony.mbms.StreamingServiceInfo;
 
 /**
- * The interface the opaque MbmsStreamingService will satisfy.
  * @hide
  */
 interface IMbmsStreamingService
 {
-    int initialize(IMbmsStreamingManagerCallback listener, int subId);
+    void initialize(IMbmsStreamingManagerCallback listener, int subId);
 
     int getStreamingServices(int subId, in List<String> serviceClasses);
 
diff --git a/telephony/java/android/telephony/mbms/vendor/MbmsDownloadServiceBase.java b/telephony/java/android/telephony/mbms/vendor/MbmsDownloadServiceBase.java
index 58bda64..8fbd448 100644
--- a/telephony/java/android/telephony/mbms/vendor/MbmsDownloadServiceBase.java
+++ b/telephony/java/android/telephony/mbms/vendor/MbmsDownloadServiceBase.java
@@ -16,9 +16,10 @@
 
 package android.telephony.mbms.vendor;
 
+import android.annotation.NonNull;
 import android.os.RemoteException;
 import android.telephony.mbms.DownloadRequest;
-import android.telephony.mbms.DownloadStatus;
+import android.telephony.mbms.FileInfo;
 import android.telephony.mbms.IDownloadCallback;
 import android.telephony.mbms.IMbmsDownloadManagerCallback;
 import android.telephony.mbms.MbmsException;
@@ -35,7 +36,9 @@
     /**
      * Initialize the download service for this app and subId, registering the listener.
      *
-     * May throw an {@link IllegalArgumentException} or a {@link SecurityException}
+     * Exceptions should not be thrown through this method -- this method is called from within a
+     * {@link android.content.ServiceConnection} defined by the framework, so apps have no way of
+     * catching them. Call {@link IMbmsDownloadManagerCallback#error(int, String)} instead.
      *
      * @param listener The callback to use to communicate with the app.
      * @param subscriptionId The subscription ID to use.
@@ -59,9 +62,8 @@
      * @param serviceClasses The service classes that the app wishes to get info on. The strings
      *                       may contain arbitrary data as negotiated between the app and the
      *                       carrier.
-     * @return One of {@link MbmsException#SUCCESS},
-     *         {@link MbmsException#ERROR_MIDDLEWARE_NOT_YET_READY},
-     *         {@link MbmsException#ERROR_CONCURRENT_SERVICE_LIMIT_REACHED}
+     * @return One of {@link MbmsException#SUCCESS} or
+     *         {@link MbmsException.GeneralErrors#ERROR_MIDDLEWARE_NOT_YET_READY},
      */
     @Override
     public int getFileServices(int subscriptionId, List<String> serviceClasses)
@@ -73,11 +75,16 @@
      * Sets the temp file root directory for this app/subscriptionId combination. The middleware
      * should persist {@code rootDirectoryPath} and send it back when sending intents to the
      * app's {@link android.telephony.mbms.MbmsDownloadReceiver}.
+     *
+     * If the calling app (as identified by the calling UID) currently has any pending download
+     * requests that have not been canceled, the middleware must return
+     * {@link MbmsException.DownloadErrors#ERROR_CANNOT_CHANGE_TEMP_FILE_ROOT} here.
+     *
      * @param subscriptionId The subscription id the download is operating under.
      * @param rootDirectoryPath The path to the app's temp file root directory.
-     * @return {@link MbmsException#ERROR_MIDDLEWARE_NOT_YET_READY},
-     *         {@link MbmsException#ERROR_CANNOT_CHANGE_TEMP_FILE_ROOT},
-     *         or {@link MbmsException#ERROR_CONCURRENT_SERVICE_LIMIT_REACHED}
+     * @return {@link MbmsException#SUCCESS},
+     *         {@link MbmsException.GeneralErrors#ERROR_MIDDLEWARE_NOT_YET_READY} or
+     *         {@link MbmsException.DownloadErrors#ERROR_CANNOT_CHANGE_TEMP_FILE_ROOT}
      */
     @Override
     public int setTempFileRootDirectory(int subscriptionId,
@@ -87,6 +94,11 @@
 
     /**
      * Issues a request to download a set of files.
+     *
+     * The middleware should expect that {@link #setTempFileRootDirectory(int, String)} has been
+     * called for this app between when the app was installed and when this method is called. If
+     * this is not the case, an {@link IllegalStateException} may be thrown.
+     *
      * @param downloadRequest An object describing the set of files to be downloaded.
      * @param listener A listener through which the middleware can provide progress updates to
      *                 the app while both are still running.
@@ -98,8 +110,18 @@
         return 0;
     }
 
+
+    /**
+     * Returns a list of pending {@link DownloadRequest}s that originated from the calling
+     * application, identified by its uid. A pending request is one that was issued via
+     * {@link #download(DownloadRequest, IDownloadCallback)} but not cancelled through
+     * {@link #cancelDownload(DownloadRequest)}.
+     * The middleware must return a non-null result synchronously or throw an exception
+     * inheriting from {@link RuntimeException}.
+     * @return A list, possibly empty, of {@link DownloadRequest}s
+     */
     @Override
-    public List<DownloadRequest> listPendingDownloads(int subscriptionId)
+    public @NonNull List<DownloadRequest> listPendingDownloads(int subscriptionId)
             throws RemoteException {
         return null;
     }
@@ -113,23 +135,47 @@
      * {@link DownloadRequest}.
      * @param downloadRequest The request to cancel
      * @return {@link MbmsException#SUCCESS},
-     *         {@link MbmsException#ERROR_UNKNOWN_DOWNLOAD_REQUEST},
-     *         {@link MbmsException#ERROR_MIDDLEWARE_NOT_YET_READY}
+     *         {@link MbmsException.DownloadErrors#ERROR_UNKNOWN_DOWNLOAD_REQUEST},
+     *         {@link MbmsException.GeneralErrors#ERROR_MIDDLEWARE_NOT_YET_READY}
      */
     @Override
     public int cancelDownload(DownloadRequest downloadRequest) throws RemoteException {
         return 0;
     }
 
+    /**
+     * Gets information about the status of a file pending download.
+     *
+     * If the middleware has not yet been properly initialized or if it has no records of the
+     * file indicated by {@code fileInfo} being associated with {@code downloadRequest},
+     * {@link android.telephony.MbmsDownloadManager#STATUS_UNKNOWN} must be returned.
+     *
+     * @param downloadRequest The download request to query.
+     * @param fileInfo The particular file within the request to get information on.
+     * @return The status of the download.
+     */
     @Override
-    public DownloadStatus getDownloadStatus(DownloadRequest downloadRequest)
+    public int getDownloadStatus(DownloadRequest downloadRequest, FileInfo fileInfo)
             throws RemoteException {
-        return null;
+        return 0;
     }
 
+    /**
+     * Resets the middleware's knowledge of previously-downloaded files in this download request.
+     *
+     * When this method is called, the middleware must attempt to re-download all the files
+     * specified by the {@link DownloadRequest}, even if the files have not changed on the server.
+     * In addition, current in-progress downloads must not be interrupted.
+     *
+     * If the middleware is not aware of the specified download request, return
+     * {@link MbmsException.DownloadErrors#ERROR_UNKNOWN_DOWNLOAD_REQUEST}.
+     *
+     * @param downloadRequest The request to re-download files for.
+     */
     @Override
-    public void resetDownloadKnowledge(DownloadRequest downloadRequest)
+    public int resetDownloadKnowledge(DownloadRequest downloadRequest)
             throws RemoteException {
+        return 0;
     }
 
     /**
diff --git a/telephony/java/android/telephony/mbms/vendor/MbmsStreamingServiceBase.java b/telephony/java/android/telephony/mbms/vendor/MbmsStreamingServiceBase.java
index c97e754..f072c46 100644
--- a/telephony/java/android/telephony/mbms/vendor/MbmsStreamingServiceBase.java
+++ b/telephony/java/android/telephony/mbms/vendor/MbmsStreamingServiceBase.java
@@ -33,16 +33,17 @@
     /**
      * Initialize streaming service for this app and subId, registering the listener.
      *
-     * May throw an {@link IllegalArgumentException} or a {@link SecurityException}
+     * Exceptions should not be thrown through this method -- this method is called from within a
+     * {@link android.content.ServiceConnection} defined by the framework, so apps have no way of
+     * catching them. Call {@link IMbmsStreamingManagerCallback#error(int, String)} instead.
      *
      * @param listener The callback to use to communicate with the app.
      * @param subscriptionId The subscription ID to use.
-     * @return {@link MbmsException#SUCCESS} or {@link MbmsException#ERROR_ALREADY_INITIALIZED}
      */
     @Override
-    public int initialize(IMbmsStreamingManagerCallback listener, int subscriptionId)
+    public void initialize(IMbmsStreamingManagerCallback listener, int subscriptionId)
             throws RemoteException {
-        return 0;
+        return;
     }
 
     /**
@@ -59,9 +60,8 @@
      * @param serviceClasses The service classes that the app wishes to get info on. The strings
      *                       may contain arbitrary data as negotiated between the app and the
      *                       carrier.
-     * @return One of {@link MbmsException#SUCCESS},
-     *         {@link MbmsException#ERROR_MIDDLEWARE_NOT_YET_READY},
-     *         {@link MbmsException#ERROR_CONCURRENT_SERVICE_LIMIT_REACHED}
+     * @return {@link MbmsException#SUCCESS} or any of the errors in
+     * {@link android.telephony.mbms.MbmsException.GeneralErrors}
      */
     @Override
     public int getStreamingServices(int subscriptionId,
@@ -79,8 +79,7 @@
      * @param subscriptionId The subscription id to use.
      * @param serviceId The ID of the streaming service that the app has requested.
      * @param listener The listener object on which the app wishes to receive updates.
-     * @return {@link MbmsException#SUCCESS}, {@link MbmsException#ERROR_STREAM_ALREADY_STARTED},
-     *         or {@link MbmsException#ERROR_UNABLE_TO_START_SERVICE}.
+     * @return Any error in {@link android.telephony.mbms.MbmsException.GeneralErrors}
      */
     @Override
     public int startStreaming(int subscriptionId, String serviceId,
diff --git a/test-runner/Android.mk b/test-runner/Android.mk
index 9053b23..3c3718a 100644
--- a/test-runner/Android.mk
+++ b/test-runner/Android.mk
@@ -22,9 +22,15 @@
 # =====================================
 include $(CLEAR_VARS)
 
-LOCAL_SRC_FILES := $(call all-java-files-under, src)
+LOCAL_SRC_FILES := \
+    $(filter-out $(android_test_mock_source_files), $(call all-java-files-under, src))
 
-LOCAL_JAVA_LIBRARIES := core-oj core-libart framework legacy-test
+LOCAL_JAVA_LIBRARIES := \
+    core-oj \
+    core-libart \
+    framework \
+    legacy-test \
+    android.test.mock \
 
 LOCAL_MODULE:= android.test.runner
 
diff --git a/tests/Internal/src/android/app/WallpaperColorsTest.java b/tests/Internal/src/android/app/WallpaperColorsTest.java
index 5bbd82b..fb529b9 100644
--- a/tests/Internal/src/android/app/WallpaperColorsTest.java
+++ b/tests/Internal/src/android/app/WallpaperColorsTest.java
@@ -77,4 +77,16 @@
         Assert.assertFalse("Light surface shouldn't support dark text "
                 + "when it contains dark pixels", supportsDarkText);
     }
+
+    /**
+     * WallpaperColors should not recycle bitmaps that it didn't create.
+     */
+    @Test
+    public void wallpaperRecycleBitmapTest() {
+        Bitmap image = Bitmap.createBitmap(300, 300, Bitmap.Config.ARGB_8888);
+        WallpaperColors.fromBitmap(image);
+        Canvas canvas = new Canvas();
+        // This would crash:
+        canvas.drawBitmap(image, 0, 0, new Paint());
+    }
 }
diff --git a/tests/Internal/src/com/android/internal/colorextraction/ColorExtractorTest.java b/tests/Internal/src/com/android/internal/colorextraction/ColorExtractorTest.java
index 7182147..0060901 100644
--- a/tests/Internal/src/com/android/internal/colorextraction/ColorExtractorTest.java
+++ b/tests/Internal/src/com/android/internal/colorextraction/ColorExtractorTest.java
@@ -62,21 +62,6 @@
     }
 
     @Test
-    public void getColors_usesFallbackIfFails() {
-        ExtractionType alwaysFail =
-                (inWallpaperColors, outGradientColorsNormal, outGradientColorsDark,
-                        outGradientColorsExtraDark) -> false;
-        ColorExtractor extractor = new ColorExtractor(mContext, alwaysFail);
-        GradientColors colors = extractor.getColors(WallpaperManager.FLAG_SYSTEM);
-
-        assertEquals("Should be using the fallback color.",
-                colors.getMainColor(), ColorExtractor.FALLBACK_COLOR);
-        assertEquals("Should be using the fallback color.",
-                colors.getSecondaryColor(), ColorExtractor.FALLBACK_COLOR);
-        assertFalse("Dark text support should be false.", colors.supportsDarkText());
-    }
-
-    @Test
     public void getColors_usesExtractedColors() {
         GradientColors colorsExpectedNormal = new GradientColors();
         colorsExpectedNormal.setMainColor(Color.RED);
@@ -96,8 +81,6 @@
             outGradientColorsNormal.set(colorsExpectedNormal);
             outGradientColorsDark.set(colorsExpectedDark);
             outGradientColorsExtraDark.set(colorsExpectedExtraDark);
-            // Successful extraction
-            return true;
         };
         ColorExtractor extractor = new ColorExtractor(mContext, type);
 
diff --git a/tests/Internal/src/com/android/internal/colorextraction/types/TonalTest.java b/tests/Internal/src/com/android/internal/colorextraction/types/TonalTest.java
index 1e3e8e9..6dc9ba7 100644
--- a/tests/Internal/src/com/android/internal/colorextraction/types/TonalTest.java
+++ b/tests/Internal/src/com/android/internal/colorextraction/types/TonalTest.java
@@ -20,6 +20,7 @@
 
 import android.app.WallpaperColors;
 import android.graphics.Color;
+import android.support.test.InstrumentationRegistry;
 import android.support.test.filters.SmallTest;
 import android.support.test.runner.AndroidJUnit4;
 import android.util.Range;
@@ -40,6 +41,32 @@
 public class TonalTest {
 
     @Test
+    public void extractInto_usesFallback() {
+        GradientColors normal = new GradientColors();
+        Tonal tonal = new Tonal(InstrumentationRegistry.getContext());
+        tonal.extractInto(null, normal, new GradientColors(),
+                new GradientColors());
+        assertFalse("Should use fallback color if WallpaperColors is null.",
+                normal.getMainColor() == Tonal.MAIN_COLOR_LIGHT);
+    }
+
+    @Test
+    public void extractInto_usesFallbackWhenTooLightOrDark() {
+        GradientColors normal = new GradientColors();
+        Tonal tonal = new Tonal(InstrumentationRegistry.getContext());
+        tonal.extractInto(new WallpaperColors(Color.valueOf(0xff000000), null, null, 0),
+                normal, new GradientColors(), new GradientColors());
+        assertTrue("Should use fallback color if WallpaperColors is too dark.",
+                normal.getMainColor() == Tonal.MAIN_COLOR_DARK);
+
+        tonal.extractInto(new WallpaperColors(Color.valueOf(0xffffffff), null, null,
+                        WallpaperColors.HINT_SUPPORTS_DARK_TEXT),
+                normal, new GradientColors(), new GradientColors());
+        assertTrue("Should use fallback color if WallpaperColors is too light.",
+                normal.getMainColor() == Tonal.MAIN_COLOR_LIGHT);
+    }
+
+    @Test
     public void colorRange_containsColor() {
         Tonal.ColorRange colorRange = new Tonal.ColorRange(new Range<>(0f, 50f),
                 new Range<>(0f, 1f), new Range<>(0f, 1f));
@@ -64,16 +91,28 @@
     }
 
     @Test
-    public void colorRange_excludeBlacklistedColor() {
-        // Creating a WallpaperColors object that contains *only* blacklisted colors
-        float[] hsl = Tonal.BLACKLISTED_COLORS[0].getCenter();
+    public void configParser_dataSanity() {
+        Tonal.ConfigParser config = new Tonal.ConfigParser(InstrumentationRegistry.getContext());
+        // 1 to avoid regression where only first item would be parsed.
+        assertTrue("Tonal palettes are empty", config.getTonalPalettes().size() > 1);
+        assertTrue("Blacklisted colors are empty", config.getBlacklistedColors().size() > 1);
+    }
+
+    @Test
+    public void tonal_excludeBlacklistedColor() {
+        // Make sure that palette generation will fail.
+        Tonal tonal = new Tonal(InstrumentationRegistry.getContext());
+
+        // Creating a WallpaperColors object that contains *only* blacklisted colors.
+        float[] hsl = tonal.getBlacklistedColors().get(0).getCenter();
         WallpaperColors colors = new WallpaperColors(Color.valueOf(ColorUtils.HSLToColor(hsl)),
                 null, null, 0);
 
         // Make sure that palette generation will fail
-        Tonal tonal = new Tonal();
-        boolean success = tonal.extractInto(colors, new GradientColors(), new GradientColors(),
+        GradientColors normal = new GradientColors();
+        tonal.extractInto(colors, normal, new GradientColors(),
                 new GradientColors());
-        assertFalse("Cannot generate a tonal palette from blacklisted colors ", success);
+        assertTrue("Cannot generate a tonal palette from blacklisted colors.",
+                normal.getMainColor() == Tonal.MAIN_COLOR_DARK);
     }
 }
diff --git a/tests/TouchLatency/.gitignore b/tests/TouchLatency/.gitignore
index cfb7164..bd79078 100644
--- a/tests/TouchLatency/.gitignore
+++ b/tests/TouchLatency/.gitignore
@@ -3,3 +3,4 @@
 /.idea
 .DS_Store
 /build
+.iml
diff --git a/tests/TouchLatency/Android.mk b/tests/TouchLatency/Android.mk
index 6ad4705..969283d 100644
--- a/tests/TouchLatency/Android.mk
+++ b/tests/TouchLatency/Android.mk
@@ -8,19 +8,11 @@
 # omit gradle 'build' dir
 LOCAL_SRC_FILES := $(call all-java-files-under,app/src/main/java)
 
-# use appcompat/support lib from the tree, so improvements/
-# regressions are reflected in test data
 LOCAL_RESOURCE_DIR := \
-    $(LOCAL_PATH)/app/src/main/res \
-    frameworks/support/v7/appcompat/res
+    $(LOCAL_PATH)/app/src/main/res
 
 LOCAL_AAPT_FLAGS := \
-    --auto-add-overlay \
-    --extra-packages android.support.v7.appcompat
-
-LOCAL_STATIC_JAVA_LIBRARIES := \
-    android-support-v4 \
-    android-support-v7-appcompat
+    --auto-add-overlay
 
 LOCAL_PACKAGE_NAME := TouchLatency
 
diff --git a/tests/TouchLatency/TouchLatency.iml b/tests/TouchLatency/TouchLatency.iml
deleted file mode 100644
index cd87cea..0000000
--- a/tests/TouchLatency/TouchLatency.iml
+++ /dev/null
@@ -1,19 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<module external.linked.project.path="$MODULE_DIR$" external.root.project.path="$MODULE_DIR$" external.system.id="GRADLE" external.system.module.group="" external.system.module.version="unspecified" type="JAVA_MODULE" version="4">
-  <component name="FacetManager">
-    <facet type="java-gradle" name="Java-Gradle">
-      <configuration>
-        <option name="BUILD_FOLDER_PATH" value="$MODULE_DIR$/build" />
-        <option name="BUILDABLE" value="false" />
-      </configuration>
-    </facet>
-  </component>
-  <component name="NewModuleRootManager" inherit-compiler-output="true">
-    <exclude-output />
-    <content url="file://$MODULE_DIR$">
-      <excludeFolder url="file://$MODULE_DIR$/.gradle" />
-    </content>
-    <orderEntry type="inheritedJdk" />
-    <orderEntry type="sourceFolder" forTests="false" />
-  </component>
-</module>
\ No newline at end of file
diff --git a/tests/TouchLatency/app/app.iml b/tests/TouchLatency/app/app.iml
deleted file mode 100644
index 689e5e0..0000000
--- a/tests/TouchLatency/app/app.iml
+++ /dev/null
@@ -1,92 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<module external.linked.project.path="$MODULE_DIR$" external.root.project.path="$MODULE_DIR$/.." external.system.id="GRADLE" external.system.module.group="TouchLatency" external.system.module.version="unspecified" type="JAVA_MODULE" version="4">
-  <component name="FacetManager">
-    <facet type="android-gradle" name="Android-Gradle">
-      <configuration>
-        <option name="GRADLE_PROJECT_PATH" value=":app" />
-      </configuration>
-    </facet>
-    <facet type="android" name="Android">
-      <configuration>
-        <option name="SELECTED_BUILD_VARIANT" value="debug" />
-        <option name="SELECTED_TEST_ARTIFACT" value="_android_test_" />
-        <option name="ASSEMBLE_TASK_NAME" value="assembleDebug" />
-        <option name="COMPILE_JAVA_TASK_NAME" value="compileDebugSources" />
-        <option name="SOURCE_GEN_TASK_NAME" value="generateDebugSources" />
-        <option name="ASSEMBLE_TEST_TASK_NAME" value="assembleDebugAndroidTest" />
-        <option name="TEST_SOURCE_GEN_TASK_NAME" value="generateDebugAndroidTestSources" />
-        <option name="ALLOW_USER_CONFIGURATION" value="false" />
-        <option name="MANIFEST_FILE_RELATIVE_PATH" value="/src/main/AndroidManifest.xml" />
-        <option name="RES_FOLDER_RELATIVE_PATH" value="/src/main/res" />
-        <option name="RES_FOLDERS_RELATIVE_PATH" value="file://$MODULE_DIR$/src/main/res" />
-        <option name="ASSETS_FOLDER_RELATIVE_PATH" value="/src/main/assets" />
-      </configuration>
-    </facet>
-  </component>
-  <component name="NewModuleRootManager" inherit-compiler-output="false">
-    <output url="file://$MODULE_DIR$/build/intermediates/classes/debug" />
-    <output-test url="file://$MODULE_DIR$/build/intermediates/classes/androidTest/debug" />
-    <exclude-output />
-    <content url="file://$MODULE_DIR$">
-      <sourceFolder url="file://$MODULE_DIR$/build/generated/source/r/debug" isTestSource="false" generated="true" />
-      <sourceFolder url="file://$MODULE_DIR$/build/generated/source/aidl/debug" isTestSource="false" generated="true" />
-      <sourceFolder url="file://$MODULE_DIR$/build/generated/source/buildConfig/debug" isTestSource="false" generated="true" />
-      <sourceFolder url="file://$MODULE_DIR$/build/generated/source/rs/debug" isTestSource="false" generated="true" />
-      <sourceFolder url="file://$MODULE_DIR$/build/generated/res/rs/debug" type="java-resource" />
-      <sourceFolder url="file://$MODULE_DIR$/build/generated/res/generated/debug" type="java-resource" />
-      <sourceFolder url="file://$MODULE_DIR$/build/generated/source/r/androidTest/debug" isTestSource="true" generated="true" />
-      <sourceFolder url="file://$MODULE_DIR$/build/generated/source/aidl/androidTest/debug" isTestSource="true" generated="true" />
-      <sourceFolder url="file://$MODULE_DIR$/build/generated/source/buildConfig/androidTest/debug" isTestSource="true" generated="true" />
-      <sourceFolder url="file://$MODULE_DIR$/build/generated/source/rs/androidTest/debug" isTestSource="true" generated="true" />
-      <sourceFolder url="file://$MODULE_DIR$/build/generated/res/rs/androidTest/debug" type="java-test-resource" />
-      <sourceFolder url="file://$MODULE_DIR$/build/generated/res/generated/androidTest/debug" type="java-test-resource" />
-      <sourceFolder url="file://$MODULE_DIR$/src/debug/res" type="java-resource" />
-      <sourceFolder url="file://$MODULE_DIR$/src/debug/resources" type="java-resource" />
-      <sourceFolder url="file://$MODULE_DIR$/src/debug/assets" type="java-resource" />
-      <sourceFolder url="file://$MODULE_DIR$/src/debug/aidl" isTestSource="false" />
-      <sourceFolder url="file://$MODULE_DIR$/src/debug/java" isTestSource="false" />
-      <sourceFolder url="file://$MODULE_DIR$/src/debug/jni" isTestSource="false" />
-      <sourceFolder url="file://$MODULE_DIR$/src/debug/rs" isTestSource="false" />
-      <sourceFolder url="file://$MODULE_DIR$/src/main/res" type="java-resource" />
-      <sourceFolder url="file://$MODULE_DIR$/src/main/resources" type="java-resource" />
-      <sourceFolder url="file://$MODULE_DIR$/src/main/assets" type="java-resource" />
-      <sourceFolder url="file://$MODULE_DIR$/src/main/aidl" isTestSource="false" />
-      <sourceFolder url="file://$MODULE_DIR$/src/main/java" isTestSource="false" />
-      <sourceFolder url="file://$MODULE_DIR$/src/main/jni" isTestSource="false" />
-      <sourceFolder url="file://$MODULE_DIR$/src/main/rs" isTestSource="false" />
-      <sourceFolder url="file://$MODULE_DIR$/src/androidTest/res" type="java-test-resource" />
-      <sourceFolder url="file://$MODULE_DIR$/src/androidTest/resources" type="java-test-resource" />
-      <sourceFolder url="file://$MODULE_DIR$/src/androidTest/assets" type="java-test-resource" />
-      <sourceFolder url="file://$MODULE_DIR$/src/androidTest/aidl" isTestSource="true" />
-      <sourceFolder url="file://$MODULE_DIR$/src/androidTest/java" isTestSource="true" />
-      <sourceFolder url="file://$MODULE_DIR$/src/androidTest/jni" isTestSource="true" />
-      <sourceFolder url="file://$MODULE_DIR$/src/androidTest/rs" isTestSource="true" />
-      <excludeFolder url="file://$MODULE_DIR$/build/intermediates/assets" />
-      <excludeFolder url="file://$MODULE_DIR$/build/intermediates/bundles" />
-      <excludeFolder url="file://$MODULE_DIR$/build/intermediates/classes" />
-      <excludeFolder url="file://$MODULE_DIR$/build/intermediates/coverage-instrumented-classes" />
-      <excludeFolder url="file://$MODULE_DIR$/build/intermediates/dependency-cache" />
-      <excludeFolder url="file://$MODULE_DIR$/build/intermediates/dex" />
-      <excludeFolder url="file://$MODULE_DIR$/build/intermediates/dex-cache" />
-      <excludeFolder url="file://$MODULE_DIR$/build/intermediates/incremental" />
-      <excludeFolder url="file://$MODULE_DIR$/build/intermediates/jacoco" />
-      <excludeFolder url="file://$MODULE_DIR$/build/intermediates/javaResources" />
-      <excludeFolder url="file://$MODULE_DIR$/build/intermediates/libs" />
-      <excludeFolder url="file://$MODULE_DIR$/build/intermediates/lint" />
-      <excludeFolder url="file://$MODULE_DIR$/build/intermediates/manifests" />
-      <excludeFolder url="file://$MODULE_DIR$/build/intermediates/ndk" />
-      <excludeFolder url="file://$MODULE_DIR$/build/intermediates/pre-dexed" />
-      <excludeFolder url="file://$MODULE_DIR$/build/intermediates/proguard" />
-      <excludeFolder url="file://$MODULE_DIR$/build/intermediates/res" />
-      <excludeFolder url="file://$MODULE_DIR$/build/intermediates/rs" />
-      <excludeFolder url="file://$MODULE_DIR$/build/intermediates/symbols" />
-      <excludeFolder url="file://$MODULE_DIR$/build/outputs" />
-      <excludeFolder url="file://$MODULE_DIR$/build/tmp" />
-    </content>
-    <orderEntry type="jdk" jdkName="Android API 21 Platform" jdkType="Android SDK" />
-    <orderEntry type="sourceFolder" forTests="false" />
-    <orderEntry type="library" exported="" name="appcompat-v7-21.0.3" level="project" />
-    <orderEntry type="library" exported="" name="support-annotations-21.0.3" level="project" />
-    <orderEntry type="library" exported="" name="support-v4-21.0.3" level="project" />
-  </component>
-</module>
\ No newline at end of file
diff --git a/tests/TouchLatency/app/build.gradle b/tests/TouchLatency/app/build.gradle
index 7133beb..2337110 100644
--- a/tests/TouchLatency/app/build.gradle
+++ b/tests/TouchLatency/app/build.gradle
@@ -18,8 +18,3 @@
         }
     }
 }
-
-dependencies {
-    compile fileTree(dir: 'libs', include: ['*.jar'])
-    compile 'com.android.support:appcompat-v7:21.0.3'
-}
diff --git a/tests/TouchLatency/app/src/main/java/com/prefabulated/touchlatency/TouchLatencyActivity.java b/tests/TouchLatency/app/src/main/java/com/prefabulated/touchlatency/TouchLatencyActivity.java
index 55440c8..b763c78 100644
--- a/tests/TouchLatency/app/src/main/java/com/prefabulated/touchlatency/TouchLatencyActivity.java
+++ b/tests/TouchLatency/app/src/main/java/com/prefabulated/touchlatency/TouchLatencyActivity.java
@@ -16,12 +16,12 @@
 
 package com.prefabulated.touchlatency;
 
+import android.app.Activity;
 import android.content.Context;
 import android.graphics.Canvas;
 import android.graphics.Color;
 import android.graphics.Paint;
 import android.os.CountDownTimer;
-import android.support.v7.app.AppCompatActivity;
 import android.os.Bundle;
 import android.text.method.Touch;
 import android.util.AttributeSet;
@@ -173,7 +173,7 @@
     private float mVelocityX, mVelocityY;
 }
 
-public class TouchLatencyActivity extends AppCompatActivity {
+public class TouchLatencyActivity extends Activity {
 
     @Override
     protected void onCreate(Bundle savedInstanceState) {
diff --git a/tests/TouchLatency/app/src/main/res/menu/menu_touch_latency.xml b/tests/TouchLatency/app/src/main/res/menu/menu_touch_latency.xml
index 1824f4a..5aef72e 100644
--- a/tests/TouchLatency/app/src/main/res/menu/menu_touch_latency.xml
+++ b/tests/TouchLatency/app/src/main/res/menu/menu_touch_latency.xml
@@ -14,8 +14,7 @@
      limitations under the License.
 -->
 <menu xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:app="http://schemas.android.com/apk/res-auto"
     xmlns:tools="http://schemas.android.com/tools" tools:context=".TouchLatencyActivity">
     <item android:id="@+id/action_settings" android:title="@string/mode"
-        android:orderInCategory="100" app:showAsAction="always" />
+        android:orderInCategory="100" android:showAsAction="always" />
 </menu>
diff --git a/tests/TouchLatency/app/src/main/res/values/styles.xml b/tests/TouchLatency/app/src/main/res/values/styles.xml
index aa7c09f..22da7c1 100644
--- a/tests/TouchLatency/app/src/main/res/values/styles.xml
+++ b/tests/TouchLatency/app/src/main/res/values/styles.xml
@@ -16,7 +16,7 @@
 <resources>
 
     <!-- Base application theme. -->
-    <style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
+    <style name="AppTheme" parent="@android:style/Theme.Material.Light.DarkActionBar">
         <!-- Customize your theme here. -->
     </style>
 
diff --git a/tests/net/AndroidManifest.xml b/tests/net/AndroidManifest.xml
index e069dd0..ba1a2ea 100644
--- a/tests/net/AndroidManifest.xml
+++ b/tests/net/AndroidManifest.xml
@@ -32,7 +32,6 @@
     <uses-permission android:name="android.permission.GET_DETAILED_TASKS" />
     <uses-permission android:name="android.permission.MANAGE_NETWORK_POLICY" />
     <uses-permission android:name="android.permission.READ_NETWORK_USAGE_HISTORY" />
-    <uses-permission android:name="android.permission.MODIFY_NETWORK_ACCOUNTING" />
     <uses-permission android:name="android.permission.CONNECTIVITY_INTERNAL" />
     <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
     <uses-permission android:name="android.permission.MANAGE_USERS" />
diff --git a/tests/net/java/android/net/ip/IpManagerTest.java b/tests/net/java/android/net/ip/IpManagerTest.java
index 025b017..867324d 100644
--- a/tests/net/java/android/net/ip/IpManagerTest.java
+++ b/tests/net/java/android/net/ip/IpManagerTest.java
@@ -16,11 +16,26 @@
 
 package android.net.ip;
 
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.fail;
+import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.timeout;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
+import android.app.AlarmManager;
 import android.content.ContentResolver;
 import android.content.Context;
 import android.content.res.Resources;
+import android.net.IpPrefix;
+import android.net.LinkAddress;
+import android.net.ip.IpManager.Callback;
+import android.net.ip.IpManager.InitialConfiguration;
+import android.net.ip.IpManager.ProvisioningConfiguration;
 import android.os.INetworkManagementService;
 import android.provider.Settings;
 import android.support.test.filters.SmallTest;
@@ -31,11 +46,17 @@
 import com.android.internal.R;
 
 import org.junit.Before;
-import org.junit.runner.RunWith;
 import org.junit.Test;
+import org.junit.runner.RunWith;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 
+import java.net.Inet4Address;
+import java.net.Inet6Address;
+import java.net.InetAddress;
+import java.util.HashSet;
+import java.util.Set;
+
 /**
  * Tests for IpManager.
  */
@@ -44,14 +65,21 @@
 public class IpManagerTest {
     private static final int DEFAULT_AVOIDBADWIFI_CONFIG_VALUE = 1;
 
+    private static final String VALID = "VALID";
+    private static final String INVALID = "INVALID";
+
     @Mock private Context mContext;
     @Mock private INetworkManagementService mNMService;
     @Mock private Resources mResources;
+    @Mock private Callback mCb;
+    @Mock private AlarmManager mAlarm;
     private MockContentResolver mContentResolver;
 
-    @Before public void setUp() throws Exception {
+    @Before
+    public void setUp() throws Exception {
         MockitoAnnotations.initMocks(this);
 
+        when(mContext.getSystemService(eq(Context.ALARM_SERVICE))).thenReturn(mAlarm);
         when(mContext.getResources()).thenReturn(mResources);
         when(mResources.getInteger(R.integer.config_networkAvoidBadWifi))
                 .thenReturn(DEFAULT_AVOIDBADWIFI_CONFIG_VALUE);
@@ -61,6 +89,13 @@
         when(mContext.getContentResolver()).thenReturn(mContentResolver);
     }
 
+    private IpManager makeIpManager(String ifname) throws Exception {
+        final IpManager ipm = new IpManager(mContext, ifname, mCb, mNMService);
+        verify(mNMService, timeout(100).times(1)).disableIpv6(ifname);
+        verify(mNMService, timeout(100).times(1)).clearInterfaceAddresses(ifname);
+        return ipm;
+    }
+
     @Test
     public void testNullCallbackDoesNotThrow() throws Exception {
         final IpManager ipm = new IpManager(mContext, "lo", null, mNMService);
@@ -68,7 +103,153 @@
 
     @Test
     public void testInvalidInterfaceDoesNotThrow() throws Exception {
-        final IpManager.Callback cb = new IpManager.Callback();
-        final IpManager ipm = new IpManager(mContext, "test_wlan0", cb, mNMService);
+        final IpManager ipm = new IpManager(mContext, "test_wlan0", mCb, mNMService);
+    }
+
+    @Test
+    public void testDefaultProvisioningConfiguration() throws Exception {
+        final String iface = "test_wlan0";
+        final IpManager ipm = makeIpManager(iface);
+
+        ProvisioningConfiguration config = new ProvisioningConfiguration.Builder()
+                .withoutIPv4()
+                // TODO: mock IpReachabilityMonitor's dependencies (NetworkInterface, PowerManager)
+                // and enable it in this test
+                .withoutIpReachabilityMonitor()
+                .build();
+
+        ipm.startProvisioning(config);
+        verify(mCb, times(1)).setNeighborDiscoveryOffload(true);
+        verify(mCb, timeout(100).times(1)).setFallbackMulticastFilter(false);
+        verify(mCb, never()).onProvisioningFailure(any());
+
+        ipm.stop();
+        verify(mNMService, timeout(100).times(1)).disableIpv6(iface);
+        verify(mNMService, timeout(100).times(1)).clearInterfaceAddresses(iface);
+    }
+
+    @Test
+    public void testInitialConfigurations() throws Exception {
+        InitialConfigurationTestCase[] testcases = {
+            validConf("valid IPv4 configuration",
+                    links("192.0.2.12/24"), prefixes("192.0.2.0/24"), dns("192.0.2.2")),
+            validConf("another valid IPv4 configuration",
+                    links("192.0.2.12/24"), prefixes("192.0.2.0/24"), dns()),
+            validConf("valid IPv6 configurations",
+                    links("2001:db8:dead:beef:f00::a0/64", "fe80::1/64"),
+                    prefixes("2001:db8:dead:beef::/64", "fe80::/64"),
+                    dns("2001:db8:dead:beef:f00::02")),
+            validConf("valid IPv6 configurations",
+                    links("fe80::1/64"), prefixes("fe80::/64"), dns()),
+            validConf("valid IPv6/v4 configuration",
+                    links("2001:db8:dead:beef:f00::a0/48", "192.0.2.12/24"),
+                    prefixes("2001:db8:dead:beef::/64", "192.0.2.0/24"),
+                    dns("192.0.2.2", "2001:db8:dead:beef:f00::02")),
+            validConf("valid IPv6 configuration without any GUA.",
+                    links("fd00:1234:5678::1/48"),
+                    prefixes("fd00:1234:5678::/48"),
+                    dns("fd00:1234:5678::1000")),
+
+            invalidConf("v4 addr and dns not in any prefix",
+                    links("192.0.2.12/24"), prefixes("198.51.100.0/24"), dns("192.0.2.2")),
+            invalidConf("v4 addr not in any prefix",
+                    links("198.51.2.12/24"), prefixes("198.51.100.0/24"), dns("192.0.2.2")),
+            invalidConf("v4 dns addr not in any prefix",
+                    links("192.0.2.12/24"), prefixes("192.0.2.0/24"), dns("198.51.100.2")),
+            invalidConf("v6 addr not in any prefix",
+                    links("2001:db8:dead:beef:f00::a0/64", "fe80::1/64"),
+                    prefixes("2001:db8:dead:beef::/64"),
+                    dns("2001:db8:dead:beef:f00::02")),
+            invalidConf("v6 dns addr not in any prefix",
+                    links("fe80::1/64"), prefixes("fe80::/64"), dns("2001:db8:dead:beef:f00::02")),
+            invalidConf("default ipv6 route and no GUA",
+                    links("fd01:1111:2222:3333::a0/128"), prefixes("::/0"), dns()),
+            invalidConf("invalid v6 prefix length",
+                    links("2001:db8:dead:beef:f00::a0/128"), prefixes("2001:db8:dead:beef::/32"),
+                    dns()),
+            invalidConf("another invalid v6 prefix length",
+                    links("2001:db8:dead:beef:f00::a0/128"), prefixes("2001:db8:dead:beef::/72"),
+                    dns())
+        };
+
+        for (InitialConfigurationTestCase testcase : testcases) {
+            if (testcase.config.isValid() != testcase.isValid) {
+                fail(testcase.errorMessage());
+            }
+        }
+    }
+
+    static class InitialConfigurationTestCase {
+        String descr;
+        boolean isValid;
+        InitialConfiguration config;
+        public String errorMessage() {
+            return String.format("%s: expected configuration %s to be %s, but was %s",
+                    descr, config, validString(isValid), validString(!isValid));
+        }
+    }
+
+    static String validString(boolean isValid) {
+        return isValid ? VALID : INVALID;
+    }
+
+    static InitialConfigurationTestCase validConf(String descr, Set<LinkAddress> links,
+            Set<IpPrefix> prefixes, Set<InetAddress> dns) {
+        return confTestCase(descr, true, conf(links, prefixes, dns));
+    }
+
+    static InitialConfigurationTestCase invalidConf(String descr, Set<LinkAddress> links,
+            Set<IpPrefix> prefixes, Set<InetAddress> dns) {
+        return confTestCase(descr, false, conf(links, prefixes, dns));
+    }
+
+    static InitialConfigurationTestCase confTestCase(
+            String descr, boolean isValid, InitialConfiguration config) {
+        InitialConfigurationTestCase testcase = new InitialConfigurationTestCase();
+        testcase.descr = descr;
+        testcase.isValid = isValid;
+        testcase.config = config;
+        return testcase;
+    }
+
+    static InitialConfiguration conf(
+            Set<LinkAddress> links, Set<IpPrefix> prefixes, Set<InetAddress> dns) {
+        InitialConfiguration conf = new InitialConfiguration();
+        conf.ipAddresses.addAll(links);
+        conf.directlyConnectedRoutes.addAll(prefixes);
+        conf.dnsServers.addAll(dns);
+        return conf;
+    }
+
+    static Set<IpPrefix> prefixes(String... prefixes) {
+        return mapIntoSet(prefixes, IpPrefix::new);
+    }
+
+    static Set<LinkAddress> links(String... addresses) {
+        return mapIntoSet(addresses, LinkAddress::new);
+    }
+
+    static Set<InetAddress> ips(String... addresses) {
+        return mapIntoSet(addresses, InetAddress::getByName);
+    }
+
+    static Set<InetAddress> dns(String... addresses) {
+        return ips(addresses);
+    }
+
+    static <A, B> Set<B> mapIntoSet(A[] in, Fn<A, B> fn) {
+        Set<B> out = new HashSet<>(in.length);
+        for (A item : in) {
+            try {
+                out.add(fn.call(item));
+            } catch (Exception e) {
+                throw new RuntimeException(e);
+            }
+        }
+        return out;
+    }
+
+    interface Fn<A,B> {
+        B call(A a) throws Exception;
     }
 }
diff --git a/tests/net/java/com/android/server/ConnectivityServiceTest.java b/tests/net/java/com/android/server/ConnectivityServiceTest.java
index bed1c32..f6481cf 100644
--- a/tests/net/java/com/android/server/ConnectivityServiceTest.java
+++ b/tests/net/java/com/android/server/ConnectivityServiceTest.java
@@ -885,6 +885,7 @@
     }
 
     private void verifyNoNetwork() {
+        waitForIdle();
         // Test getActiveNetworkInfo()
         assertNull(mCm.getActiveNetworkInfo());
         // Test getActiveNetwork()
@@ -3281,7 +3282,7 @@
         ConditionVariable broadcastCV = waitForConnectivityBroadcasts(1);
 
         verifyNoNetwork();
-        MockNetworkAgent lowpanNetwork = new MockNetworkAgent(TRANSPORT_WIFI_AWARE);
+        MockNetworkAgent wifiAware = new MockNetworkAgent(TRANSPORT_WIFI_AWARE);
         assertNull(mCm.getActiveNetworkInfo());
 
         Network[] allNetworks = mCm.getAllNetworks();
@@ -3295,19 +3296,19 @@
         final TestNetworkCallback callback = new TestNetworkCallback();
         mCm.registerNetworkCallback(request, callback);
 
-        // Bring up lowpan.
-        lowpanNetwork.connect(false, false);
-        callback.expectAvailableCallbacks(lowpanNetwork);
+        // Bring up wifi aware network.
+        wifiAware.connect(false, false);
+        callback.expectAvailableCallbacks(wifiAware);
 
         assertNull(mCm.getActiveNetworkInfo());
         assertNull(mCm.getActiveNetwork());
-        // TODO: getAllNetworkInfo is dirty and returns a non-empty array rght from the start
+        // TODO: getAllNetworkInfo is dirty and returns a non-empty array right from the start
         // of this test. Fix it and uncomment the assert below.
         //assertEmpty(mCm.getAllNetworkInfo());
 
-        // Disconnect lowpan.
-        lowpanNetwork.disconnect();
-        callback.expectCallback(CallbackState.LOST, lowpanNetwork);
+        // Disconnect wifi aware network.
+        wifiAware.disconnect();
+        callback.expectCallbackLike((info) -> info.state == CallbackState.LOST, TIMEOUT_MS);
         mCm.unregisterNetworkCallback(callback);
 
         verifyNoNetwork();
diff --git a/tests/net/java/com/android/server/connectivity/IpConnectivityEventBuilderTest.java b/tests/net/java/com/android/server/connectivity/IpConnectivityEventBuilderTest.java
index d11565a..eff04ab 100644
--- a/tests/net/java/com/android/server/connectivity/IpConnectivityEventBuilderTest.java
+++ b/tests/net/java/com/android/server/connectivity/IpConnectivityEventBuilderTest.java
@@ -201,9 +201,14 @@
                 "  time_ms: 1",
                 "  transports: 0",
                 "  default_network_event <",
+                "    default_network_duration_ms: 0",
+                "    final_score: 0",
+                "    initial_score: 0",
+                "    ip_support: 0",
                 "    network_id <",
                 "      network_id: 102",
                 "    >",
+                "    no_default_network_duration_ms: 0",
                 "    previous_network_id <",
                 "      network_id: 101",
                 "    >",
@@ -442,6 +447,8 @@
                 "    program_updates_all: 7",
                 "    program_updates_allowing_multicast: 3",
                 "    received_ras: 10",
+                "    total_packet_dropped: 0",
+                "    total_packet_processed: 0",
                 "    zero_lifetime_ras: 1",
                 "  >",
                 ">",
diff --git a/tests/net/java/com/android/server/connectivity/IpConnectivityMetricsTest.java b/tests/net/java/com/android/server/connectivity/IpConnectivityMetricsTest.java
index e01469b..cc18b7f 100644
--- a/tests/net/java/com/android/server/connectivity/IpConnectivityMetricsTest.java
+++ b/tests/net/java/com/android/server/connectivity/IpConnectivityMetricsTest.java
@@ -256,9 +256,14 @@
                 "  time_ms: 300",
                 "  transports: 0",
                 "  default_network_event <",
+                "    default_network_duration_ms: 0",
+                "    final_score: 0",
+                "    initial_score: 0",
+                "    ip_support: 0",
                 "    network_id <",
                 "      network_id: 102",
                 "    >",
+                "    no_default_network_duration_ms: 0",
                 "    previous_network_id <",
                 "      network_id: 101",
                 "    >",
@@ -308,6 +313,8 @@
                 "    program_updates_all: 7",
                 "    program_updates_allowing_multicast: 3",
                 "    received_ras: 10",
+                "    total_packet_dropped: 0",
+                "    total_packet_processed: 0",
                 "    zero_lifetime_ras: 1",
                 "  >",
                 ">",
@@ -367,6 +374,10 @@
                 "    event_types: 1",
                 "    event_types: 1",
                 "    event_types: 2",
+                "    getaddrinfo_error_count: 0",
+                "    getaddrinfo_query_count: 0",
+                "    gethostbyname_error_count: 0",
+                "    gethostbyname_query_count: 0",
                 "    latencies_ms: 3456",
                 "    latencies_ms: 45",
                 "    latencies_ms: 638",
@@ -384,6 +395,10 @@
                 "  dns_lookup_batch <",
                 "    event_types: 1",
                 "    event_types: 2",
+                "    getaddrinfo_error_count: 0",
+                "    getaddrinfo_query_count: 0",
+                "    gethostbyname_error_count: 0",
+                "    gethostbyname_query_count: 0",
                 "    latencies_ms: 56",
                 "    latencies_ms: 34",
                 "    return_codes: 0",
diff --git a/tests/net/java/com/android/server/connectivity/NetdEventListenerServiceTest.java b/tests/net/java/com/android/server/connectivity/NetdEventListenerServiceTest.java
index f98ab3d..46f395e 100644
--- a/tests/net/java/com/android/server/connectivity/NetdEventListenerServiceTest.java
+++ b/tests/net/java/com/android/server/connectivity/NetdEventListenerServiceTest.java
@@ -111,6 +111,10 @@
                 "    event_types: 1",
                 "    event_types: 2",
                 "    event_types: 2",
+                "    getaddrinfo_error_count: 0",
+                "    getaddrinfo_query_count: 0",
+                "    gethostbyname_error_count: 0",
+                "    gethostbyname_query_count: 0",
                 "    latencies_ms: 3456",
                 "    latencies_ms: 267",
                 "    latencies_ms: 1230",
@@ -142,6 +146,10 @@
                 "    event_types: 2",
                 "    event_types: 1",
                 "    event_types: 1",
+                "    getaddrinfo_error_count: 0",
+                "    getaddrinfo_query_count: 0",
+                "    gethostbyname_error_count: 0",
+                "    gethostbyname_query_count: 0",
                 "    latencies_ms: 56",
                 "    latencies_ms: 78",
                 "    latencies_ms: 14",
diff --git a/tests/net/java/com/android/server/connectivity/TetheringTest.java b/tests/net/java/com/android/server/connectivity/TetheringTest.java
index acf9e8f7..a115146 100644
--- a/tests/net/java/com/android/server/connectivity/TetheringTest.java
+++ b/tests/net/java/com/android/server/connectivity/TetheringTest.java
@@ -178,6 +178,7 @@
         mTethering = new Tethering(mServiceContext, mNMService, mStatsService, mPolicyManager,
                                    mLooper.getLooper(), mSystemProperties,
                                    mTetheringDependencies);
+        verify(mNMService).registerTetheringStatsProvider(any(), anyString());
     }
 
     @After
diff --git a/tests/net/java/com/android/server/connectivity/VpnTest.java b/tests/net/java/com/android/server/connectivity/VpnTest.java
index 506d9e5..f0b3724 100644
--- a/tests/net/java/com/android/server/connectivity/VpnTest.java
+++ b/tests/net/java/com/android/server/connectivity/VpnTest.java
@@ -27,13 +27,16 @@
 import android.app.AppOpsManager;
 import android.app.NotificationManager;
 import android.content.Context;
-import android.content.Intent;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.content.pm.ServiceInfo;
 import android.content.pm.UserInfo;
 import android.net.NetworkInfo.DetailedState;
 import android.net.UidRange;
-import android.os.Build;
+import android.net.VpnService;
+import android.os.Build.VERSION_CODES;
+import android.os.Bundle;
 import android.os.INetworkManagementService;
 import android.os.Looper;
 import android.os.UserHandle;
@@ -45,22 +48,22 @@
 
 import com.android.internal.net.VpnConfig;
 
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Map;
-import java.util.Set;
-
 import org.mockito.Answers;
-import org.mockito.ArgumentCaptor;
 import org.mockito.InOrder;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Map;
+import java.util.Set;
+
 /**
  * Tests for {@link Vpn}.
  *
  * Build, install and run with:
- *  runtest --path src/com/android/server/connectivity/VpnTest.java
+ *  runtest --path java/com/android/server/connectivity/VpnTest.java
  */
 public class VpnTest extends AndroidTestCase {
     private static final String TAG = "VpnTest";
@@ -116,7 +119,7 @@
 
         // Used by {@link Notification.Builder}
         ApplicationInfo applicationInfo = new ApplicationInfo();
-        applicationInfo.targetSdkVersion = Build.VERSION_CODES.CUR_DEVELOPMENT;
+        applicationInfo.targetSdkVersion = VERSION_CODES.CUR_DEVELOPMENT;
         when(mContext.getApplicationInfo()).thenReturn(applicationInfo);
 
         doNothing().when(mNetService).registerObserver(any());
@@ -315,6 +318,40 @@
     }
 
     @SmallTest
+    public void testIsAlwaysOnPackageSupported() throws Exception {
+        final Vpn vpn = createVpn(primaryUser.id);
+
+        ApplicationInfo appInfo = new ApplicationInfo();
+        when(mPackageManager.getApplicationInfoAsUser(eq(PKGS[0]), anyInt(), eq(primaryUser.id)))
+                .thenReturn(appInfo);
+
+        ServiceInfo svcInfo = new ServiceInfo();
+        ResolveInfo resInfo = new ResolveInfo();
+        resInfo.serviceInfo = svcInfo;
+        when(mPackageManager.queryIntentServicesAsUser(any(), eq(PackageManager.GET_META_DATA),
+                eq(primaryUser.id)))
+                .thenReturn(Collections.singletonList(resInfo));
+
+        // null package name should return false
+        assertFalse(vpn.isAlwaysOnPackageSupported(null));
+
+        // Pre-N apps are not supported
+        appInfo.targetSdkVersion = VERSION_CODES.M;
+        assertFalse(vpn.isAlwaysOnPackageSupported(PKGS[0]));
+
+        // N+ apps are supported by default
+        appInfo.targetSdkVersion = VERSION_CODES.N;
+        assertTrue(vpn.isAlwaysOnPackageSupported(PKGS[0]));
+
+        // Apps that opt out explicitly are not supported
+        appInfo.targetSdkVersion = VERSION_CODES.CUR_DEVELOPMENT;
+        Bundle metaData = new Bundle();
+        metaData.putBoolean(VpnService.METADATA_SUPPORTS_ALWAYS_ON, false);
+        svcInfo.metaData = metaData;
+        assertFalse(vpn.isAlwaysOnPackageSupported(PKGS[0]));
+    }
+
+    @SmallTest
     public void testNotificationShownForAlwaysOnApp() {
         final UserHandle userHandle = UserHandle.of(primaryUser.id);
         final Vpn vpn = createVpn(primaryUser.id);
diff --git a/tests/net/java/com/android/server/connectivity/tethering/OffloadControllerTest.java b/tests/net/java/com/android/server/connectivity/tethering/OffloadControllerTest.java
index 0dedf70..dcb9723 100644
--- a/tests/net/java/com/android/server/connectivity/tethering/OffloadControllerTest.java
+++ b/tests/net/java/com/android/server/connectivity/tethering/OffloadControllerTest.java
@@ -16,25 +16,38 @@
 
 package com.android.server.connectivity.tethering;
 
+import static android.net.NetworkStats.SET_DEFAULT;
+import static android.net.NetworkStats.TAG_NONE;
+import static android.net.TrafficStats.UID_TETHERING;
 import static android.provider.Settings.Global.TETHER_OFFLOAD_DISABLED;
+import static com.android.server.connectivity.tethering.OffloadHardwareInterface.ForwardedStats;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
 import static org.mockito.Matchers.any;
 import static org.mockito.Matchers.anyObject;
+import static org.mockito.Matchers.anyString;
 import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.doNothing;
 import static org.mockito.Mockito.inOrder;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.reset;
 import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
 import android.content.Context;
 import android.content.pm.ApplicationInfo;
+import android.net.ITetheringStatsProvider;
+import android.net.IpPrefix;
 import android.net.LinkAddress;
 import android.net.LinkProperties;
+import android.net.NetworkStats;
 import android.net.RouteInfo;
 import android.net.util.SharedLog;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.INetworkManagementService;
 import android.provider.Settings;
 import android.provider.Settings.SettingNotFoundException;
 
@@ -45,6 +58,8 @@
 
 import java.net.InetAddress;
 import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.Set;
 
 import org.junit.After;
 import org.junit.Before;
@@ -63,11 +78,14 @@
     @Mock private OffloadHardwareInterface mHardware;
     @Mock private ApplicationInfo mApplicationInfo;
     @Mock private Context mContext;
+    @Mock private INetworkManagementService mNMService;
     private final ArgumentCaptor<ArrayList> mStringArrayCaptor =
             ArgumentCaptor.forClass(ArrayList.class);
+    private final ArgumentCaptor<ITetheringStatsProvider.Stub> mTetherStatsProviderCaptor =
+            ArgumentCaptor.forClass(ITetheringStatsProvider.Stub.class);
     private MockContentResolver mContentResolver;
 
-    @Before public void setUp() throws Exception {
+    @Before public void setUp() {
         MockitoAnnotations.initMocks(this);
         when(mContext.getApplicationInfo()).thenReturn(mApplicationInfo);
         when(mContext.getPackageName()).thenReturn("OffloadControllerTest");
@@ -85,14 +103,23 @@
         when(mHardware.initOffloadConfig()).thenReturn(true);
         when(mHardware.initOffloadControl(any(OffloadHardwareInterface.ControlCallback.class)))
                 .thenReturn(true);
+        when(mHardware.getForwardedStats(any())).thenReturn(new ForwardedStats());
     }
 
     private void enableOffload() {
         Settings.Global.putInt(mContentResolver, TETHER_OFFLOAD_DISABLED, 0);
     }
 
+    private OffloadController makeOffloadController() throws Exception {
+        OffloadController offload = new OffloadController(new Handler(Looper.getMainLooper()),
+                mHardware, mContentResolver, mNMService, new SharedLog("test"));
+        verify(mNMService).registerTetheringStatsProvider(
+                mTetherStatsProviderCaptor.capture(), anyString());
+        return offload;
+    }
+
     @Test
-    public void testNoSettingsValueDefaultDisabledDoesNotStart() {
+    public void testNoSettingsValueDefaultDisabledDoesNotStart() throws Exception {
         setupFunctioningHardwareInterface();
         when(mHardware.getDefaultTetherOffloadDisabled()).thenReturn(1);
         try {
@@ -100,8 +127,7 @@
             fail();
         } catch (SettingNotFoundException expected) {}
 
-        final OffloadController offload =
-                new OffloadController(null, mHardware, mContentResolver, new SharedLog("test"));
+        final OffloadController offload = makeOffloadController();
         offload.start();
 
         final InOrder inOrder = inOrder(mHardware);
@@ -113,7 +139,7 @@
     }
 
     @Test
-    public void testNoSettingsValueDefaultEnabledDoesStart() {
+    public void testNoSettingsValueDefaultEnabledDoesStart() throws Exception {
         setupFunctioningHardwareInterface();
         when(mHardware.getDefaultTetherOffloadDisabled()).thenReturn(0);
         try {
@@ -121,8 +147,7 @@
             fail();
         } catch (SettingNotFoundException expected) {}
 
-        final OffloadController offload =
-                new OffloadController(null, mHardware, mContentResolver, new SharedLog("test"));
+        final OffloadController offload = makeOffloadController();
         offload.start();
 
         final InOrder inOrder = inOrder(mHardware);
@@ -134,12 +159,11 @@
     }
 
     @Test
-    public void testSettingsAllowsStart() {
+    public void testSettingsAllowsStart() throws Exception {
         setupFunctioningHardwareInterface();
         Settings.Global.putInt(mContentResolver, TETHER_OFFLOAD_DISABLED, 0);
 
-        final OffloadController offload =
-                new OffloadController(null, mHardware, mContentResolver, new SharedLog("test"));
+        final OffloadController offload = makeOffloadController();
         offload.start();
 
         final InOrder inOrder = inOrder(mHardware);
@@ -151,12 +175,11 @@
     }
 
     @Test
-    public void testSettingsDisablesStart() {
+    public void testSettingsDisablesStart() throws Exception {
         setupFunctioningHardwareInterface();
         Settings.Global.putInt(mContentResolver, TETHER_OFFLOAD_DISABLED, 1);
 
-        final OffloadController offload =
-                new OffloadController(null, mHardware, mContentResolver, new SharedLog("test"));
+        final OffloadController offload = makeOffloadController();
         offload.start();
 
         final InOrder inOrder = inOrder(mHardware);
@@ -171,8 +194,7 @@
         setupFunctioningHardwareInterface();
         enableOffload();
 
-        final OffloadController offload =
-                new OffloadController(null, mHardware, mContentResolver, new SharedLog("test"));
+        final OffloadController offload = makeOffloadController();
         offload.start();
 
         final InOrder inOrder = inOrder(mHardware);
@@ -182,17 +204,43 @@
                 any(OffloadHardwareInterface.ControlCallback.class));
         inOrder.verifyNoMoreInteractions();
 
-        offload.setUpstreamLinkProperties(null);
-        inOrder.verify(mHardware, times(1)).setUpstreamParameters(
-                eq(null), eq(null), eq(null), eq(null));
+        // In reality, the UpstreamNetworkMonitor would have passed down to us
+        // a covering set of local prefixes representing a minimum essential
+        // set plus all the prefixes on networks with network agents.
+        //
+        // We simulate that there, and then add upstream elements one by one
+        // and watch what happens.
+        final Set<IpPrefix> minimumLocalPrefixes = new HashSet<>();
+        for (String s : new String[]{
+                "127.0.0.0/8", "192.0.2.0/24", "fe80::/64", "2001:db8::/64"}) {
+            minimumLocalPrefixes.add(new IpPrefix(s));
+        }
+        offload.setLocalPrefixes(minimumLocalPrefixes);
+        inOrder.verify(mHardware, times(1)).setLocalPrefixes(mStringArrayCaptor.capture());
+        ArrayList<String> localPrefixes = mStringArrayCaptor.getValue();
+        assertEquals(4, localPrefixes.size());
+        assertTrue(localPrefixes.contains("127.0.0.0/8"));
+        assertTrue(localPrefixes.contains("192.0.2.0/24"));
+        assertTrue(localPrefixes.contains("fe80::/64"));
+        assertTrue(localPrefixes.contains("2001:db8::/64"));
         inOrder.verifyNoMoreInteractions();
-        reset(mHardware);
+
+        offload.setUpstreamLinkProperties(null);
+        // No change in local addresses means no call to setLocalPrefixes().
+        inOrder.verify(mHardware, never()).setLocalPrefixes(mStringArrayCaptor.capture());
+        // This LinkProperties value does not differ from the default upstream.
+        // There should be no extraneous call to setUpstreamParameters().
+        inOrder.verify(mHardware, never()).setUpstreamParameters(
+                anyObject(), anyObject(), anyObject(), anyObject());
+        inOrder.verifyNoMoreInteractions();
 
         final LinkProperties lp = new LinkProperties();
 
         final String testIfName = "rmnet_data17";
         lp.setInterfaceName(testIfName);
         offload.setUpstreamLinkProperties(lp);
+        // No change in local addresses means no call to setLocalPrefixes().
+        inOrder.verify(mHardware, never()).setLocalPrefixes(mStringArrayCaptor.capture());
         inOrder.verify(mHardware, times(1)).setUpstreamParameters(
                 eq(testIfName), eq(null), eq(null), eq(null));
         inOrder.verifyNoMoreInteractions();
@@ -200,23 +248,38 @@
         final String ipv4Addr = "192.0.2.5";
         final String linkAddr = ipv4Addr + "/24";
         lp.addLinkAddress(new LinkAddress(linkAddr));
+        lp.addRoute(new RouteInfo(new IpPrefix("192.0.2.0/24")));
         offload.setUpstreamLinkProperties(lp);
+        // IPv4 prefixes and addresses on the upstream are simply left as whole
+        // prefixes (already passed in from UpstreamNetworkMonitor code). If a
+        // tethering client sends traffic to the IPv4 default router or other
+        // clients on the upstream this will not be hardware-forwarded, and that
+        // should be fine for now. Ergo: no change in local addresses, no call
+        // to setLocalPrefixes().
+        inOrder.verify(mHardware, never()).setLocalPrefixes(mStringArrayCaptor.capture());
         inOrder.verify(mHardware, times(1)).setUpstreamParameters(
                 eq(testIfName), eq(ipv4Addr), eq(null), eq(null));
+        inOrder.verify(mHardware, times(1)).getForwardedStats(eq(testIfName));
         inOrder.verifyNoMoreInteractions();
 
         final String ipv4Gateway = "192.0.2.1";
         lp.addRoute(new RouteInfo(InetAddress.getByName(ipv4Gateway)));
         offload.setUpstreamLinkProperties(lp);
+        // No change in local addresses means no call to setLocalPrefixes().
+        inOrder.verify(mHardware, never()).setLocalPrefixes(mStringArrayCaptor.capture());
         inOrder.verify(mHardware, times(1)).setUpstreamParameters(
                 eq(testIfName), eq(ipv4Addr), eq(ipv4Gateway), eq(null));
+        inOrder.verify(mHardware, times(1)).getForwardedStats(eq(testIfName));
         inOrder.verifyNoMoreInteractions();
 
         final String ipv6Gw1 = "fe80::cafe";
         lp.addRoute(new RouteInfo(InetAddress.getByName(ipv6Gw1)));
         offload.setUpstreamLinkProperties(lp);
+        // No change in local addresses means no call to setLocalPrefixes().
+        inOrder.verify(mHardware, never()).setLocalPrefixes(mStringArrayCaptor.capture());
         inOrder.verify(mHardware, times(1)).setUpstreamParameters(
                 eq(testIfName), eq(ipv4Addr), eq(ipv4Gateway), mStringArrayCaptor.capture());
+        inOrder.verify(mHardware, times(1)).getForwardedStats(eq(testIfName));
         ArrayList<String> v6gws = mStringArrayCaptor.getValue();
         assertEquals(1, v6gws.size());
         assertTrue(v6gws.contains(ipv6Gw1));
@@ -225,8 +288,11 @@
         final String ipv6Gw2 = "fe80::d00d";
         lp.addRoute(new RouteInfo(InetAddress.getByName(ipv6Gw2)));
         offload.setUpstreamLinkProperties(lp);
+        // No change in local addresses means no call to setLocalPrefixes().
+        inOrder.verify(mHardware, never()).setLocalPrefixes(mStringArrayCaptor.capture());
         inOrder.verify(mHardware, times(1)).setUpstreamParameters(
                 eq(testIfName), eq(ipv4Addr), eq(ipv4Gateway), mStringArrayCaptor.capture());
+        inOrder.verify(mHardware, times(1)).getForwardedStats(eq(testIfName));
         v6gws = mStringArrayCaptor.getValue();
         assertEquals(2, v6gws.size());
         assertTrue(v6gws.contains(ipv6Gw1));
@@ -240,12 +306,115 @@
         stacked.addRoute(new RouteInfo(InetAddress.getByName("fe80::bad:f00")));
         assertTrue(lp.addStackedLink(stacked));
         offload.setUpstreamLinkProperties(lp);
+        // No change in local addresses means no call to setLocalPrefixes().
+        inOrder.verify(mHardware, never()).setLocalPrefixes(mStringArrayCaptor.capture());
+        inOrder.verify(mHardware, times(1)).setUpstreamParameters(
+                eq(testIfName), eq(ipv4Addr), eq(ipv4Gateway), mStringArrayCaptor.capture());
+        inOrder.verify(mHardware, times(1)).getForwardedStats(eq(testIfName));
+        v6gws = mStringArrayCaptor.getValue();
+        assertEquals(2, v6gws.size());
+        assertTrue(v6gws.contains(ipv6Gw1));
+        assertTrue(v6gws.contains(ipv6Gw2));
+        inOrder.verifyNoMoreInteractions();
+
+        // Add in some IPv6 upstream info. When there is a tethered downstream
+        // making use of the IPv6 prefix we would expect to see the /64 route
+        // removed from "local prefixes" and /128s added for the upstream IPv6
+        // addresses.  This is not yet implemented, and for now we simply
+        // expect to see these /128s.
+        lp.addRoute(new RouteInfo(new IpPrefix("2001:db8::/64")));
+        // "2001:db8::/64" plus "assigned" ASCII in hex
+        lp.addLinkAddress(new LinkAddress("2001:db8::6173:7369:676e:6564/64"));
+        // "2001:db8::/64" plus "random" ASCII in hex
+        lp.addLinkAddress(new LinkAddress("2001:db8::7261:6e64:6f6d/64"));
+        offload.setUpstreamLinkProperties(lp);
+        inOrder.verify(mHardware, times(1)).setLocalPrefixes(mStringArrayCaptor.capture());
+        localPrefixes = mStringArrayCaptor.getValue();
+        assertEquals(6, localPrefixes.size());
+        assertTrue(localPrefixes.contains("127.0.0.0/8"));
+        assertTrue(localPrefixes.contains("192.0.2.0/24"));
+        assertTrue(localPrefixes.contains("fe80::/64"));
+        assertTrue(localPrefixes.contains("2001:db8::/64"));
+        assertTrue(localPrefixes.contains("2001:db8::6173:7369:676e:6564/128"));
+        assertTrue(localPrefixes.contains("2001:db8::7261:6e64:6f6d/128"));
+        // The relevant parts of the LinkProperties have not changed, but at the
+        // moment we do not de-dup upstream LinkProperties this carefully.
         inOrder.verify(mHardware, times(1)).setUpstreamParameters(
                 eq(testIfName), eq(ipv4Addr), eq(ipv4Gateway), mStringArrayCaptor.capture());
         v6gws = mStringArrayCaptor.getValue();
         assertEquals(2, v6gws.size());
         assertTrue(v6gws.contains(ipv6Gw1));
         assertTrue(v6gws.contains(ipv6Gw2));
+        inOrder.verify(mHardware, times(1)).getForwardedStats(eq(testIfName));
         inOrder.verifyNoMoreInteractions();
+
+        // Completely identical LinkProperties updates are de-duped.
+        offload.setUpstreamLinkProperties(lp);
+        // This LinkProperties value does not differ from the default upstream.
+        // There should be no extraneous call to setUpstreamParameters().
+        inOrder.verify(mHardware, never()).setUpstreamParameters(
+                anyObject(), anyObject(), anyObject(), anyObject());
+        inOrder.verifyNoMoreInteractions();
+    }
+
+    private void assertNetworkStats(String iface, ForwardedStats stats, NetworkStats.Entry entry) {
+        assertEquals(iface, entry.iface);
+        assertEquals(stats.rxBytes, entry.rxBytes);
+        assertEquals(stats.txBytes, entry.txBytes);
+        assertEquals(SET_DEFAULT, entry.set);
+        assertEquals(TAG_NONE, entry.tag);
+        assertEquals(UID_TETHERING, entry.uid);
+    }
+
+    @Test
+    public void testGetForwardedStats() throws Exception {
+        setupFunctioningHardwareInterface();
+        enableOffload();
+
+        final OffloadController offload = makeOffloadController();
+        offload.start();
+
+        final String ethernetIface = "eth1";
+        final String mobileIface = "rmnet_data0";
+
+        ForwardedStats ethernetStats = new ForwardedStats();
+        ethernetStats.rxBytes = 12345;
+        ethernetStats.txBytes = 54321;
+
+        ForwardedStats mobileStats = new ForwardedStats();
+        mobileStats.rxBytes = 999;
+        mobileStats.txBytes = 99999;
+
+        when(mHardware.getForwardedStats(eq(ethernetIface))).thenReturn(ethernetStats);
+        when(mHardware.getForwardedStats(eq(mobileIface))).thenReturn(mobileStats);
+
+        final LinkProperties lp = new LinkProperties();
+        lp.setInterfaceName(ethernetIface);
+        offload.setUpstreamLinkProperties(lp);
+
+        lp.setInterfaceName(mobileIface);
+        offload.setUpstreamLinkProperties(lp);
+
+        lp.setInterfaceName(ethernetIface);
+        offload.setUpstreamLinkProperties(lp);
+
+        ethernetStats.rxBytes = 100000;
+        ethernetStats.txBytes = 100000;
+        offload.setUpstreamLinkProperties(null);
+
+        NetworkStats stats = mTetherStatsProviderCaptor.getValue().getTetherStats();
+        assertEquals(2, stats.size());
+
+        NetworkStats.Entry entry = null;
+        int ethernetPosition = ethernetIface.equals(stats.getValues(0, entry).iface) ? 0 : 1;
+        int mobilePosition = 1 - ethernetPosition;
+
+        entry = stats.getValues(mobilePosition, entry);
+        assertNetworkStats(mobileIface, mobileStats, entry);
+
+        ethernetStats.rxBytes = 12345 + 100000;
+        ethernetStats.txBytes = 54321 + 100000;
+        entry = stats.getValues(ethernetPosition, entry);
+        assertNetworkStats(ethernetIface, ethernetStats, entry);
     }
 }
diff --git a/tests/net/java/com/android/server/connectivity/tethering/UpstreamNetworkMonitorTest.java b/tests/net/java/com/android/server/connectivity/tethering/UpstreamNetworkMonitorTest.java
index 69c93b1..c3b9def 100644
--- a/tests/net/java/com/android/server/connectivity/tethering/UpstreamNetworkMonitorTest.java
+++ b/tests/net/java/com/android/server/connectivity/tethering/UpstreamNetworkMonitorTest.java
@@ -324,19 +324,14 @@
     }
 
     @Test
-    public void testOffloadExemptPrefixes() throws Exception {
+    public void testLocalPrefixes() throws Exception {
         mUNM.start();
 
-        // [0] Test minimum set of exempt prefixes.
-        Set<IpPrefix> exempt = mUNM.getOffloadExemptPrefixes();
-        final String[] MINSET = {
-                "127.0.0.0/8", "169.254.0.0/16",
-                "::/3", "fe80::/64", "fc00::/7", "ff00::/8",
-        };
-        assertPrefixSet(exempt, INCLUDES, MINSET);
+        // [0] Test minimum set of local prefixes.
+        Set<IpPrefix> local = mUNM.getLocalPrefixes();
+        assertTrue(local.isEmpty());
+
         final Set<String> alreadySeen = new HashSet<>();
-        Collections.addAll(alreadySeen, MINSET);
-        assertEquals(alreadySeen.size(), exempt.size());
 
         // [1] Pretend Wi-Fi connects.
         final TestNetworkAgent wifiAgent = new TestNetworkAgent(mCM, TRANSPORT_WIFI);
@@ -355,15 +350,15 @@
         wifiAgent.fakeConnect();
         wifiAgent.sendLinkProperties(wifiLp);
 
-        exempt = mUNM.getOffloadExemptPrefixes();
-        assertPrefixSet(exempt, INCLUDES, alreadySeen);
+        local = mUNM.getLocalPrefixes();
+        assertPrefixSet(local, INCLUDES, alreadySeen);
         final String[] wifiLinkPrefixes = {
-                // Excludes link-local as that's already tested within MINSET.
+                // Link-local prefixes are excluded and dealt with elsewhere.
                 "100.112.96.0/20", "2001:db8:4:fd00::/64", "fd6a:a640:60bf:e985::/64",
         };
-        assertPrefixSet(exempt, INCLUDES, wifiLinkPrefixes);
+        assertPrefixSet(local, INCLUDES, wifiLinkPrefixes);
         Collections.addAll(alreadySeen, wifiLinkPrefixes);
-        assertEquals(alreadySeen.size(), exempt.size());
+        assertEquals(alreadySeen.size(), local.size());
 
         // [2] Pretend mobile connects.
         final TestNetworkAgent cellAgent = new TestNetworkAgent(mCM, TRANSPORT_CELLULAR);
@@ -379,12 +374,12 @@
         cellAgent.fakeConnect();
         cellAgent.sendLinkProperties(cellLp);
 
-        exempt = mUNM.getOffloadExemptPrefixes();
-        assertPrefixSet(exempt, INCLUDES, alreadySeen);
+        local = mUNM.getLocalPrefixes();
+        assertPrefixSet(local, INCLUDES, alreadySeen);
         final String[] cellLinkPrefixes = { "10.102.211.32/27", "2001:db8:0:1::/64" };
-        assertPrefixSet(exempt, INCLUDES, cellLinkPrefixes);
+        assertPrefixSet(local, INCLUDES, cellLinkPrefixes);
         Collections.addAll(alreadySeen, cellLinkPrefixes);
-        assertEquals(alreadySeen.size(), exempt.size());
+        assertEquals(alreadySeen.size(), local.size());
 
         // [3] Pretend DUN connects.
         final TestNetworkAgent dunAgent = new TestNetworkAgent(mCM, TRANSPORT_CELLULAR);
@@ -401,21 +396,20 @@
         dunAgent.fakeConnect();
         dunAgent.sendLinkProperties(dunLp);
 
-        exempt = mUNM.getOffloadExemptPrefixes();
-        assertPrefixSet(exempt, INCLUDES, alreadySeen);
+        local = mUNM.getLocalPrefixes();
+        assertPrefixSet(local, INCLUDES, alreadySeen);
         final String[] dunLinkPrefixes = { "192.0.2.32/27", "2001:db8:1:2::/64" };
-        assertPrefixSet(exempt, INCLUDES, dunLinkPrefixes);
+        assertPrefixSet(local, INCLUDES, dunLinkPrefixes);
         Collections.addAll(alreadySeen, dunLinkPrefixes);
-        assertEquals(alreadySeen.size(), exempt.size());
+        assertEquals(alreadySeen.size(), local.size());
 
         // [4] Pretend Wi-Fi disconnected.  It's addresses/prefixes should no
         // longer be included (should be properly removed).
         wifiAgent.fakeDisconnect();
-        exempt = mUNM.getOffloadExemptPrefixes();
-        assertPrefixSet(exempt, INCLUDES, MINSET);
-        assertPrefixSet(exempt, EXCLUDES, wifiLinkPrefixes);
-        assertPrefixSet(exempt, INCLUDES, cellLinkPrefixes);
-        assertPrefixSet(exempt, INCLUDES, dunLinkPrefixes);
+        local = mUNM.getLocalPrefixes();
+        assertPrefixSet(local, EXCLUDES, wifiLinkPrefixes);
+        assertPrefixSet(local, INCLUDES, cellLinkPrefixes);
+        assertPrefixSet(local, INCLUDES, dunLinkPrefixes);
     }
 
     private void assertSatisfiesLegacyType(int legacyType, NetworkState ns) {
diff --git a/tests/radio/src/android/hardware/radio/tests/RadioTest.java b/tests/radio/src/android/hardware/radio/tests/RadioTest.java
index b7a5ac4..aa5780a 100644
--- a/tests/radio/src/android/hardware/radio/tests/RadioTest.java
+++ b/tests/radio/src/android/hardware/radio/tests/RadioTest.java
@@ -18,10 +18,12 @@
 import android.Manifest;
 import android.content.Context;
 import android.content.pm.PackageManager;
+import android.hardware.radio.ProgramSelector;
 import android.hardware.radio.RadioManager;
 import android.hardware.radio.RadioTuner;
 import android.support.test.InstrumentationRegistry;
 import android.support.test.runner.AndroidJUnit4;
+import android.test.suitebuilder.annotation.MediumTest;
 import android.util.Log;
 
 import java.lang.reflect.Constructor;
@@ -42,6 +44,7 @@
 import static org.mockito.Matchers.any;
 import static org.mockito.Matchers.anyInt;
 import static org.mockito.Mockito.after;
+import static org.mockito.Mockito.atLeast;
 import static org.mockito.Mockito.atMost;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.timeout;
@@ -54,6 +57,7 @@
  * A test for broadcast radio API.
  */
 @RunWith(AndroidJUnit4.class)
+@MediumTest
 public class RadioTest {
     private static final String TAG = "RadioTest";
 
@@ -62,6 +66,7 @@
     private final int kConfigCallbackTimeoutMs = 10000;
     private final int kCancelTimeoutMs = 1000;
     private final int kTuneCallbackTimeoutMs = 30000;
+    private final int kFullScanTimeoutMs = 60000;
 
     private RadioManager mRadioManager;
     private RadioTuner mRadioTuner;
@@ -81,7 +86,8 @@
 
         // check if radio is supported and skip the test if it's not
         PackageManager packageManager = mContext.getPackageManager();
-        boolean isRadioSupported = packageManager.hasSystemFeature(PackageManager.FEATURE_RADIO);
+        boolean isRadioSupported = packageManager.hasSystemFeature(
+                PackageManager.FEATURE_BROADCAST_RADIO);
         assumeTrue(isRadioSupported);
 
         // Check radio access permission
@@ -105,7 +111,7 @@
             mRadioTuner.close();
             mRadioTuner = null;
         }
-        verifyNoMoreInteractions(mCallback);
+        resetCallback();
     }
 
     private void openTuner() {
@@ -113,6 +119,7 @@
     }
 
     private void resetCallback() {
+        verify(mCallback, atLeast(0)).onMetadataChanged(any());
         verifyNoMoreInteractions(mCallback);
         Mockito.reset(mCallback);
     }
@@ -141,11 +148,9 @@
         assertNotNull(mRadioTuner);
         verify(mCallback, timeout(kConfigCallbackTimeoutMs)).onConfigurationChanged(any());
         resetCallback();
-    }
 
-    private void checkAntenna() {
-        boolean isConnected = mRadioTuner.isAntennaConnected();
-        assertTrue(isConnected);
+        boolean isAntennaConnected = mRadioTuner.isAntennaConnected();
+        assertTrue(isAntennaConnected);
     }
 
     @Test
@@ -253,7 +258,6 @@
     @Test
     public void testStep() {
         openTuner();
-        checkAntenna();
 
         int ret = mRadioTuner.step(RadioTuner.DIRECTION_DOWN, true);
         assertEquals(RadioManager.STATUS_OK, ret);
@@ -269,7 +273,6 @@
     @Test
     public void testTuneAndGetPI() {
         openTuner();
-        checkAntenna();
 
         int channel = mFmBandConfig.getLowerLimit() + mFmBandConfig.getSpacing();
 
@@ -301,7 +304,6 @@
     @Test
     public void testLateCancel() {
         openTuner();
-        checkAntenna();
 
         int ret = mRadioTuner.step(RadioTuner.DIRECTION_DOWN, false);
         assertEquals(RadioManager.STATUS_OK, ret);
@@ -314,7 +316,6 @@
     @Test
     public void testScanAndCancel() {
         openTuner();
-        checkAntenna();
 
         /* There is a possible race condition between scan and cancel commands - the scan may finish
          * before cancel command is issued. Thus we accept both outcomes in this test.
@@ -332,7 +333,6 @@
     @Test
     public void testStartBackgroundScan() {
         openTuner();
-        checkAntenna();
 
         boolean ret = mRadioTuner.startBackgroundScan();
         boolean isSupported = mModule.isBackgroundScanningSupported();
@@ -342,7 +342,6 @@
     @Test
     public void testGetProgramList() {
         openTuner();
-        checkAntenna();
 
         try {
             List<RadioManager.ProgramInfo> list = mRadioTuner.getProgramList(null);
@@ -354,6 +353,39 @@
     }
 
     @Test
+    public void testTuneFromProgramList() {
+        openTuner();
+
+        List<RadioManager.ProgramInfo> list;
+
+        try {
+            list = mRadioTuner.getProgramList(null);
+            assertNotNull(list);
+        } catch (IllegalStateException e) {
+            Log.i(TAG, "Background list is not ready, trying to fix it");
+
+            boolean success = mRadioTuner.startBackgroundScan();
+            assertTrue(success);
+            verify(mCallback, timeout(kFullScanTimeoutMs)).onBackgroundScanComplete();
+
+            list = mRadioTuner.getProgramList(null);
+            assertNotNull(list);
+        }
+
+        if (list.isEmpty()) {
+            Log.i(TAG, "Program list is empty, can't test tune");
+            return;
+        }
+
+        ProgramSelector sel = list.get(0).getSelector();
+        mRadioTuner.tune(sel);
+        ArgumentCaptor<RadioManager.ProgramInfo> infoc =
+                ArgumentCaptor.forClass(RadioManager.ProgramInfo.class);
+        verify(mCallback, timeout(kTuneCallbackTimeoutMs)).onProgramInfoChanged(infoc.capture());
+        assertEquals(sel, infoc.getValue().getSelector());
+    }
+
+    @Test
     public void testForcedAnalog() {
         openTuner();
 
diff --git a/tools/aapt2/ConfigDescription.cpp b/tools/aapt2/ConfigDescription.cpp
index f16d806..7ff0c72 100644
--- a/tools/aapt2/ConfigDescription.cpp
+++ b/tools/aapt2/ConfigDescription.cpp
@@ -987,8 +987,4 @@
   return !ConflictsWith(o) && !Dominates(o) && !o.Dominates(*this);
 }
 
-::std::ostream& operator<<(::std::ostream& out, const ConfigDescription& o) {
-  return out << o.toString().string();
-}
-
 }  // namespace aapt
diff --git a/tools/aapt2/ConfigDescription.h b/tools/aapt2/ConfigDescription.h
index 8c519a1..65c9617 100644
--- a/tools/aapt2/ConfigDescription.h
+++ b/tools/aapt2/ConfigDescription.h
@@ -24,22 +24,30 @@
 
 namespace aapt {
 
-// Subclass of ResTable_config that adds convenient
-// initialization and comparison methods.
+/*
+ * Subclass of ResTable_config that adds convenient
+ * initialization and comparison methods.
+ */
 struct ConfigDescription : public android::ResTable_config {
-  // Returns an immutable default config.
+  /**
+   * Returns an immutable default config.
+   */
   static const ConfigDescription& DefaultConfig();
 
-  // Parse a string of the form 'fr-sw600dp-land' and fill in the
-  // given ResTable_config with resulting configuration parameters.
-  //
-  // The resulting configuration has the appropriate sdkVersion defined
-  // for backwards compatibility.
+  /*
+   * Parse a string of the form 'fr-sw600dp-land' and fill in the
+   * given ResTable_config with resulting configuration parameters.
+   *
+   * The resulting configuration has the appropriate sdkVersion defined
+   * for backwards compatibility.
+   */
   static bool Parse(const android::StringPiece& str, ConfigDescription* out = nullptr);
 
-  // If the configuration uses an axis that was added after
-  // the original Android release, make sure the SDK version
-  // is set accordingly.
+  /**
+   * If the configuration uses an axis that was added after
+   * the original Android release, make sure the SDK version
+   * is set accordingly.
+   */
   static void ApplyVersionForCompatibility(ConfigDescription* config);
 
   ConfigDescription();
@@ -53,30 +61,38 @@
 
   ConfigDescription CopyWithoutSdkVersion() const;
 
-  // A configuration X dominates another configuration Y, if X has at least the
-  // precedence of Y and X is strictly more general than Y: for any type defined
-  // by X, the same type is defined by Y with a value equal to or, in the case
-  // of ranges, more specific than that of X.
-  //
-  // For example, the configuration 'en-w800dp' dominates 'en-rGB-w1024dp'. It
-  // does not dominate 'fr', 'en-w720dp', or 'mcc001-en-w800dp'.
+  /**
+   * A configuration X dominates another configuration Y, if X has at least the
+   * precedence of Y and X is strictly more general than Y: for any type defined
+   * by X, the same type is defined by Y with a value equal to or, in the case
+   * of ranges, more specific than that of X.
+   *
+   * For example, the configuration 'en-w800dp' dominates 'en-rGB-w1024dp'. It
+   * does not dominate 'fr', 'en-w720dp', or 'mcc001-en-w800dp'.
+   */
   bool Dominates(const ConfigDescription& o) const;
 
-  // Returns true if this configuration defines a more important configuration
-  // parameter than o. For example, "en" has higher precedence than "v23",
-  // whereas "en" has the same precedence as "en-v23".
+  /**
+   * Returns true if this configuration defines a more important configuration
+   * parameter than o. For example, "en" has higher precedence than "v23",
+   * whereas "en" has the same precedence as "en-v23".
+   */
   bool HasHigherPrecedenceThan(const ConfigDescription& o) const;
 
-  // A configuration conflicts with another configuration if both
-  // configurations define an incompatible configuration parameter. An
-  // incompatible configuration parameter is a non-range, non-density parameter
-  // that is defined in both configurations as a different, non-default value.
+  /**
+   * A configuration conflicts with another configuration if both
+   * configurations define an incompatible configuration parameter. An
+   * incompatible configuration parameter is a non-range, non-density parameter
+   * that is defined in both configurations as a different, non-default value.
+   */
   bool ConflictsWith(const ConfigDescription& o) const;
 
-  // A configuration is compatible with another configuration if both
-  // configurations can match a common concrete device configuration and are
-  // unrelated by domination. For example, land-v11 conflicts with port-v21
-  // but is compatible with v21 (both land-v11 and v21 would match en-land-v23).
+  /**
+   * A configuration is compatible with another configuration if both
+   * configurations can match a common concrete device configuration and are
+   * unrelated by domination. For example, land-v11 conflicts with port-v21
+   * but is compatible with v21 (both land-v11 and v21 would match en-land-v23).
+   */
   bool IsCompatibleWith(const ConfigDescription& o) const;
 
   bool MatchWithDensity(const ConfigDescription& o) const;
@@ -89,8 +105,6 @@
   bool operator>(const ConfigDescription& o) const;
 };
 
-::std::ostream& operator<<(::std::ostream& out, const ConfigDescription& o);
-
 inline ConfigDescription::ConfigDescription() {
   memset(this, 0, sizeof(*this));
   size = sizeof(android::ResTable_config);
@@ -109,13 +123,15 @@
   *this = o;
 }
 
-inline ConfigDescription& ConfigDescription::operator=(const android::ResTable_config& o) {
+inline ConfigDescription& ConfigDescription::operator=(
+    const android::ResTable_config& o) {
   *static_cast<android::ResTable_config*>(this) = o;
   size = sizeof(android::ResTable_config);
   return *this;
 }
 
-inline ConfigDescription& ConfigDescription::operator=(const ConfigDescription& o) {
+inline ConfigDescription& ConfigDescription::operator=(
+    const ConfigDescription& o) {
   *static_cast<android::ResTable_config*>(this) = o;
   return *this;
 }
@@ -125,7 +141,8 @@
   return *this;
 }
 
-inline bool ConfigDescription::MatchWithDensity(const ConfigDescription& o) const {
+inline bool ConfigDescription::MatchWithDensity(
+    const ConfigDescription& o) const {
   return match(o) && (density == 0 || density == o.density);
 }
 
@@ -153,6 +170,11 @@
   return compare(o) > 0;
 }
 
+inline ::std::ostream& operator<<(::std::ostream& out,
+                                  const ConfigDescription& o) {
+  return out << o.toString().string();
+}
+
 }  // namespace aapt
 
 #endif  // AAPT_CONFIG_DESCRIPTION_H
diff --git a/tools/aapt2/cmd/Link.cpp b/tools/aapt2/cmd/Link.cpp
index 353d734..e6bf3a6 100644
--- a/tools/aapt2/cmd/Link.cpp
+++ b/tools/aapt2/cmd/Link.cpp
@@ -60,7 +60,6 @@
 #include "unflatten/BinaryResourceParser.h"
 #include "util/Files.h"
 #include "xml/XmlDom.h"
-#include "xml/XmlUtil.h"
 
 using android::StringPiece;
 using android::base::StringPrintf;
@@ -343,18 +342,16 @@
     ConfigDescription config;
 
     // The entry this file came from.
-    ResourceEntry* entry = nullptr;
+    ResourceEntry* entry;
 
     // The file to copy as-is.
-    io::IFile* file_to_copy = nullptr;
+    io::IFile* file_to_copy;
 
     // The XML to process and flatten.
     std::unique_ptr<xml::XmlResource> xml_to_flatten;
 
     // The destination to write this file to.
     std::string dst_path;
-
-    bool skip_versioning = false;
   };
 
   uint32_t GetCompressionFlags(const StringPiece& str);
@@ -434,6 +431,19 @@
   return ArchiveEntry::kCompress;
 }
 
+static bool IsTransitionElement(const std::string& name) {
+  return name == "fade" || name == "changeBounds" || name == "slide" || name == "explode" ||
+         name == "changeImageTransform" || name == "changeTransform" ||
+         name == "changeClipBounds" || name == "autoTransition" || name == "recolor" ||
+         name == "changeScroll" || name == "transitionSet" || name == "transition" ||
+         name == "transitionManager";
+}
+
+static bool IsVectorElement(const std::string& name) {
+  return name == "vector" || name == "animated-vector" || name == "pathInterpolator" ||
+         name == "objectAnimator";
+}
+
 template <typename T>
 std::vector<T> make_singleton_vec(T&& val) {
   std::vector<T> vec;
@@ -466,10 +476,21 @@
     }
   }
 
-  if (options_.no_auto_version || file_op->skip_versioning) {
+  if (options_.no_auto_version) {
     return make_singleton_vec(std::move(file_op->xml_to_flatten));
   }
 
+  if (options_.no_version_vectors || options_.no_version_transitions) {
+    // Skip this if it is a vector or animated-vector.
+    xml::Element* el = xml::FindRootElement(doc);
+    if (el && el->namespace_uri.empty()) {
+      if ((options_.no_version_vectors && IsVectorElement(el->name)) ||
+          (options_.no_version_transitions && IsTransitionElement(el->name))) {
+        return make_singleton_vec(std::move(file_op->xml_to_flatten));
+      }
+    }
+  }
+
   const ConfigDescription& config = file_op->config;
   ResourceEntry* entry = file_op->entry;
 
@@ -483,26 +504,15 @@
   bool error = false;
   std::map<std::pair<ConfigDescription, StringPiece>, FileOperation> config_sorted_files;
 
-  int tag_version_options = 0;
-  if (options_.no_version_vectors) {
-    tag_version_options |= xml::kNoVersionVector;
-  }
-
-  if (options_.no_version_transitions) {
-    tag_version_options |= xml::kNoVersionTransition;
-  }
-
   for (auto& pkg : table->packages) {
     for (auto& type : pkg->types) {
       // Sort by config and name, so that we get better locality in the zip file.
       config_sorted_files.clear();
+      std::queue<FileOperation> file_operations;
 
       // Populate the queue with all files in the ResourceTable.
       for (auto& entry : type->entries) {
-        const auto values_end = entry->values.end();
-        for (auto values_iter = entry->values.begin(); values_iter != values_end; ++values_iter) {
-          ResourceConfigValue* config_value = values_iter->get();
-
+        for (auto& config_value : entry->values) {
           // WARNING! Do not insert or remove any resources while executing in this scope. It will
           // corrupt the iteration order.
 
@@ -544,44 +554,6 @@
             file_op.xml_to_flatten->file.config = config_value->config;
             file_op.xml_to_flatten->file.source = file_ref->GetSource();
             file_op.xml_to_flatten->file.name = ResourceName(pkg->name, type->type, entry->name);
-
-            // Check if this file needs to be versioned based on tag rules.
-            xml::Element* root_el = xml::FindRootElement(file_op.xml_to_flatten.get());
-            if (root_el == nullptr) {
-              context_->GetDiagnostics()->Error(DiagMessage(file->GetSource())
-                                                << "failed to find the root XML element");
-              return false;
-            }
-
-            if (root_el->namespace_uri.empty()) {
-              if (Maybe<xml::TagApiVersionResult> result =
-                      xml::GetXmlTagApiVersion(root_el->name, tag_version_options)) {
-                file_op.skip_versioning = result.value().skip_version;
-                if (result.value().api_version && config_value->config.sdkVersion == 0u) {
-                  const ApiVersion min_tag_version = result.value().api_version.value();
-                  // Only version it if it doesn't specify its own version and the version is
-                  // greater than the minSdk.
-                  const util::Range<ApiVersion> valid_range{
-                      context_->GetMinSdkVersion() + 1,
-                      FindNextApiVersionForConfigInSortedVector(values_iter, values_end)};
-                  if (valid_range.Contains(min_tag_version)) {
-                    // Update the configurations. The iteration order will not be affected
-                    // since sdkVersions in ConfigDescriptions are the last property compared
-                    // in the sort function.
-                    if (context_->IsVerbose()) {
-                      context_->GetDiagnostics()->Note(DiagMessage(config_value->value->GetSource())
-                                                       << "auto-versioning XML resource to API "
-                                                       << min_tag_version);
-                    }
-                    const_cast<ConfigDescription&>(config_value->config).sdkVersion =
-                        static_cast<uint16_t>(min_tag_version);
-                    file_op.config.sdkVersion = static_cast<uint16_t>(min_tag_version);
-                    file_op.xml_to_flatten->file.config.sdkVersion =
-                        static_cast<uint16_t>(min_tag_version);
-                  }
-                }
-              }
-            }
           }
 
           // NOTE(adamlesinski): Explicitly construct a StringPiece here, or
diff --git a/tools/aapt2/configuration/ConfigurationParser.cpp b/tools/aapt2/configuration/ConfigurationParser.cpp
index 0b6743c..d051120 100644
--- a/tools/aapt2/configuration/ConfigurationParser.cpp
+++ b/tools/aapt2/configuration/ConfigurationParser.cpp
@@ -29,6 +29,7 @@
 #include "Diagnostics.h"
 #include "io/File.h"
 #include "io/FileSystem.h"
+#include "util/Maybe.h"
 #include "util/Util.h"
 #include "xml/XmlActionExecutor.h"
 #include "xml/XmlDom.h"
@@ -109,6 +110,61 @@
   return kAbiToStringMap.find(abi)->second;
 }
 
+/**
+ * Attempts to replace the placeholder in the name string with the provided value. Returns true on
+ * success, or false if the either the placeholder is not found in the name, or the value is not
+ * present and the placeholder was.
+ */
+static bool ReplacePlaceholder(const std::string& placeholder, const Maybe<std::string>& value,
+                               std::string* name, IDiagnostics* diag) {
+  size_t offset = name->find(placeholder);
+  if (value) {
+    if (offset == std::string::npos) {
+      diag->Error(DiagMessage() << "Missing placeholder for artifact: " << placeholder);
+      return false;
+    }
+    name->replace(offset, placeholder.length(), value.value());
+    return true;
+  }
+
+  // Make sure the placeholder was not present if the desired value was not present.
+  bool result = (offset == std::string::npos);
+  if (!result) {
+    diag->Error(DiagMessage() << "Placeholder present but no value for artifact: " << placeholder);
+  }
+  return result;
+}
+
+Maybe<std::string> Artifact::ToArtifactName(const std::string& format, IDiagnostics* diag) const {
+  std::string result = format;
+
+  if (!ReplacePlaceholder("{abi}", abi_group, &result, diag)) {
+    return {};
+  }
+
+  if (!ReplacePlaceholder("{density}", screen_density_group, &result, diag)) {
+    return {};
+  }
+
+  if (!ReplacePlaceholder("{locale}", locale_group, &result, diag)) {
+    return {};
+  }
+
+  if (!ReplacePlaceholder("{sdk}", android_sdk_group, &result, diag)) {
+    return {};
+  }
+
+  if (!ReplacePlaceholder("{feature}", device_feature_group, &result, diag)) {
+    return {};
+  }
+
+  if (!ReplacePlaceholder("{gl}", gl_texture_group, &result, diag)) {
+    return {};
+  }
+
+  return result;
+}
+
 }  // namespace configuration
 
 /** Returns a ConfigurationParser for the file located at the provided path. */
diff --git a/tools/aapt2/configuration/ConfigurationParser.h b/tools/aapt2/configuration/ConfigurationParser.h
index 3fae5dd..28c355e 100644
--- a/tools/aapt2/configuration/ConfigurationParser.h
+++ b/tools/aapt2/configuration/ConfigurationParser.h
@@ -20,8 +20,9 @@
 #include <string>
 #include <unordered_map>
 #include <vector>
-#include <ConfigDescription.h>
 
+#include "ConfigDescription.h"
+#include "Diagnostics.h"
 #include "util/Maybe.h"
 
 namespace aapt {
@@ -48,6 +49,9 @@
   Maybe<std::string> device_feature_group;
   /** If present, uses the OpenGL texture group with this name. */
   Maybe<std::string> gl_texture_group;
+
+  /** Convert an artifact name template into a name string based on configuration contents. */
+  Maybe<std::string> ToArtifactName(const std::string& format, IDiagnostics* diag) const;
 };
 
 /** Enumeration of currently supported ABIs. */
@@ -212,4 +216,4 @@
 
 }  // namespace aapt
 
-#endif //AAPT2_CONFIGURATION_H
+#endif  // AAPT2_CONFIGURATION_H
diff --git a/tools/aapt2/configuration/ConfigurationParser_test.cpp b/tools/aapt2/configuration/ConfigurationParser_test.cpp
index a4fa134..fb71e98 100644
--- a/tools/aapt2/configuration/ConfigurationParser_test.cpp
+++ b/tools/aapt2/configuration/ConfigurationParser_test.cpp
@@ -32,6 +32,7 @@
 using android::ResTable_config;
 using configuration::Abi;
 using configuration::AndroidSdk;
+using configuration::Artifact;
 using configuration::PostProcessingConfiguration;
 using configuration::DeviceFeature;
 using configuration::GlTexture;
@@ -417,5 +418,55 @@
   ASSERT_THAT(out, ElementsAre(low_latency, pro));
 }
 
+TEST(ArtifactTest, Simple) {
+  StdErrDiagnostics diag;
+  Artifact x86;
+  x86.abi_group = {"x86"};
+
+  auto x86_result = x86.ToArtifactName("something.{abi}.apk", &diag);
+  ASSERT_TRUE(x86_result);
+  EXPECT_EQ(x86_result.value(), "something.x86.apk");
+
+  Artifact arm;
+  arm.abi_group = {"armeabi-v7a"};
+
+  auto arm_result = arm.ToArtifactName("app.{abi}.apk", &diag);
+  ASSERT_TRUE(arm_result);
+  EXPECT_EQ(arm_result.value(), "app.armeabi-v7a.apk");
+}
+
+TEST(ArtifactTest, Complex) {
+  StdErrDiagnostics diag;
+  Artifact artifact;
+  artifact.abi_group = {"mips64"};
+  artifact.screen_density_group = {"ldpi"};
+  artifact.device_feature_group = {"df1"};
+  artifact.gl_texture_group = {"glx1"};
+  artifact.locale_group = {"en-AU"};
+  artifact.android_sdk_group = {"26"};
+
+  auto result =
+      artifact.ToArtifactName("app.{density}_{locale}_{feature}_{gl}.sdk{sdk}.{abi}.apk", &diag);
+  ASSERT_TRUE(result);
+  EXPECT_EQ(result.value(), "app.ldpi_en-AU_df1_glx1.sdk26.mips64.apk");
+}
+
+TEST(ArtifactTest, Missing) {
+  StdErrDiagnostics diag;
+  Artifact x86;
+  x86.abi_group = {"x86"};
+
+  EXPECT_FALSE(x86.ToArtifactName("something.{density}.apk", &diag));
+  EXPECT_FALSE(x86.ToArtifactName("something.apk", &diag));
+}
+
+TEST(ArtifactTest, Empty) {
+  StdErrDiagnostics diag;
+  Artifact artifact;
+
+  EXPECT_FALSE(artifact.ToArtifactName("something.{density}.apk", &diag));
+  EXPECT_TRUE(artifact.ToArtifactName("something.apk", &diag));
+}
+
 }  // namespace
 }  // namespace aapt
diff --git a/tools/aapt2/integration-tests/AppOne/res/drawable/adaptive-icon.xml b/tools/aapt2/integration-tests/AppOne/res/drawable/adaptive-icon.xml
deleted file mode 100644
index e3cda7e..0000000
--- a/tools/aapt2/integration-tests/AppOne/res/drawable/adaptive-icon.xml
+++ /dev/null
@@ -1,20 +0,0 @@
-<?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.
--->
-
-<adaptive-icon xmlns:android="http://schema.android.com/apk/res/android">
-    <background android:drawable="@android:color/white" />
-    <foreground android:drawable="@drawable/image" />
-</adaptive-icon>
diff --git a/tools/aapt2/link/AutoVersioner.cpp b/tools/aapt2/link/AutoVersioner.cpp
index 4ac70d9..f80c6e9 100644
--- a/tools/aapt2/link/AutoVersioner.cpp
+++ b/tools/aapt2/link/AutoVersioner.cpp
@@ -34,19 +34,6 @@
   return sdk_version_to_generate < FindNextApiVersionForConfig(entry, config);
 }
 
-ApiVersion FindNextApiVersionForConfigInSortedVector(
-    std::vector<std::unique_ptr<ResourceConfigValue>>::const_iterator start,
-    std::vector<std::unique_ptr<ResourceConfigValue>>::const_iterator end) {
-  const ConfigDescription start_config = (*start)->config.CopyWithoutSdkVersion();
-  ++start;
-  if (start != end) {
-    if ((*start)->config.CopyWithoutSdkVersion() == start_config) {
-      return static_cast<ApiVersion>((*start)->config.sdkVersion);
-    }
-  }
-  return std::numeric_limits<ApiVersion>::max();
-}
-
 ApiVersion FindNextApiVersionForConfig(const ResourceEntry* entry,
                                        const ConfigDescription& config) {
   const auto end_iter = entry->values.end();
@@ -59,7 +46,25 @@
 
   // The source config came from this list, so it should be here.
   CHECK(iter != entry->values.end());
-  return FindNextApiVersionForConfigInSortedVector(iter, end_iter);
+  ++iter;
+
+  // The next configuration either only varies in sdkVersion, or it is completely different
+  // and therefore incompatible. If it is incompatible, we must generate the versioned resource.
+
+  // NOTE: The ordering of configurations takes sdkVersion as higher precedence than other
+  // qualifiers, so we need to iterate through the entire list to be sure there
+  // are no higher sdk level versions of this resource.
+  ConfigDescription temp_config(config);
+  for (; iter != end_iter; ++iter) {
+    temp_config.sdkVersion = (*iter)->config.sdkVersion;
+    if (temp_config == (*iter)->config) {
+      // The two configs are the same, return the sdkVersion.
+      return (*iter)->config.sdkVersion;
+    }
+  }
+
+  // Didn't find another config with a different sdk version, so return the highest possible value.
+  return std::numeric_limits<ApiVersion>::max();
 }
 
 bool AutoVersioner::Consume(IAaptContext* context, ResourceTable* table) {
diff --git a/tools/aapt2/link/AutoVersioner_test.cpp b/tools/aapt2/link/AutoVersioner_test.cpp
index 88a831b..49639f8 100644
--- a/tools/aapt2/link/AutoVersioner_test.cpp
+++ b/tools/aapt2/link/AutoVersioner_test.cpp
@@ -42,8 +42,8 @@
 
   ResourceEntry entry("foo");
   entry.values.push_back(util::make_unique<ResourceConfigValue>(ConfigDescription::DefaultConfig(), ""));
-  entry.values.push_back(util::make_unique<ResourceConfigValue>(v21_config, ""));
   entry.values.push_back(util::make_unique<ResourceConfigValue>(sw600dp_v13_config, ""));
+  entry.values.push_back(util::make_unique<ResourceConfigValue>(v21_config, ""));
 
   EXPECT_TRUE(ShouldGenerateVersionedResource(&entry, ConfigDescription::DefaultConfig(), 17));
   EXPECT_FALSE(ShouldGenerateVersionedResource(&entry, ConfigDescription::DefaultConfig(), 22));
diff --git a/tools/aapt2/link/Linkers.h b/tools/aapt2/link/Linkers.h
index 493b6b1..5527f90 100644
--- a/tools/aapt2/link/Linkers.h
+++ b/tools/aapt2/link/Linkers.h
@@ -23,7 +23,6 @@
 #include "android-base/macros.h"
 
 #include "Resource.h"
-#include "ResourceTable.h"
 #include "SdkConstants.h"
 #include "process/IResourceTableConsumer.h"
 #include "xml/XmlDom.h"
@@ -42,19 +41,17 @@
   ResourceNameRef resource;
 };
 
-// Determines whether a versioned resource should be created. If a versioned resource already
-// exists, it takes precedence.
+/**
+ * Determines whether a versioned resource should be created. If a versioned
+ * resource already exists, it takes precedence.
+ */
 bool ShouldGenerateVersionedResource(const ResourceEntry* entry, const ConfigDescription& config,
                                      const ApiVersion sdk_version_to_generate);
 
-// Finds the next largest ApiVersion of `config` for values defined for `entry`.
+// Finds the next largest ApiVersion of the config which is identical to the given config except
+// for sdkVersion.
 ApiVersion FindNextApiVersionForConfig(const ResourceEntry* entry, const ConfigDescription& config);
 
-// Finds the next largest ApiVersion of the config pointed to by the iterator `start`.
-ApiVersion FindNextApiVersionForConfigInSortedVector(
-    std::vector<std::unique_ptr<ResourceConfigValue>>::const_iterator start,
-    std::vector<std::unique_ptr<ResourceConfigValue>>::const_iterator end);
-
 class AutoVersioner : public IResourceTableConsumer {
  public:
   AutoVersioner() = default;
diff --git a/tools/aapt2/readme.md b/tools/aapt2/readme.md
index e128c13..c8d3617 100644
--- a/tools/aapt2/readme.md
+++ b/tools/aapt2/readme.md
@@ -9,7 +9,6 @@
   (bug 62839863)
 - Fixed issue where Java classes referenced from fragments and menus were not added to
   the set of Proguard keep rules. (bug 62216174)
-- Automatically version XML `<adaptive-icon>` resources to v26. (bug 62316340)
 - Fixed issue where escaped unicode characters would generate malformed UTF-8. (bug 62839202)
 - Fixed issue where apostrophes or quotes used in XML attribute values were ignored.
   (bug 62840406, 62840718)
diff --git a/tools/aapt2/util/TypeTraits.h b/tools/aapt2/util/TypeTraits.h
index 6fcb4bdb..b6539ed 100644
--- a/tools/aapt2/util/TypeTraits.h
+++ b/tools/aapt2/util/TypeTraits.h
@@ -38,7 +38,6 @@
 
 DEFINE_HAS_BINARY_OP_TRAIT(has_eq_op, ==);
 DEFINE_HAS_BINARY_OP_TRAIT(has_lt_op, <);
-DEFINE_HAS_BINARY_OP_TRAIT(has_lte_op, <=);
 
 /**
  * Type trait that checks if two types can be equated (==) and compared (<).
diff --git a/tools/aapt2/util/Util.h b/tools/aapt2/util/Util.h
index 410258c..b9ada77 100644
--- a/tools/aapt2/util/Util.h
+++ b/tools/aapt2/util/Util.h
@@ -48,11 +48,6 @@
 struct Range {
   T start;
   T end;
-
-  typename std::enable_if<has_lte_op<const T, const T>::value, bool>::type Contains(
-      const T& t) const {
-    return start <= t && t < end;
-  }
 };
 
 std::vector<std::string> Split(const android::StringPiece& str, char sep);
diff --git a/tools/aapt2/xml/XmlUtil.cpp b/tools/aapt2/xml/XmlUtil.cpp
index fa1b0f0..fb8cee8 100644
--- a/tools/aapt2/xml/XmlUtil.cpp
+++ b/tools/aapt2/xml/XmlUtil.cpp
@@ -80,65 +80,5 @@
   }
 }
 
-namespace {
-
-struct TagCompat {
-  ApiVersion api_version;
-
-  enum class XmlType {
-    kVector,
-    kTransition,
-    kAdaptiveIcon,
-  };
-
-  XmlType type;
-};
-
-std::unordered_map<StringPiece, TagCompat> sTagVersions = {
-    {"fade", {SDK_LOLLIPOP, TagCompat::XmlType::kTransition}},
-    {"changeBounds", {SDK_LOLLIPOP, TagCompat::XmlType::kTransition}},
-    {"slide", {SDK_LOLLIPOP, TagCompat::XmlType::kTransition}},
-    {"explode", {SDK_LOLLIPOP, TagCompat::XmlType::kTransition}},
-    {"changeImageTransform", {SDK_LOLLIPOP, TagCompat::XmlType::kTransition}},
-    {"changeTransform", {SDK_LOLLIPOP, TagCompat::XmlType::kTransition}},
-    {"changeClipBounds", {SDK_LOLLIPOP, TagCompat::XmlType::kTransition}},
-    {"autoTransition", {SDK_LOLLIPOP, TagCompat::XmlType::kTransition}},
-    {"recolor", {SDK_LOLLIPOP, TagCompat::XmlType::kTransition}},
-    {"changeScroll", {SDK_LOLLIPOP, TagCompat::XmlType::kTransition}},
-    {"transitionSet", {SDK_LOLLIPOP, TagCompat::XmlType::kTransition}},
-    {"transition", {SDK_LOLLIPOP, TagCompat::XmlType::kTransition}},
-    {"transitionManager", {SDK_LOLLIPOP, TagCompat::XmlType::kTransition}},
-
-    {"vector", {SDK_LOLLIPOP, TagCompat::XmlType::kVector}},
-    {"animated-vector", {SDK_LOLLIPOP, TagCompat::XmlType::kVector}},
-    {"pathInterpolator", {SDK_LOLLIPOP, TagCompat::XmlType::kVector}},
-    {"objectAnimator", {SDK_LOLLIPOP, TagCompat::XmlType::kVector}},
-
-    {"adaptive-icon", {SDK_O, TagCompat::XmlType::kAdaptiveIcon}},
-};
-
-}  // namespace
-
-Maybe<TagApiVersionResult> GetXmlTagApiVersion(const StringPiece& tag_name, int options) {
-  auto iter = sTagVersions.find(tag_name);
-  if (iter == sTagVersions.end()) {
-    return {};
-  }
-
-  const TagCompat& tag_compat = iter->second;
-  if (options & kNoVersionVector) {
-    if (tag_compat.type == TagCompat::XmlType::kVector) {
-      return TagApiVersionResult{{}, true /*skip_version*/};
-    }
-  }
-
-  if (options & kNoVersionTransition) {
-    if (tag_compat.type == TagCompat::XmlType::kTransition) {
-      return TagApiVersionResult{{}, true /*skip_version*/};
-    }
-  }
-  return TagApiVersionResult{tag_compat.api_version, false /*skip_version*/};
-}
-
 }  // namespace xml
 }  // namespace aapt
diff --git a/tools/aapt2/xml/XmlUtil.h b/tools/aapt2/xml/XmlUtil.h
index 552f42a..1650ac2 100644
--- a/tools/aapt2/xml/XmlUtil.h
+++ b/tools/aapt2/xml/XmlUtil.h
@@ -20,16 +20,18 @@
 #include <string>
 
 #include "ResourceValues.h"
-#include "SdkConstants.h"
 #include "util/Maybe.h"
 
 namespace aapt {
 namespace xml {
 
 constexpr const char* kSchemaAuto = "http://schemas.android.com/apk/res-auto";
-constexpr const char* kSchemaPublicPrefix = "http://schemas.android.com/apk/res/";
-constexpr const char* kSchemaPrivatePrefix = "http://schemas.android.com/apk/prv/res/";
-constexpr const char* kSchemaAndroid = "http://schemas.android.com/apk/res/android";
+constexpr const char* kSchemaPublicPrefix =
+    "http://schemas.android.com/apk/res/";
+constexpr const char* kSchemaPrivatePrefix =
+    "http://schemas.android.com/apk/prv/res/";
+constexpr const char* kSchemaAndroid =
+    "http://schemas.android.com/apk/res/android";
 constexpr const char* kSchemaTools = "http://schemas.android.com/tools";
 constexpr const char* kSchemaAapt = "http://schemas.android.com/aapt";
 
@@ -100,24 +102,6 @@
 void TransformReferenceFromNamespace(IPackageDeclStack* decl_stack,
                                      const android::StringPiece& local_package, Reference* in_ref);
 
-struct TagApiVersionResult {
-  // If set, the API version to apply.
-  Maybe<ApiVersion> api_version;
-
-  // Whether to skip any auto-versioning.
-  bool skip_version;
-};
-
-enum TagVersionOptions : int {
-  // Skip versioning XML resources that deal with vector drawables.
-  kNoVersionVector,
-
-  // Skip versioning XML resources that deal with transitions.
-  kNoVersionTransition,
-};
-
-Maybe<TagApiVersionResult> GetXmlTagApiVersion(const android::StringPiece& tag_name, int options);
-
 }  // namespace xml
 }  // namespace aapt
 
diff --git a/wifi/java/android/net/wifi/WifiConfiguration.java b/wifi/java/android/net/wifi/WifiConfiguration.java
index e7fbe4f..13d54ab 100644
--- a/wifi/java/android/net/wifi/WifiConfiguration.java
+++ b/wifi/java/android/net/wifi/WifiConfiguration.java
@@ -245,6 +245,7 @@
      * (e.g., {@code 01a243f405}).
      */
     public String SSID;
+
     /**
      * When set, this network configuration entry should only be used when
      * associating with the AP having the specified BSSID. The value is
@@ -740,22 +741,58 @@
     }
 
     /**
+     * Indicates if the creator of this configuration has expressed that it
+     * should be considered metered.
+     *
+     * @see #isMetered(WifiConfiguration, WifiInfo)
      * @hide
-     * A hint about whether or not the network represented by this WifiConfiguration
-     * is metered. This is hinted at via the meteredHint bit on DHCP results set in
-     * {@link com.android.server.wifi.WifiStateMachine}, or via a network score in
-     * {@link com.android.server.wifi.ExternalScoreEvaluator}.
      */
     @SystemApi
     public boolean meteredHint;
 
+    /** {@hide} */
+    public static final int METERED_OVERRIDE_NONE = 0;
+    /** {@hide} */
+    public static final int METERED_OVERRIDE_METERED = 1;
+    /** {@hide} */
+    public static final int METERED_OVERRIDE_NOT_METERED = 2;
+
     /**
+     * Indicates if the end user has expressed an explicit opinion about the
+     * meteredness of this network, such as through the Settings app.
+     * <p>
+     * This should always override any values from {@link #meteredHint} or
+     * {@link WifiInfo#getMeteredHint()}.
+     *
+     * @see #isMetered(WifiConfiguration, WifiInfo)
      * @hide
-     * Indicates if a user has specified the WifiConfiguration to be metered. Users
-     * can toggle if a network is metered within Settings -> Data Usage -> Network
-     * Restrictions.
      */
-    public boolean meteredOverride;
+    public int meteredOverride = METERED_OVERRIDE_NONE;
+
+    /**
+     * Blend together all the various opinions to decide if the given network
+     * should be considered metered or not.
+     *
+     * @hide
+     */
+    public static boolean isMetered(WifiConfiguration config, WifiInfo info) {
+        boolean metered = false;
+        if (info != null && info.getMeteredHint()) {
+            metered = true;
+        }
+        if (config != null && config.meteredHint) {
+            metered = true;
+        }
+        if (config != null
+                && config.meteredOverride == WifiConfiguration.METERED_OVERRIDE_METERED) {
+            metered = true;
+        }
+        if (config != null
+                && config.meteredOverride == WifiConfiguration.METERED_OVERRIDE_NOT_METERED) {
+            metered = false;
+        }
+        return metered;
+    }
 
     /**
      * @hide
@@ -1426,7 +1463,7 @@
         didSelfAdd = false;
         ephemeral = false;
         meteredHint = false;
-        meteredOverride = false;
+        meteredOverride = METERED_OVERRIDE_NONE;
         useExternalScores = false;
         validatedInternetAccess = false;
         mIpConfiguration = new IpConfiguration();
@@ -1520,23 +1557,24 @@
             sbuf.append(this.numNoInternetAccessReports).append("\n");
         }
         if (this.updateTime != null) {
-            sbuf.append("update ").append(this.updateTime).append("\n");
+            sbuf.append(" update ").append(this.updateTime).append("\n");
         }
         if (this.creationTime != null) {
-            sbuf.append("creation").append(this.creationTime).append("\n");
+            sbuf.append(" creation ").append(this.creationTime).append("\n");
         }
         if (this.didSelfAdd) sbuf.append(" didSelfAdd");
         if (this.selfAdded) sbuf.append(" selfAdded");
         if (this.validatedInternetAccess) sbuf.append(" validatedInternetAccess");
         if (this.ephemeral) sbuf.append(" ephemeral");
         if (this.meteredHint) sbuf.append(" meteredHint");
-        if (this.meteredOverride) sbuf.append(" meteredOverride");
         if (this.useExternalScores) sbuf.append(" useExternalScores");
         if (this.didSelfAdd || this.selfAdded || this.validatedInternetAccess
-            || this.ephemeral || this.meteredHint || this.meteredOverride
-            || this.useExternalScores) {
+            || this.ephemeral || this.meteredHint || this.useExternalScores) {
             sbuf.append("\n");
         }
+        if (this.meteredOverride != METERED_OVERRIDE_NONE) {
+            sbuf.append(" meteredOverride ").append(meteredOverride).append("\n");
+        }
         sbuf.append(" KeyMgmt:");
         for (int k = 0; k < this.allowedKeyManagement.size(); k++) {
             if (this.allowedKeyManagement.get(k)) {
@@ -2062,7 +2100,7 @@
         dest.writeInt(isLegacyPasspointConfig ? 1 : 0);
         dest.writeInt(ephemeral ? 1 : 0);
         dest.writeInt(meteredHint ? 1 : 0);
-        dest.writeInt(meteredOverride ? 1 : 0);
+        dest.writeInt(meteredOverride);
         dest.writeInt(useExternalScores ? 1 : 0);
         dest.writeInt(creatorUid);
         dest.writeInt(lastConnectUid);
@@ -2129,7 +2167,7 @@
                 config.isLegacyPasspointConfig = in.readInt() != 0;
                 config.ephemeral = in.readInt() != 0;
                 config.meteredHint = in.readInt() != 0;
-                config.meteredOverride = in.readInt() != 0;
+                config.meteredOverride = in.readInt();
                 config.useExternalScores = in.readInt() != 0;
                 config.creatorUid = in.readInt();
                 config.lastConnectUid = in.readInt();
diff --git a/wifi/java/android/net/wifi/WifiInfo.java b/wifi/java/android/net/wifi/WifiInfo.java
index e48f7bd..bd25356 100644
--- a/wifi/java/android/net/wifi/WifiInfo.java
+++ b/wifi/java/android/net/wifi/WifiInfo.java
@@ -459,7 +459,13 @@
         return mMacAddress != null && !DEFAULT_MAC_ADDRESS.equals(mMacAddress);
     }
 
-    /** {@hide} */
+    /**
+     * Indicates if we've dynamically detected this active network connection as
+     * being metered.
+     *
+     * @see WifiConfiguration#isMetered(WifiConfiguration, WifiInfo)
+     * @hide
+     */
     public void setMeteredHint(boolean meteredHint) {
         mMeteredHint = meteredHint;
     }
diff --git a/wifi/java/android/net/wifi/WifiManager.java b/wifi/java/android/net/wifi/WifiManager.java
index 7173775..598360c 100644
--- a/wifi/java/android/net/wifi/WifiManager.java
+++ b/wifi/java/android/net/wifi/WifiManager.java
@@ -1837,7 +1837,7 @@
     }
 
     /**
-     * This call will be deprecated and removed in an upcoming release.  It is no longer used to
+     * This call is deprecated and removed.  It is no longer used to
      * start WiFi Tethering.  Please use {@link ConnectivityManager#startTethering(int, boolean,
      * ConnectivityManager#OnStartTetheringCallback)} if
      * the caller has proper permissions.  Callers can also use the LocalOnlyHotspot feature for a
@@ -1849,8 +1849,11 @@
      * @return {@code false}
      *
      * @hide
+     * @deprecated This API is nolonger supported.
+     * @removed
      */
     @SystemApi
+    @Deprecated
     @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED)
     public boolean setWifiApEnabled(WifiConfiguration wifiConfig, boolean enabled) {
         String packageName = mContext.getOpPackageName();