Merge "Add support for render-ahead"
diff --git a/api/current.txt b/api/current.txt
index 712475e..5b5bd24 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -27276,7 +27276,6 @@
     method public int describeContents();
     method public boolean hasCapability(int);
     method public boolean hasTransport(int);
-    method public boolean hasUnwantedCapability(int);
     method public void writeToParcel(android.os.Parcel, int);
     field public static final android.os.Parcelable.Creator<android.net.NetworkRequest> CREATOR;
   }
@@ -27285,7 +27284,6 @@
     ctor public NetworkRequest.Builder();
     method public android.net.NetworkRequest.Builder addCapability(int);
     method public android.net.NetworkRequest.Builder addTransportType(int);
-    method public android.net.NetworkRequest.Builder addUnwantedCapability(int);
     method public android.net.NetworkRequest build();
     method public android.net.NetworkRequest.Builder removeCapability(int);
     method public android.net.NetworkRequest.Builder removeTransportType(int);
diff --git a/api/removed.txt b/api/removed.txt
index fd48f24..4e730c2 100644
--- a/api/removed.txt
+++ b/api/removed.txt
@@ -296,6 +296,14 @@
   public static abstract class NetworkBadging.Badging implements java.lang.annotation.Annotation {
   }
 
+  public class NetworkRequest implements android.os.Parcelable {
+    method public boolean hasUnwantedCapability(int);
+  }
+
+  public static class NetworkRequest.Builder {
+    method public android.net.NetworkRequest.Builder addUnwantedCapability(int);
+  }
+
   public class SSLCertificateSocketFactory extends javax.net.ssl.SSLSocketFactory {
     method public static deprecated org.apache.http.conn.ssl.SSLSocketFactory getHttpSocketFactory(int, android.net.SSLSessionCache);
   }
diff --git a/api/test-current.txt b/api/test-current.txt
index 15f3400..d5b1399 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -245,6 +245,8 @@
     method public abstract int getInstallReason(java.lang.String, android.os.UserHandle);
     method public abstract java.lang.String[] getNamesForUids(int[]);
     method public abstract java.lang.String getPermissionControllerPackageName();
+    method public abstract java.lang.String getServicesSystemSharedLibraryPackageName();
+    method public abstract java.lang.String getSharedSystemSharedLibraryPackageName();
     method public abstract boolean isPermissionReviewModeEnabled();
     field public static final java.lang.String FEATURE_ADOPTABLE_STORAGE = "android.software.adoptable_storage";
     field public static final java.lang.String FEATURE_FILE_BASED_ENCRYPTION = "android.software.file_based_encryption";
@@ -465,10 +467,18 @@
     method public void setType(int);
   }
 
+  public class LocationManager {
+    method public java.lang.String[] getBackgroundThrottlingWhitelist();
+  }
+
 }
 
 package android.media {
 
+  public final class AudioFocusRequest {
+    method public android.media.AudioManager.OnAudioFocusChangeListener getOnAudioFocusChangeListener();
+  }
+
   public final class AudioFormat implements android.os.Parcelable {
     method public static int channelCountFromInChannelMask(int);
     method public static int channelCountFromOutChannelMask(int);
@@ -476,6 +486,17 @@
     method public static boolean isEncodingLinearPcm(int);
   }
 
+  public final class AudioPresentation {
+    ctor public AudioPresentation(int, int, java.util.Map<java.lang.String, java.lang.String>, java.lang.String, int, boolean, boolean, boolean);
+    method public int getPresentationId();
+    method public int getProgramId();
+  }
+
+  public final class PlaybackParams implements android.os.Parcelable {
+    method public int getAudioStretchMode();
+    method public android.media.PlaybackParams setAudioStretchMode(int);
+  }
+
   public static final class VolumeShaper.Configuration.Builder {
     method public android.media.VolumeShaper.Configuration.Builder setOptionFlags(int);
   }
@@ -534,6 +555,15 @@
     field public static final int RESOURCES_SDK_INT;
   }
 
+  public class DeviceIdleManager {
+    method public java.lang.String[] getSystemPowerWhitelist();
+    method public java.lang.String[] getSystemPowerWhitelistExceptIdle();
+  }
+
+  public class Environment {
+    method public static java.io.File buildPath(java.io.File, java.lang.String...);
+  }
+
   public class IncidentManager {
     method public void reportIncident(android.os.IncidentReportArgs);
   }
@@ -601,12 +631,18 @@
     method public abstract void log(android.os.StrictMode.ViolationInfo);
   }
 
+  public class SystemProperties {
+    method public static java.lang.String get(java.lang.String, java.lang.String);
+  }
+
   public final class UserHandle implements android.os.Parcelable {
     method public static int getAppId(int);
     method public int getIdentifier();
+    field public static final android.os.UserHandle SYSTEM;
   }
 
   public class UserManager {
+    method public static boolean isSplitSystemUser();
     field public static final java.lang.String ACTION_USER_RESTRICTIONS_CHANGED = "android.os.action.USER_RESTRICTIONS_CHANGED";
   }
 
diff --git a/cmds/statsd/src/StatsService.cpp b/cmds/statsd/src/StatsService.cpp
index f16109c..d3cda63 100644
--- a/cmds/statsd/src/StatsService.cpp
+++ b/cmds/statsd/src/StatsService.cpp
@@ -178,23 +178,34 @@
     }
 
     bool verbose = false;
+    bool proto = false;
     if (args.size() > 0 && !args[0].compare(String16("-v"))) {
         verbose = true;
     }
+    if (args.size() > 0 && !args[args.size()-1].compare(String16("--proto"))) {
+        proto = true;
+    }
 
-    // TODO: Proto format for incident reports
-    dump_impl(out, verbose);
+    dump_impl(out, verbose, proto);
 
     fclose(out);
     return NO_ERROR;
 }
 
 /**
- * Write debugging data about statsd in text format.
+ * Write debugging data about statsd in text or proto format.
  */
-void StatsService::dump_impl(FILE* out, bool verbose) {
-    StatsdStats::getInstance().dumpStats(out);
-    mProcessor->dumpStates(out, verbose);
+void StatsService::dump_impl(FILE* out, bool verbose, bool proto) {
+    if (proto) {
+        vector<uint8_t> data;
+        StatsdStats::getInstance().dumpStats(&data, false); // does not reset statsdStats.
+        for (size_t i = 0; i < data.size(); i ++) {
+            fprintf(out, "%c", data[i]);
+        }
+    } else {
+        StatsdStats::getInstance().dumpStats(out);
+        mProcessor->dumpStates(out, verbose);
+    }
 }
 
 /**
@@ -325,6 +336,7 @@
     fprintf(out, "\n");
     fprintf(out, "usage: adb shell cmd stats print-stats\n");
     fprintf(out, "  Prints some basic stats.\n");
+    fprintf(out, "  --proto       Print proto binary instead of string format.\n");
     fprintf(out, "\n");
     fprintf(out, "\n");
     fprintf(out, "usage: adb shell cmd stats clear-puller-cache\n");
@@ -524,13 +536,28 @@
 }
 
 status_t StatsService::cmd_print_stats(FILE* out, const Vector<String8>& args) {
-    vector<ConfigKey> configs = mConfigManager->GetAllConfigKeys();
-    for (const ConfigKey& key : configs) {
-        fprintf(out, "Config %s uses %zu bytes\n", key.ToString().c_str(),
-                mProcessor->GetMetricsSize(key));
+    int argCount = args.size();
+    bool proto = false;
+    if (!std::strcmp("--proto", args[argCount-1].c_str())) {
+        proto = true;
+        argCount -= 1;
     }
     StatsdStats& statsdStats = StatsdStats::getInstance();
-    statsdStats.dumpStats(out);
+    if (proto) {
+        vector<uint8_t> data;
+        statsdStats.dumpStats(&data, false); // does not reset statsdStats.
+        for (size_t i = 0; i < data.size(); i ++) {
+            fprintf(out, "%c", data[i]);
+        }
+
+    } else {
+        vector<ConfigKey> configs = mConfigManager->GetAllConfigKeys();
+        for (const ConfigKey& key : configs) {
+            fprintf(out, "Config %s uses %zu bytes\n", key.ToString().c_str(),
+                    mProcessor->GetMetricsSize(key));
+        }
+        statsdStats.dumpStats(out);
+    }
     return NO_ERROR;
 }
 
diff --git a/cmds/statsd/src/StatsService.h b/cmds/statsd/src/StatsService.h
index a4552e1..648e9c5 100644
--- a/cmds/statsd/src/StatsService.h
+++ b/cmds/statsd/src/StatsService.h
@@ -149,9 +149,9 @@
                                          uint32_t serial);
 
     /**
-     * Text output of dumpsys.
+     * Text or proto output of dumpsys.
      */
-    void dump_impl(FILE* out, bool verbose);
+    void dump_impl(FILE* out, bool verbose, bool proto);
 
     /**
      * Print usage information for the commands
diff --git a/config/hiddenapi-light-greylist.txt b/config/hiddenapi-light-greylist.txt
index 290f8ac..44543a7 100644
--- a/config/hiddenapi-light-greylist.txt
+++ b/config/hiddenapi-light-greylist.txt
@@ -1512,7 +1512,6 @@
 Landroid/os/UserHandle;->MU_ENABLED:Z
 Landroid/os/UserHandle;->OWNER:Landroid/os/UserHandle;
 Landroid/os/UserHandle;->PER_USER_RANGE:I
-Landroid/os/UserHandle;->SYSTEM:Landroid/os/UserHandle;
 Landroid/os/UserHandle;->USER_ALL:I
 Landroid/os/UserHandle;->USER_CURRENT:I
 Landroid/os/UserHandle;->USER_CURRENT_OR_SELF:I
@@ -2245,7 +2244,6 @@
 Landroid/view/SurfaceView;->mSurfaceHolder:Landroid/view/SurfaceHolder;
 Landroid/view/SurfaceView;->surfacePositionLost_uiRtSync(J)V
 Landroid/view/SurfaceView;->updateSurfacePosition_renderWorker(JIIII)V
-Landroid/view/textclassifier/Logger;->DISABLED:Landroid/view/textclassifier/Logger;
 Landroid/view/textclassifier/logging/SmartSelectionEventTracker;-><init>(Landroid/content/Context;I)V
 Landroid/view/textclassifier/logging/SmartSelectionEventTracker;->logEvent(Landroid/view/textclassifier/logging/SmartSelectionEventTracker$SelectionEvent;)V
 Landroid/view/textclassifier/logging/SmartSelectionEventTracker$SelectionEvent;->selectionAction(III)Landroid/view/textclassifier/logging/SmartSelectionEventTracker$SelectionEvent;
diff --git a/core/java/android/app/ActivityOptions.java b/core/java/android/app/ActivityOptions.java
index 09dcbf2..ecd99a7 100644
--- a/core/java/android/app/ActivityOptions.java
+++ b/core/java/android/app/ActivityOptions.java
@@ -1139,7 +1139,8 @@
      * {@link android.app.admin.DevicePolicyManager} can run in LockTask mode. Therefore, if
      * {@link android.app.admin.DevicePolicyManager#isLockTaskPermitted(String)} returns
      * {@code false} for the package of the target activity, a {@link SecurityException} will be
-     * thrown during {@link Context#startActivity(Intent, Bundle)}.
+     * thrown during {@link Context#startActivity(Intent, Bundle)}. This method doesn't affect
+     * activities that are already running — relaunch the activity to run in lock task mode.
      *
      * Defaults to {@code false} if not set.
      *
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index 8719875..4ab6724 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -6431,6 +6431,7 @@
         /**
          * @return the user to be displayed for any replies sent by the user
          */
+        @NonNull
         public Person getUser() {
             return mUser;
         }
@@ -6505,7 +6506,8 @@
          *
          * @return this object for method chaining
          */
-        public MessagingStyle addMessage(CharSequence text, long timestamp, Person sender) {
+        public MessagingStyle addMessage(@NonNull CharSequence text, long timestamp,
+                @Nullable Person sender) {
             return addMessage(new Message(text, timestamp, sender));
         }
 
@@ -6935,7 +6937,7 @@
              * to differentiate between the different users.
              * </p>
              */
-            public Message(CharSequence text, long timestamp, @Nullable Person sender){
+            public Message(@NonNull CharSequence text, long timestamp, @Nullable Person sender) {
                 mText = text;
                 mTimestamp = timestamp;
                 mSender = sender;
diff --git a/core/java/android/app/NotificationManager.java b/core/java/android/app/NotificationManager.java
index 46d1264..5113fd0 100644
--- a/core/java/android/app/NotificationManager.java
+++ b/core/java/android/app/NotificationManager.java
@@ -1145,6 +1145,21 @@
                 SUPPRESSED_EFFECT_NOTIFICATION_LIST
         };
 
+        private static final int[] SCREEN_OFF_SUPPRESSED_EFFECTS = {
+                SUPPRESSED_EFFECT_SCREEN_OFF,
+                SUPPRESSED_EFFECT_FULL_SCREEN_INTENT,
+                SUPPRESSED_EFFECT_LIGHTS,
+                SUPPRESSED_EFFECT_AMBIENT,
+        };
+
+        private static final int[] SCREEN_ON_SUPPRESSED_EFFECTS = {
+                SUPPRESSED_EFFECT_SCREEN_ON,
+                SUPPRESSED_EFFECT_PEEK,
+                SUPPRESSED_EFFECT_STATUS_BAR,
+                SUPPRESSED_EFFECT_BADGE,
+                SUPPRESSED_EFFECT_NOTIFICATION_LIST
+        };
+
         /**
          * Visual effects to suppress for a notification that is filtered by Do Not Disturb mode.
          * Bitmask of SUPPRESSED_EFFECT_* constants.
@@ -1297,6 +1312,32 @@
             return true;
         }
 
+        /**
+         * @hide
+         */
+        public static boolean areAnyScreenOffEffectsSuppressed(int effects) {
+            for (int i = 0; i < SCREEN_OFF_SUPPRESSED_EFFECTS.length; i++) {
+                final int effect = SCREEN_OFF_SUPPRESSED_EFFECTS[i];
+                if ((effects & effect) != 0) {
+                    return true;
+                }
+            }
+            return false;
+        }
+
+        /**
+         * @hide
+         */
+        public static boolean areAnyScreenOnEffectsSuppressed(int effects) {
+            for (int i = 0; i < SCREEN_ON_SUPPRESSED_EFFECTS.length; i++) {
+                final int effect = SCREEN_ON_SUPPRESSED_EFFECTS[i];
+                if ((effects & effect) != 0) {
+                    return true;
+                }
+            }
+            return false;
+        }
+
         public static String suppressedEffectsToString(int effects) {
             if (effects <= 0) return "";
             final StringBuilder sb = new StringBuilder();
diff --git a/core/java/android/app/RemoteAction.java b/core/java/android/app/RemoteAction.java
index 47741c0..c174665 100644
--- a/core/java/android/app/RemoteAction.java
+++ b/core/java/android/app/RemoteAction.java
@@ -122,6 +122,7 @@
     public RemoteAction clone() {
         RemoteAction action = new RemoteAction(mIcon, mTitle, mContentDescription, mActionIntent);
         action.setEnabled(mEnabled);
+        action.setShouldShowIcon(mShouldShowIcon);
         return action;
     }
 
diff --git a/core/java/android/app/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java
index 3ee1ed5..246d4a3 100644
--- a/core/java/android/app/SystemServiceRegistry.java
+++ b/core/java/android/app/SystemServiceRegistry.java
@@ -105,10 +105,12 @@
 import android.os.BatteryManager;
 import android.os.BatteryStats;
 import android.os.Build;
+import android.os.DeviceIdleManager;
 import android.os.DropBoxManager;
 import android.os.HardwarePropertiesManager;
 import android.os.IBatteryPropertiesRegistrar;
 import android.os.IBinder;
+import android.os.IDeviceIdleController;
 import android.os.IHardwarePropertiesManager;
 import android.os.IPowerManager;
 import android.os.IRecoverySystem;
@@ -984,6 +986,17 @@
                                 ctx.mMainThread.getHandler());
                     }
             });
+
+        registerService(Context.DEVICE_IDLE_CONTROLLER, DeviceIdleManager.class,
+                new CachedServiceFetcher<DeviceIdleManager>() {
+                    @Override
+                    public DeviceIdleManager createService(ContextImpl ctx)
+                            throws ServiceNotFoundException {
+                        IDeviceIdleController service = IDeviceIdleController.Stub.asInterface(
+                                ServiceManager.getServiceOrThrow(
+                                        Context.DEVICE_IDLE_CONTROLLER));
+                        return new DeviceIdleManager(ctx.getOuterContext(), service);
+                    }});
     }
 
     /**
diff --git a/core/java/android/app/WallpaperManager.java b/core/java/android/app/WallpaperManager.java
index 465340f..17bc6ea 100644
--- a/core/java/android/app/WallpaperManager.java
+++ b/core/java/android/app/WallpaperManager.java
@@ -401,7 +401,8 @@
                 }
             }
             synchronized (this) {
-                if (mCachedWallpaper != null && mCachedWallpaperUserId == userId) {
+                if (mCachedWallpaper != null && mCachedWallpaperUserId == userId
+                        && !mCachedWallpaper.isRecycled()) {
                     return mCachedWallpaper;
                 }
                 mCachedWallpaper = null;
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index 8f1b328..990147b 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -6198,6 +6198,7 @@
      * @hide
      */
      @SystemApi
+     @RequiresPermission(android.Manifest.permission.MANAGE_USERS)
      public @Nullable List<String> getPermittedAccessibilityServices(int userId) {
         throwIfParentInstance("getPermittedAccessibilityServices");
         if (mService != null) {
diff --git a/core/java/android/app/usage/NetworkStatsManager.java b/core/java/android/app/usage/NetworkStatsManager.java
index b2fe958..85f4efc 100644
--- a/core/java/android/app/usage/NetworkStatsManager.java
+++ b/core/java/android/app/usage/NetworkStatsManager.java
@@ -305,6 +305,8 @@
      *            {@link java.lang.System#currentTimeMillis}.
      * @param uid UID of app
      * @param tag TAG of interest. Use {@link NetworkStats.Bucket#TAG_NONE} for no tags.
+     * @param state state of interest. Use {@link NetworkStats.Bucket#STATE_ALL} to aggregate
+     *            traffic from all states.
      * @return Statistics object or null if an error happened during statistics collection.
      * @throws SecurityException if permissions are insufficient to read network statistics.
      */
diff --git a/core/java/android/bluetooth/BluetoothHidDevice.java b/core/java/android/bluetooth/BluetoothHidDevice.java
index af99bf7..3bc8544 100644
--- a/core/java/android/bluetooth/BluetoothHidDevice.java
+++ b/core/java/android/bluetooth/BluetoothHidDevice.java
@@ -701,6 +701,28 @@
     }
 
     /**
+     * Gets the application name of the current HidDeviceService user.
+     *
+     * @return the current user name, or empty string if cannot get the name
+     * {@hide}
+     */
+    public String getUserAppName() {
+        final IBluetoothHidDevice service = mService;
+
+        if (service != null) {
+            try {
+                return service.getUserAppName();
+            } catch (RemoteException e) {
+                Log.e(TAG, e.toString());
+            }
+        } else {
+            Log.w(TAG, "Proxy not attached to service");
+        }
+
+        return "";
+    }
+
+    /**
      * Initiates connection to host which is currently paired with this device. If the application
      * is not registered, #connect(BluetoothDevice) will fail. The connection state should be
      * tracked by the application by handling callback from Callback#onConnectionStateChanged. The
diff --git a/core/java/android/content/ContentResolver.java b/core/java/android/content/ContentResolver.java
index 9f3df37..f7908b6 100644
--- a/core/java/android/content/ContentResolver.java
+++ b/core/java/android/content/ContentResolver.java
@@ -51,7 +51,6 @@
 import android.util.EventLog;
 import android.util.Log;
 
-import com.android.internal.util.ArrayUtils;
 import com.android.internal.util.MimeIconUtils;
 import com.android.internal.util.Preconditions;
 
@@ -602,6 +601,8 @@
             try {
                 return provider.getType(url);
             } catch (RemoteException e) {
+                // Arbitrary and not worth documenting, as Activity
+                // Manager will kill this process shortly anyway.
                 return null;
             } catch (java.lang.Exception e) {
                 Log.w(TAG, "Failed to get type for: " + url + " (" + e.getMessage() + ")");
@@ -620,9 +621,7 @@
                     ContentProvider.getUriWithoutUserId(url), resolveUserId(url));
             return type;
         } catch (RemoteException e) {
-            // Arbitrary and not worth documenting, as Activity
-            // Manager will kill this process shortly anyway.
-            return null;
+            throw e.rethrowFromSystemServer();
         } catch (java.lang.Exception e) {
             Log.w(TAG, "Failed to get type for: " + url + " (" + e.getMessage() + ")");
             return null;
@@ -1964,6 +1963,7 @@
             getContentService().registerContentObserver(uri, notifyForDescendents,
                     observer.getContentObserver(), userHandle, mTargetSdkVersion);
         } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
         }
     }
 
@@ -1982,6 +1982,7 @@
                         contentObserver);
             }
         } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
         }
     }
 
@@ -2089,6 +2090,7 @@
                     syncToNetwork ? NOTIFY_SYNC_TO_NETWORK : 0,
                     userHandle, mTargetSdkVersion);
         } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
         }
     }
 
@@ -2105,6 +2107,7 @@
                     observer != null && observer.deliverSelfNotifications(), flags,
                     userHandle, mTargetSdkVersion);
         } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
         }
     }
 
@@ -2126,6 +2129,7 @@
                     ContentProvider.getUriWithoutUserId(uri), modeFlags, /* toPackage= */ null,
                     resolveUserId(uri));
         } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
         }
     }
 
@@ -2141,6 +2145,7 @@
                     ContentProvider.getUriWithoutUserId(uri), modeFlags, toPackage,
                     resolveUserId(uri));
         } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
         }
     }
 
@@ -2160,6 +2165,7 @@
                     ContentProvider.getUriWithoutUserId(uri), modeFlags, /* toPackage= */ null,
                     resolveUserId(uri));
         } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
         }
     }
 
@@ -2178,7 +2184,7 @@
             return ActivityManager.getService()
                     .getPersistedUriPermissions(mPackageName, true).getList();
         } catch (RemoteException e) {
-            throw new RuntimeException("Activity manager has died", e);
+            throw e.rethrowFromSystemServer();
         }
     }
 
@@ -2194,7 +2200,7 @@
             return ActivityManager.getService()
                     .getPersistedUriPermissions(mPackageName, false).getList();
         } catch (RemoteException e) {
-            throw new RuntimeException("Activity manager has died", e);
+            throw e.rethrowFromSystemServer();
         }
     }
 
@@ -2273,7 +2279,7 @@
         try {
             getContentService().syncAsUser(request, userId);
         } catch(RemoteException e) {
-            // Shouldn't happen.
+            throw e.rethrowFromSystemServer();
         }
     }
 
@@ -2285,7 +2291,7 @@
         try {
             getContentService().sync(request);
         } catch(RemoteException e) {
-            // Shouldn't happen.
+            throw e.rethrowFromSystemServer();
         }
     }
 
@@ -2349,6 +2355,7 @@
         try {
             getContentService().cancelSync(account, authority, null);
         } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
         }
     }
 
@@ -2360,6 +2367,7 @@
         try {
             getContentService().cancelSyncAsUser(account, authority, null, userId);
         } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
         }
     }
 
@@ -2371,7 +2379,7 @@
         try {
             return getContentService().getSyncAdapterTypes();
         } catch (RemoteException e) {
-            throw new RuntimeException("the ContentService should always be reachable", e);
+            throw e.rethrowFromSystemServer();
         }
     }
 
@@ -2383,7 +2391,7 @@
         try {
             return getContentService().getSyncAdapterTypesAsUser(userId);
         } catch (RemoteException e) {
-            throw new RuntimeException("the ContentService should always be reachable", e);
+            throw e.rethrowFromSystemServer();
         }
     }
 
@@ -2397,8 +2405,8 @@
         try {
             return getContentService().getSyncAdapterPackagesForAuthorityAsUser(authority, userId);
         } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
         }
-        return ArrayUtils.emptyArray(String.class);
     }
 
     /**
@@ -2414,7 +2422,7 @@
         try {
             return getContentService().getSyncAutomatically(account, authority);
         } catch (RemoteException e) {
-            throw new RuntimeException("the ContentService should always be reachable", e);
+            throw e.rethrowFromSystemServer();
         }
     }
 
@@ -2427,7 +2435,7 @@
         try {
             return getContentService().getSyncAutomaticallyAsUser(account, authority, userId);
         } catch (RemoteException e) {
-            throw new RuntimeException("the ContentService should always be reachable", e);
+            throw e.rethrowFromSystemServer();
         }
     }
 
@@ -2453,8 +2461,7 @@
         try {
             getContentService().setSyncAutomaticallyAsUser(account, authority, sync, userId);
         } catch (RemoteException e) {
-            // exception ignored; if this is thrown then it means the runtime is in the midst of
-            // being restarted
+            throw e.rethrowFromSystemServer();
         }
     }
 
@@ -2500,8 +2507,7 @@
         try {
              getContentService().addPeriodicSync(account, authority, extras, pollFrequency);
         } catch (RemoteException e) {
-            // exception ignored; if this is thrown then it means the runtime is in the midst of
-            // being restarted
+            throw e.rethrowFromSystemServer();
         }
     }
 
@@ -2540,7 +2546,7 @@
         try {
             getContentService().removePeriodicSync(account, authority, extras);
         } catch (RemoteException e) {
-            throw new RuntimeException("the ContentService should always be reachable", e);
+            throw e.rethrowFromSystemServer();
         }
     }
 
@@ -2564,8 +2570,7 @@
         try {
             getContentService().cancelRequest(request);
         } catch (RemoteException e) {
-            // exception ignored; if this is thrown then it means the runtime is in the midst of
-            // being restarted
+            throw e.rethrowFromSystemServer();
         }
     }
 
@@ -2582,7 +2587,7 @@
         try {
             return getContentService().getPeriodicSyncs(account, authority, null);
         } catch (RemoteException e) {
-            throw new RuntimeException("the ContentService should always be reachable", e);
+            throw e.rethrowFromSystemServer();
         }
     }
 
@@ -2596,7 +2601,7 @@
         try {
             return getContentService().getIsSyncable(account, authority);
         } catch (RemoteException e) {
-            throw new RuntimeException("the ContentService should always be reachable", e);
+            throw e.rethrowFromSystemServer();
         }
     }
 
@@ -2609,7 +2614,7 @@
         try {
             return getContentService().getIsSyncableAsUser(account, authority, userId);
         } catch (RemoteException e) {
-            throw new RuntimeException("the ContentService should always be reachable", e);
+            throw e.rethrowFromSystemServer();
         }
     }
 
@@ -2623,8 +2628,7 @@
         try {
             getContentService().setIsSyncable(account, authority, syncable);
         } catch (RemoteException e) {
-            // exception ignored; if this is thrown then it means the runtime is in the midst of
-            // being restarted
+            throw e.rethrowFromSystemServer();
         }
     }
 
@@ -2640,7 +2644,7 @@
         try {
             return getContentService().getMasterSyncAutomatically();
         } catch (RemoteException e) {
-            throw new RuntimeException("the ContentService should always be reachable", e);
+            throw e.rethrowFromSystemServer();
         }
     }
 
@@ -2652,7 +2656,7 @@
         try {
             return getContentService().getMasterSyncAutomaticallyAsUser(userId);
         } catch (RemoteException e) {
-            throw new RuntimeException("the ContentService should always be reachable", e);
+            throw e.rethrowFromSystemServer();
         }
     }
 
@@ -2676,8 +2680,7 @@
         try {
             getContentService().setMasterSyncAutomaticallyAsUser(sync, userId);
         } catch (RemoteException e) {
-            // exception ignored; if this is thrown then it means the runtime is in the midst of
-            // being restarted
+            throw e.rethrowFromSystemServer();
         }
     }
 
@@ -2701,7 +2704,7 @@
         try {
             return getContentService().isSyncActive(account, authority, null);
         } catch (RemoteException e) {
-            throw new RuntimeException("the ContentService should always be reachable", e);
+            throw e.rethrowFromSystemServer();
         }
     }
 
@@ -2727,7 +2730,7 @@
             }
             return syncs.get(0);
         } catch (RemoteException e) {
-            throw new RuntimeException("the ContentService should always be reachable", e);
+            throw e.rethrowFromSystemServer();
         }
     }
 
@@ -2744,7 +2747,7 @@
         try {
             return getContentService().getCurrentSyncs();
         } catch (RemoteException e) {
-            throw new RuntimeException("the ContentService should always be reachable", e);
+            throw e.rethrowFromSystemServer();
         }
     }
 
@@ -2756,7 +2759,7 @@
         try {
             return getContentService().getCurrentSyncsAsUser(userId);
         } catch (RemoteException e) {
-            throw new RuntimeException("the ContentService should always be reachable", e);
+            throw e.rethrowFromSystemServer();
         }
     }
 
@@ -2771,7 +2774,7 @@
         try {
             return getContentService().getSyncStatus(account, authority, null);
         } catch (RemoteException e) {
-            throw new RuntimeException("the ContentService should always be reachable", e);
+            throw e.rethrowFromSystemServer();
         }
     }
 
@@ -2784,7 +2787,7 @@
         try {
             return getContentService().getSyncStatusAsUser(account, authority, null, userId);
         } catch (RemoteException e) {
-            throw new RuntimeException("the ContentService should always be reachable", e);
+            throw e.rethrowFromSystemServer();
         }
     }
 
@@ -2809,7 +2812,7 @@
         try {
             return getContentService().isSyncPendingAsUser(account, authority, null, userId);
         } catch (RemoteException e) {
-            throw new RuntimeException("the ContentService should always be reachable", e);
+            throw e.rethrowFromSystemServer();
         }
     }
 
@@ -2841,7 +2844,7 @@
             getContentService().addStatusChangeListener(mask, observer);
             return observer;
         } catch (RemoteException e) {
-            throw new RuntimeException("the ContentService should always be reachable", e);
+            throw e.rethrowFromSystemServer();
         }
     }
 
@@ -2856,8 +2859,7 @@
         try {
             getContentService().removeStatusChangeListener((ISyncStatusObserver.Stub) handle);
         } catch (RemoteException e) {
-            // exception ignored; if this is thrown then it means the runtime is in the midst of
-            // being restarted
+            throw e.rethrowFromSystemServer();
         }
     }
 
@@ -3027,9 +3029,7 @@
             return sContentService;
         }
         IBinder b = ServiceManager.getService(CONTENT_SERVICE_NAME);
-        if (false) Log.v("ContentService", "default service binder = " + b);
         sContentService = IContentService.Stub.asInterface(b);
-        if (false) Log.v("ContentService", "default service = " + sContentService);
         return sContentService;
     }
 
@@ -3038,7 +3038,7 @@
         return mPackageName;
     }
 
-    private static IContentService sContentService;
+    private static volatile IContentService sContentService;
     private final Context mContext;
 
     final String mPackageName;
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index 920056a..ede7ee4 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -3780,7 +3780,7 @@
     public static final String DROPBOX_SERVICE = "dropbox";
 
     /**
-     * System service name for the DeviceIdleController.  There is no Java API for this.
+     * System service name for the DeviceIdleManager.
      * @see #getSystemService(String)
      * @hide
      */
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index efc9b6d..01ee671 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -6810,6 +6810,9 @@
                 case "--activity-task-on-home":
                     intent.addFlags(Intent.FLAG_ACTIVITY_TASK_ON_HOME);
                     break;
+                case "--activity-match-external":
+                    intent.addFlags(Intent.FLAG_ACTIVITY_MATCH_EXTERNAL);
+                    break;
                 case "--receiver-registered-only":
                     intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
                     break;
@@ -6946,7 +6949,7 @@
                 "    [--activity-no-user-action] [--activity-previous-is-top]",
                 "    [--activity-reorder-to-front] [--activity-reset-task-if-needed]",
                 "    [--activity-single-top] [--activity-clear-task]",
-                "    [--activity-task-on-home]",
+                "    [--activity-task-on-home] [--activity-match-external]",
                 "    [--receiver-registered-only] [--receiver-replace-pending]",
                 "    [--receiver-foreground] [--receiver-no-abort]",
                 "    [--receiver-include-background]",
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index 660026f..d734aa3 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -3952,6 +3952,7 @@
      *
      * @hide
      */
+    @TestApi
     public abstract @NonNull String getServicesSystemSharedLibraryPackageName();
 
     /**
@@ -3961,6 +3962,7 @@
      *
      * @hide
      */
+    @TestApi
     public abstract @NonNull String getSharedSystemSharedLibraryPackageName();
 
     /**
diff --git a/core/java/android/net/NetworkRequest.java b/core/java/android/net/NetworkRequest.java
index 82af5d3..6f812ac 100644
--- a/core/java/android/net/NetworkRequest.java
+++ b/core/java/android/net/NetworkRequest.java
@@ -233,6 +233,8 @@
          *
          * @param capability The capability to add to unwanted capability list.
          * @return The builder to facilitate chaining.
+         *
+         * @removed
          */
         public Builder addUnwantedCapability(@NetworkCapabilities.NetCapability int capability) {
             mNetworkCapabilities.addUnwantedCapability(capability);
@@ -439,6 +441,8 @@
 
     /**
      * @see Builder#addUnwantedCapability(int)
+     *
+     * @removed
      */
     public boolean hasUnwantedCapability(@NetCapability int capability) {
         return networkCapabilities.hasUnwantedCapability(capability);
diff --git a/core/java/android/net/metrics/ApfStats.java b/core/java/android/net/metrics/ApfStats.java
index 3b0dc7e..76a781d 100644
--- a/core/java/android/net/metrics/ApfStats.java
+++ b/core/java/android/net/metrics/ApfStats.java
@@ -20,7 +20,7 @@
 import android.os.Parcelable;
 
 /**
- * An event logged for an interface with APF capabilities when its IpManager state machine exits.
+ * An event logged for an interface with APF capabilities when its IpClient state machine exits.
  * {@hide}
  */
 public final class ApfStats implements Parcelable {
diff --git a/core/java/android/os/DeviceIdleManager.java b/core/java/android/os/DeviceIdleManager.java
new file mode 100644
index 0000000..9039f92
--- /dev/null
+++ b/core/java/android/os/DeviceIdleManager.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.os;
+
+import android.annotation.NonNull;
+import android.annotation.SystemService;
+import android.annotation.TestApi;
+import android.content.Context;
+
+/**
+ * Access to the service that keeps track of device idleness and drives low power mode based on
+ * that.
+ *
+ * @hide
+ */
+@TestApi
+@SystemService(Context.DEVICE_IDLE_CONTROLLER)
+public class DeviceIdleManager {
+    private final Context mContext;
+    private final IDeviceIdleController mService;
+
+    /**
+     * @hide
+     */
+    public DeviceIdleManager(@NonNull Context context, @NonNull IDeviceIdleController service) {
+        mContext = context;
+        mService = service;
+    }
+
+    /**
+     * @return package names the system has white-listed to opt out of power save restrictions,
+     * except for device idle mode.
+     */
+    public @NonNull String[] getSystemPowerWhitelistExceptIdle() {
+        try {
+            return mService.getSystemPowerWhitelistExceptIdle();
+        } catch (RemoteException e) {
+            e.rethrowFromSystemServer();
+            return new String[0];
+        }
+    }
+
+    /**
+     * @return package names the system has white-listed to opt out of power save restrictions for
+     * all modes.
+     */
+    public @NonNull String[] getSystemPowerWhitelist() {
+        try {
+            return mService.getSystemPowerWhitelist();
+        } catch (RemoteException e) {
+            e.rethrowFromSystemServer();
+            return new String[0];
+        }
+    }
+}
diff --git a/core/java/android/os/Environment.java b/core/java/android/os/Environment.java
index 03203d0..213260f 100644
--- a/core/java/android/os/Environment.java
+++ b/core/java/android/os/Environment.java
@@ -16,6 +16,7 @@
 
 package android.os;
 
+import android.annotation.TestApi;
 import android.app.admin.DevicePolicyManager;
 import android.content.Context;
 import android.os.storage.StorageManager;
@@ -1033,6 +1034,7 @@
      *
      * @hide
      */
+    @TestApi
     public static File buildPath(File base, String... segments) {
         File cur = base;
         for (String segment : segments) {
diff --git a/core/java/android/os/SystemProperties.java b/core/java/android/os/SystemProperties.java
index 8eb39c0..7d3ba6a 100644
--- a/core/java/android/os/SystemProperties.java
+++ b/core/java/android/os/SystemProperties.java
@@ -19,6 +19,7 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.SystemApi;
+import android.annotation.TestApi;
 import android.util.Log;
 import android.util.MutableInt;
 
@@ -35,6 +36,7 @@
  * {@hide}
  */
 @SystemApi
+@TestApi
 public class SystemProperties {
     private static final String TAG = "SystemProperties";
     private static final boolean TRACK_KEY_ACCESS = false;
@@ -110,6 +112,7 @@
      */
     @NonNull
     @SystemApi
+    @TestApi
     public static String get(@NonNull String key, @Nullable String def) {
         if (TRACK_KEY_ACCESS) onKeyAccess(key);
         return native_get(key, def);
diff --git a/core/java/android/os/UserHandle.java b/core/java/android/os/UserHandle.java
index 094f004..4d4f31d 100644
--- a/core/java/android/os/UserHandle.java
+++ b/core/java/android/os/UserHandle.java
@@ -82,6 +82,7 @@
     public static final int USER_SERIAL_SYSTEM = 0;
 
     /** @hide A user handle to indicate the "system" user of the device */
+    @TestApi
     public static final UserHandle SYSTEM = new UserHandle(USER_SYSTEM);
 
     /**
diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java
index 54bbb7d..5e4d191 100644
--- a/core/java/android/os/UserManager.java
+++ b/core/java/android/os/UserManager.java
@@ -1153,6 +1153,7 @@
      * primary user are two separate users. Previously system user and primary user are combined as
      * a single owner user.  see @link {android.os.UserHandle#USER_OWNER}
      */
+    @TestApi
     public static boolean isSplitSystemUser() {
         return RoSystemProperties.FW_SYSTEM_USER_SPLIT;
     }
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 5a3cf77..b644c1d 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -5313,7 +5313,7 @@
          *
          * For more information about how the platform handles {@code ANDROID_ID}
          * in Android 8.0 (API level 26) and higher, see <a
-         * href="{@docRoot}preview/behavior-changes.html#privacy-all">
+         * href="{@docRoot}about/versions/oreo/android-8.0-changes.html#privacy-all">
          * Android 8.0 Behavior Changes</a>.
          *
          * <p class="note"><strong>Note:</strong> For apps that were installed
@@ -7384,6 +7384,17 @@
                 BOOLEAN_VALIDATOR;
 
         /**
+         * Whether the swipe up gesture to switch apps should be enabled.
+         *
+         * @hide
+         */
+        public static final String SWIPE_UP_TO_SWITCH_APPS_ENABLED =
+                "swipe_up_to_switch_apps_enabled";
+
+        private static final Validator SWIPE_UP_TO_SWITCH_APPS_ENABLED_VALIDATOR =
+                BOOLEAN_VALIDATOR;
+
+        /**
          * Whether or not the smart camera lift trigger that launches the camera when the user moves
          * the phone into a position for taking photos should be enabled.
          *
@@ -7894,6 +7905,7 @@
             NIGHT_DISPLAY_AUTO_MODE,
             SYNC_PARENT_SOUNDS,
             CAMERA_DOUBLE_TWIST_TO_FLIP_ENABLED,
+            SWIPE_UP_TO_SWITCH_APPS_ENABLED,
             CAMERA_DOUBLE_TAP_POWER_GESTURE_DISABLED,
             SYSTEM_NAVIGATION_KEYS_ENABLED,
             QS_TILES,
@@ -8027,6 +8039,8 @@
             VALIDATORS.put(SYNC_PARENT_SOUNDS, SYNC_PARENT_SOUNDS_VALIDATOR);
             VALIDATORS.put(CAMERA_DOUBLE_TWIST_TO_FLIP_ENABLED,
                     CAMERA_DOUBLE_TWIST_TO_FLIP_ENABLED_VALIDATOR);
+            VALIDATORS.put(SWIPE_UP_TO_SWITCH_APPS_ENABLED,
+                    SWIPE_UP_TO_SWITCH_APPS_ENABLED_VALIDATOR);
             VALIDATORS.put(CAMERA_DOUBLE_TAP_POWER_GESTURE_DISABLED,
                     CAMERA_DOUBLE_TAP_POWER_GESTURE_DISABLED_VALIDATOR);
             VALIDATORS.put(SYSTEM_NAVIGATION_KEYS_ENABLED,
@@ -8938,6 +8952,20 @@
        /** {@hide} */
        public static final String NETSTATS_UID_TAG_DELETE_AGE = "netstats_uid_tag_delete_age";
 
+       /** {@hide} */
+       public static final String NETPOLICY_QUOTA_ENABLED = "netpolicy_quota_enabled";
+       /** {@hide} */
+       public static final String NETPOLICY_QUOTA_UNLIMITED = "netpolicy_quota_unlimited";
+       /** {@hide} */
+       public static final String NETPOLICY_QUOTA_LIMITED = "netpolicy_quota_limited";
+       /** {@hide} */
+       public static final String NETPOLICY_QUOTA_FRAC_JOBS = "netpolicy_quota_frac_jobs";
+       /** {@hide} */
+       public static final String NETPOLICY_QUOTA_FRAC_MULTIPATH = "netpolicy_quota_frac_multipath";
+
+       /** {@hide} */
+       public static final String NETPOLICY_OVERRIDE_ENABLED = "netpolicy_override_enabled";
+
        /**
         * User preference for which network(s) should be used. Only the
         * connectivity service should touch this.
@@ -10809,6 +10837,15 @@
                 = "time_only_mode_constants";
 
         /**
+         * Whether of not to send keycode sleep for ungaze when Home is the foreground activity on
+         * watch type devices.
+         * Type: int (0 for false, 1 for true)
+         * Default: 0
+         * @hide
+         */
+        public static final String UNGAZE_SLEEP_ENABLED = "ungaze_sleep_enabled";
+
+        /**
          * Whether or not Network Watchlist feature is enabled.
          * Type: int (0 for false, 1 for true)
          * Default: 0
diff --git a/core/java/android/service/notification/ZenModeConfig.java b/core/java/android/service/notification/ZenModeConfig.java
index 3830b7a..daecea7 100644
--- a/core/java/android/service/notification/ZenModeConfig.java
+++ b/core/java/android/service/notification/ZenModeConfig.java
@@ -1487,14 +1487,18 @@
     /**
      * Returns a description of the current do not disturb settings from config.
      * - If turned on manually and end time is known, returns end time.
+     * - If turned on manually and end time is on forever until turned off, return null if
+     * describeForeverCondition is false, else return String describing indefinite behavior
      * - If turned on by an automatic rule, returns the automatic rule name.
      * - If on due to an app, returns the app name.
      * - If there's a combination of rules/apps that trigger, then shows the one that will
      *  last the longest if applicable.
-     * @return null if do not disturb is off.
+     * @return null if DND is off or describeForeverCondition is false and
+     * DND is on forever (until turned off)
      */
-    public static String getDescription(Context context, boolean zenOn, ZenModeConfig config) {
-        if (!zenOn) {
+    public static String getDescription(Context context, boolean zenOn, ZenModeConfig config,
+            boolean describeForeverCondition) {
+        if (!zenOn || config == null) {
             return null;
         }
 
@@ -1513,8 +1517,11 @@
             } else {
                 if (id == null) {
                     // Do not disturb manually triggered to remain on forever until turned off
-                    // No subtext
-                    return null;
+                    if (describeForeverCondition) {
+                        return context.getString(R.string.zen_mode_forever);
+                    } else {
+                        return null;
+                    }
                 } else {
                     latestEndTime = tryParseCountdownConditionId(id);
                     if (latestEndTime > 0) {
diff --git a/core/java/android/text/Selection.java b/core/java/android/text/Selection.java
index 3445658..5256e47 100644
--- a/core/java/android/text/Selection.java
+++ b/core/java/android/text/Selection.java
@@ -180,7 +180,7 @@
      * Remove the selection or cursor, if any, from the text.
      */
     public static final void removeSelection(Spannable text) {
-        text.removeSpan(SELECTION_START);
+        text.removeSpan(SELECTION_START, Spanned.SPAN_INTERMEDIATE);
         text.removeSpan(SELECTION_END);
         removeMemory(text);
     }
diff --git a/core/java/android/text/Spannable.java b/core/java/android/text/Spannable.java
index 39b78eb..8315b2a 100644
--- a/core/java/android/text/Spannable.java
+++ b/core/java/android/text/Spannable.java
@@ -46,6 +46,19 @@
     public void removeSpan(Object what);
 
     /**
+     * Remove the specified object from the range of text to which it
+     * was attached, if any.  It is OK to remove an object that was never
+     * attached in the first place.
+     *
+     * See {@link Spanned} for an explanation of what the flags mean.
+     *
+     * @hide
+     */
+    default void removeSpan(Object what, int flags) {
+        removeSpan(what);
+    }
+
+    /**
      * Factory used by TextView to create new {@link Spannable Spannables}. You can subclass
      * it to provide something other than {@link SpannableString}.
      *
diff --git a/core/java/android/text/SpannableStringBuilder.java b/core/java/android/text/SpannableStringBuilder.java
index d41dfdc..41a9c45 100644
--- a/core/java/android/text/SpannableStringBuilder.java
+++ b/core/java/android/text/SpannableStringBuilder.java
@@ -312,7 +312,7 @@
                     // The following condition indicates that the span would become empty
                     (textIsRemoved || mSpanStarts[i] > start || mSpanEnds[i] < mGapStart)) {
                 mIndexOfSpan.remove(mSpans[i]);
-                removeSpan(i);
+                removeSpan(i, 0 /* flags */);
                 return true;
             }
             return resolveGap(mSpanStarts[i]) <= end && (i & 1) != 0 &&
@@ -472,7 +472,7 @@
     }
 
     // Note: caller is responsible for removing the mIndexOfSpan entry.
-    private void removeSpan(int i) {
+    private void removeSpan(int i, int flags) {
         Object object = mSpans[i];
 
         int start = mSpanStarts[i];
@@ -496,7 +496,9 @@
         // Invariants must be restored before sending span removed notifications.
         restoreInvariants();
 
-        sendSpanRemoved(object, start, end);
+        if ((flags & Spanned.SPAN_INTERMEDIATE) == 0) {
+            sendSpanRemoved(object, start, end);
+        }
     }
 
     // Documentation from interface
@@ -782,10 +784,19 @@
      * Remove the specified markup object from the buffer.
      */
     public void removeSpan(Object what) {
+        removeSpan(what, 0 /* flags */);
+    }
+
+    /**
+     * Remove the specified markup object from the buffer.
+     *
+     * @hide
+     */
+    public void removeSpan(Object what, int flags) {
         if (mIndexOfSpan == null) return;
         Integer i = mIndexOfSpan.remove(what);
         if (i != null) {
-            removeSpan(i.intValue());
+            removeSpan(i.intValue(), flags);
         }
     }
 
diff --git a/core/java/android/text/SpannableStringInternal.java b/core/java/android/text/SpannableStringInternal.java
index 5dd1a52..bcc2fda 100644
--- a/core/java/android/text/SpannableStringInternal.java
+++ b/core/java/android/text/SpannableStringInternal.java
@@ -249,6 +249,13 @@
     }
 
     /* package */ void removeSpan(Object what) {
+        removeSpan(what, 0 /* flags */);
+    }
+
+    /**
+     * @hide
+     */
+    public void removeSpan(Object what, int flags) {
         int count = mSpanCount;
         Object[] spans = mSpans;
         int[] data = mSpanData;
@@ -262,11 +269,13 @@
 
                 System.arraycopy(spans, i + 1, spans, i, c);
                 System.arraycopy(data, (i + 1) * COLUMNS,
-                                 data, i * COLUMNS, c * COLUMNS);
+                        data, i * COLUMNS, c * COLUMNS);
 
                 mSpanCount--;
 
-                sendSpanRemoved(what, ostart, oend);
+                if ((flags & Spanned.SPAN_INTERMEDIATE) == 0) {
+                    sendSpanRemoved(what, ostart, oend);
+                }
                 return;
             }
         }
diff --git a/core/java/android/util/FeatureFlagUtils.java b/core/java/android/util/FeatureFlagUtils.java
index eecdb74..59387d6 100644
--- a/core/java/android/util/FeatureFlagUtils.java
+++ b/core/java/android/util/FeatureFlagUtils.java
@@ -38,7 +38,6 @@
     static {
         DEFAULT_FLAGS = new HashMap<>();
         DEFAULT_FLAGS.put("settings_battery_display_app_list", "false");
-        DEFAULT_FLAGS.put("settings_zone_picker_v2", "true");
         DEFAULT_FLAGS.put("settings_about_phone_v2", "true");
         DEFAULT_FLAGS.put("settings_bluetooth_while_driving", "false");
         DEFAULT_FLAGS.put("settings_data_usage_v2", "true");
diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java
index d4610a5..5deee11 100644
--- a/core/java/android/view/SurfaceControl.java
+++ b/core/java/android/view/SurfaceControl.java
@@ -87,6 +87,7 @@
     private static native void nativeMergeTransaction(long transactionObj,
             long otherTransactionObj);
     private static native void nativeSetAnimationTransaction(long transactionObj);
+    private static native void nativeSetEarlyWakeup(long transactionObj);
 
     private static native void nativeSetLayer(long transactionObj, long nativeObject, int zorder);
     private static native void nativeSetRelativeLayer(long transactionObj, long nativeObject,
@@ -1642,6 +1643,19 @@
         }
 
         /**
+         * Indicate that SurfaceFlinger should wake up earlier than usual as a result of this
+         * transaction. This should be used when the caller thinks that the scene is complex enough
+         * that it's likely to hit GL composition, and thus, SurfaceFlinger needs to more time in
+         * order not to miss frame deadlines.
+         * <p>
+         * Corresponds to setting ISurfaceComposer::eEarlyWakeup
+         */
+        public Transaction setEarlyWakeup() {
+            nativeSetEarlyWakeup(mNativeObject);
+            return this;
+        }
+
+        /**
          * Merge the other transaction into this transaction, clearing the
          * other transaction as if it had been applied.
          */
diff --git a/core/java/android/view/autofill/AutofillPopupWindow.java b/core/java/android/view/autofill/AutofillPopupWindow.java
index 9b49248..a6495d1 100644
--- a/core/java/android/view/autofill/AutofillPopupWindow.java
+++ b/core/java/android/view/autofill/AutofillPopupWindow.java
@@ -79,9 +79,8 @@
     public AutofillPopupWindow(@NonNull IAutofillWindowPresenter presenter) {
         mWindowPresenter = new WindowPresenter(presenter);
 
-        // Here is a bit of voodoo - we want to show the window as system
-        // controlled one so it covers app windows, but at the same time it has to be
-        // an application type (so it's contained inside the application area).
+        // We want to show the window as system controlled one so it covers app windows, but it has
+        // to be an application type (so it's contained inside the application area).
         // Hence, we set it to the application type with the highest z-order, which currently
         // is TYPE_APPLICATION_ABOVE_SUB_PANEL.
         setWindowLayoutType(WindowManager.LayoutParams.TYPE_APPLICATION_ABOVE_SUB_PANEL);
diff --git a/core/java/android/view/inputmethod/BaseInputConnection.java b/core/java/android/view/inputmethod/BaseInputConnection.java
index 5f7a0f7..090e19f 100644
--- a/core/java/android/view/inputmethod/BaseInputConnection.java
+++ b/core/java/android/view/inputmethod/BaseInputConnection.java
@@ -522,7 +522,7 @@
             b = tmp;
         }
 
-        if (a == b) return null;
+        if (a == b || a < 0) return null;
 
         if ((flags&GET_TEXT_WITH_STYLES) != 0) {
             return content.subSequence(a, b);
diff --git a/core/java/android/view/inputmethod/InputMethodInfo.java b/core/java/android/view/inputmethod/InputMethodInfo.java
index f0f30a0..372362f 100644
--- a/core/java/android/view/inputmethod/InputMethodInfo.java
+++ b/core/java/android/view/inputmethod/InputMethodInfo.java
@@ -503,6 +503,14 @@
 
     /**
      * @hide
+     * @return {@code true} if the IME is a trusted system component (e.g. pre-installed)
+     */
+    public boolean isSystem() {
+        return (mService.serviceInfo.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0;
+    }
+
+    /**
+     * @hide
      */
     public boolean isAuxiliaryIme() {
         return mIsAuxIme;
diff --git a/core/java/android/view/textclassifier/GenerateLinksLogger.java b/core/java/android/view/textclassifier/GenerateLinksLogger.java
index 73cf43b..067513f 100644
--- a/core/java/android/view/textclassifier/GenerateLinksLogger.java
+++ b/core/java/android/view/textclassifier/GenerateLinksLogger.java
@@ -19,13 +19,13 @@
 import android.annotation.Nullable;
 import android.metrics.LogMaker;
 import android.util.ArrayMap;
-import android.util.Log;
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.logging.MetricsLogger;
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
 import com.android.internal.util.Preconditions;
 
+import java.util.Locale;
 import java.util.Map;
 import java.util.Objects;
 import java.util.Random;
@@ -39,6 +39,7 @@
 public final class GenerateLinksLogger {
 
     private static final String LOG_TAG = "GenerateLinksLogger";
+    private static final boolean DEBUG_LOG_ENABLED = false;
     private static final String ZERO = "0";
 
     private final MetricsLogger mMetricsLogger;
@@ -127,7 +128,7 @@
     }
 
     private static void debugLog(LogMaker log) {
-        if (!Logger.DEBUG_LOG_ENABLED) return;
+        if (!DEBUG_LOG_ENABLED) return;
 
         final String callId = Objects.toString(
                 log.getTaggedData(MetricsEvent.FIELD_LINKIFY_CALL_ID), "");
@@ -142,8 +143,9 @@
         final int latencyMs = Integer.parseInt(
                 Objects.toString(log.getTaggedData(MetricsEvent.FIELD_LINKIFY_LATENCY), ZERO));
 
-        Log.d(LOG_TAG, String.format("%s:%s %d links (%d/%d chars) %dms %s", callId, entityType,
-                numLinks, linkLength, textLength, latencyMs, log.getPackageName()));
+        Log.d(LOG_TAG,
+                String.format(Locale.US, "%s:%s %d links (%d/%d chars) %dms %s", callId, entityType,
+                        numLinks, linkLength, textLength, latencyMs, log.getPackageName()));
     }
 
     /** Helper class for storing per-entity type statistics. */
diff --git a/core/java/android/view/textclassifier/Logger.java b/core/java/android/view/textclassifier/Logger.java
deleted file mode 100644
index f03906a..0000000
--- a/core/java/android/view/textclassifier/Logger.java
+++ /dev/null
@@ -1,397 +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.view.textclassifier;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.content.Context;
-
-import com.android.internal.util.Preconditions;
-
-import java.text.BreakIterator;
-import java.util.Locale;
-import java.util.Objects;
-
-/**
- * A helper for logging TextClassifier related events.
- * @hide
- */
-public abstract class Logger {
-
-    private static final String LOG_TAG = "Logger";
-    /* package */ static final boolean DEBUG_LOG_ENABLED = true;
-
-    private @SelectionEvent.InvocationMethod int mInvocationMethod;
-    private SelectionEvent mPrevEvent;
-    private SelectionEvent mSmartEvent;
-    private SelectionEvent mStartEvent;
-
-    /**
-     * Logger that does not log anything.
-     * @hide
-     */
-    public static final Logger DISABLED = new Logger() {
-        @Override
-        public void writeEvent(SelectionEvent event) {}
-    };
-
-    @Nullable
-    private final Config mConfig;
-
-    public Logger(Config config) {
-        mConfig = Preconditions.checkNotNull(config);
-    }
-
-    private Logger() {
-        mConfig = null;
-    }
-
-    /**
-     * Writes the selection event to a log.
-     */
-    public abstract void writeEvent(@NonNull SelectionEvent event);
-
-    /**
-     * Returns true if the resultId matches that of a smart selection event (i.e.
-     * {@link SelectionEvent#EVENT_SMART_SELECTION_SINGLE} or
-     * {@link SelectionEvent#EVENT_SMART_SELECTION_MULTI}).
-     * Returns false otherwise.
-     */
-    public boolean isSmartSelection(@NonNull String resultId) {
-        return false;
-    }
-
-    /**
-     * Returns a token iterator for tokenizing text for logging purposes.
-     */
-    public BreakIterator getTokenIterator(@NonNull Locale locale) {
-        return BreakIterator.getWordInstance(Preconditions.checkNotNull(locale));
-    }
-
-    /**
-     * Logs a "selection started" event.
-     *
-     * @param invocationMethod  the way the selection was triggered
-     * @param start  the token index of the selected token
-     */
-    public final void logSelectionStartedEvent(
-            @SelectionEvent.InvocationMethod int invocationMethod, int start) {
-        if (mConfig == null) {
-            return;
-        }
-
-        mInvocationMethod = invocationMethod;
-        logEvent(new SelectionEvent(
-                start, start + 1, SelectionEvent.EVENT_SELECTION_STARTED,
-                TextClassifier.TYPE_UNKNOWN, mInvocationMethod, null, mConfig));
-    }
-
-    /**
-     * Logs a "selection modified" event.
-     * Use when the user modifies the selection.
-     *
-     * @param start  the start token (inclusive) index of the selection
-     * @param end  the end token (exclusive) index of the selection
-     */
-    public final void logSelectionModifiedEvent(int start, int end) {
-        Preconditions.checkArgument(end >= start, "end cannot be less than start");
-
-        if (mConfig == null) {
-            return;
-        }
-
-        logEvent(new SelectionEvent(
-                start, end, SelectionEvent.EVENT_SELECTION_MODIFIED,
-                TextClassifier.TYPE_UNKNOWN, mInvocationMethod, null, mConfig));
-    }
-
-    /**
-     * Logs a "selection modified" event.
-     * Use when the user modifies the selection and the selection's entity type is known.
-     *
-     * @param start  the start token (inclusive) index of the selection
-     * @param end  the end token (exclusive) index of the selection
-     * @param classification  the TextClassification object returned by the TextClassifier that
-     *      classified the selected text
-     */
-    public final void logSelectionModifiedEvent(
-            int start, int end, @NonNull TextClassification classification) {
-        Preconditions.checkArgument(end >= start, "end cannot be less than start");
-        Preconditions.checkNotNull(classification);
-
-        if (mConfig == null) {
-            return;
-        }
-
-        final String entityType = classification.getEntityCount() > 0
-                ? classification.getEntity(0)
-                : TextClassifier.TYPE_UNKNOWN;
-        logEvent(new SelectionEvent(
-                start, end, SelectionEvent.EVENT_SELECTION_MODIFIED,
-                entityType, mInvocationMethod, classification.getId(), mConfig));
-    }
-
-    /**
-     * Logs a "selection modified" event.
-     * Use when a TextClassifier modifies the selection.
-     *
-     * @param start  the start token (inclusive) index of the selection
-     * @param end  the end token (exclusive) index of the selection
-     * @param selection  the TextSelection object returned by the TextClassifier for the
-     *      specified selection
-     */
-    public final void logSelectionModifiedEvent(
-            int start, int end, @NonNull TextSelection selection) {
-        Preconditions.checkArgument(end >= start, "end cannot be less than start");
-        Preconditions.checkNotNull(selection);
-
-        if (mConfig == null) {
-            return;
-        }
-
-        final int eventType;
-        if (isSmartSelection(selection.getId())) {
-            eventType = end - start > 1
-                    ? SelectionEvent.EVENT_SMART_SELECTION_MULTI
-                    : SelectionEvent.EVENT_SMART_SELECTION_SINGLE;
-
-        } else {
-            eventType = SelectionEvent.EVENT_AUTO_SELECTION;
-        }
-        final String entityType = selection.getEntityCount() > 0
-                ? selection.getEntity(0)
-                : TextClassifier.TYPE_UNKNOWN;
-        logEvent(new SelectionEvent(start, end, eventType, entityType, mInvocationMethod,
-                selection.getId(), mConfig));
-    }
-
-    /**
-     * Logs an event specifying an action taken on a selection.
-     * Use when the user clicks on an action to act on the selected text.
-     *
-     * @param start  the start token (inclusive) index of the selection
-     * @param end  the end token (exclusive) index of the selection
-     * @param actionType  the action that was performed on the selection
-     */
-    public final void logSelectionActionEvent(
-            int start, int end, @SelectionEvent.ActionType int actionType) {
-        Preconditions.checkArgument(end >= start, "end cannot be less than start");
-        checkActionType(actionType);
-
-        if (mConfig == null) {
-            return;
-        }
-
-        logEvent(new SelectionEvent(
-                start, end, actionType, TextClassifier.TYPE_UNKNOWN, mInvocationMethod,
-                null, mConfig));
-    }
-
-    /**
-     * Logs an event specifying an action taken on a selection.
-     * Use when the user clicks on an action to act on the selected text and the selection's
-     * entity type is known.
-     *
-     * @param start  the start token (inclusive) index of the selection
-     * @param end  the end token (exclusive) index of the selection
-     * @param actionType  the action that was performed on the selection
-     * @param classification  the TextClassification object returned by the TextClassifier that
-     *      classified the selected text
-     *
-     * @throws IllegalArgumentException If actionType is not a valid SelectionEvent actionType
-     */
-    public final void logSelectionActionEvent(
-            int start, int end, @SelectionEvent.ActionType int actionType,
-            @NonNull TextClassification classification) {
-        Preconditions.checkArgument(end >= start, "end cannot be less than start");
-        Preconditions.checkNotNull(classification);
-        checkActionType(actionType);
-
-        if (mConfig == null) {
-            return;
-        }
-
-        final String entityType = classification.getEntityCount() > 0
-                ? classification.getEntity(0)
-                : TextClassifier.TYPE_UNKNOWN;
-        logEvent(new SelectionEvent(start, end, actionType, entityType, mInvocationMethod,
-                classification.getId(), mConfig));
-    }
-
-    private void logEvent(@NonNull SelectionEvent event) {
-        Preconditions.checkNotNull(event);
-
-        if (event.getEventType() != SelectionEvent.EVENT_SELECTION_STARTED
-                && mStartEvent == null) {
-            if (DEBUG_LOG_ENABLED) {
-                Log.d(LOG_TAG, "Selection session not yet started. Ignoring event");
-            }
-            return;
-        }
-
-        final long now = System.currentTimeMillis();
-        switch (event.getEventType()) {
-            case SelectionEvent.EVENT_SELECTION_STARTED:
-                Preconditions.checkArgument(event.getAbsoluteEnd() == event.getAbsoluteStart() + 1);
-                event.setSessionId(startNewSession());
-                mStartEvent = event;
-                break;
-            case SelectionEvent.EVENT_SMART_SELECTION_SINGLE:  // fall through
-            case SelectionEvent.EVENT_SMART_SELECTION_MULTI:
-                mSmartEvent = event;
-                break;
-            case SelectionEvent.EVENT_SELECTION_MODIFIED:  // fall through
-            case SelectionEvent.EVENT_AUTO_SELECTION:
-                if (mPrevEvent != null
-                        && mPrevEvent.getAbsoluteStart() == event.getAbsoluteStart()
-                        && mPrevEvent.getAbsoluteEnd() == event.getAbsoluteEnd()) {
-                    // Selection did not change. Ignore event.
-                    return;
-                }
-                break;
-            default:
-                // do nothing.
-        }
-
-        event.setEventTime(now);
-        if (mStartEvent != null) {
-            event.setSessionId(mStartEvent.getSessionId())
-                    .setDurationSinceSessionStart(now - mStartEvent.getEventTime())
-                    .setStart(event.getAbsoluteStart() - mStartEvent.getAbsoluteStart())
-                    .setEnd(event.getAbsoluteEnd() - mStartEvent.getAbsoluteStart());
-        }
-        if (mSmartEvent != null) {
-            event.setResultId(mSmartEvent.getResultId())
-                    .setSmartStart(mSmartEvent.getAbsoluteStart() - mStartEvent.getAbsoluteStart())
-                    .setSmartEnd(mSmartEvent.getAbsoluteEnd() - mStartEvent.getAbsoluteStart());
-        }
-        if (mPrevEvent != null) {
-            event.setDurationSincePreviousEvent(now - mPrevEvent.getEventTime())
-                    .setEventIndex(mPrevEvent.getEventIndex() + 1);
-        }
-        writeEvent(event);
-        mPrevEvent = event;
-
-        if (event.isTerminal()) {
-            endSession();
-        }
-    }
-
-    private TextClassificationSessionId startNewSession() {
-        endSession();
-        return new TextClassificationSessionId();
-    }
-
-    private void endSession() {
-        mPrevEvent = null;
-        mSmartEvent = null;
-        mStartEvent = null;
-    }
-
-    /**
-     * @throws IllegalArgumentException If eventType is not an {@link SelectionEvent.ActionType}
-     */
-    private static void checkActionType(@SelectionEvent.EventType int eventType)
-            throws IllegalArgumentException {
-        switch (eventType) {
-            case SelectionEvent.ACTION_OVERTYPE:  // fall through
-            case SelectionEvent.ACTION_COPY:  // fall through
-            case SelectionEvent.ACTION_PASTE:  // fall through
-            case SelectionEvent.ACTION_CUT:  // fall through
-            case SelectionEvent.ACTION_SHARE:  // fall through
-            case SelectionEvent.ACTION_SMART_SHARE:  // fall through
-            case SelectionEvent.ACTION_DRAG:  // fall through
-            case SelectionEvent.ACTION_ABANDON:  // fall through
-            case SelectionEvent.ACTION_SELECT_ALL:  // fall through
-            case SelectionEvent.ACTION_RESET:  // fall through
-                return;
-            default:
-                throw new IllegalArgumentException(
-                        String.format(Locale.US, "%d is not an eventType", eventType));
-        }
-    }
-
-
-    /**
-     * A Logger config.
-     */
-    public static final class Config {
-
-        private final String mPackageName;
-        private final String mWidgetType;
-        @Nullable private final String mWidgetVersion;
-
-        /**
-         * @param context Context of the widget the logger logs for
-         * @param widgetType a name for the widget being logged for. e.g.
-         *      {@link TextClassifier#WIDGET_TYPE_TEXTVIEW}
-         * @param widgetVersion a string version info for the widget the logger logs for
-         */
-        public Config(
-                @NonNull Context context,
-                @TextClassifier.WidgetType String widgetType,
-                @Nullable String widgetVersion) {
-            mPackageName = Preconditions.checkNotNull(context).getPackageName();
-            mWidgetType = widgetType;
-            mWidgetVersion = widgetVersion;
-        }
-
-        /**
-         * Returns the package name of the application the logger logs for.
-         */
-        public String getPackageName() {
-            return mPackageName;
-        }
-
-        /**
-         * Returns the name for the widget being logged for. e.g.
-         * {@link TextClassifier#WIDGET_TYPE_TEXTVIEW}.
-         */
-        public String getWidgetType() {
-            return mWidgetType;
-        }
-
-        /**
-         * Returns string version info for the logger. This is specific to the text classifier.
-         */
-        @Nullable
-        public String getWidgetVersion() {
-            return mWidgetVersion;
-        }
-
-        @Override
-        public int hashCode() {
-            return Objects.hash(mPackageName, mWidgetType, mWidgetVersion);
-        }
-
-        @Override
-        public boolean equals(Object obj) {
-            if (obj == this) {
-                return true;
-            }
-
-            if (!(obj instanceof Config)) {
-                return false;
-            }
-
-            final Config other = (Config) obj;
-            return Objects.equals(mPackageName, other.mPackageName)
-                    && Objects.equals(mWidgetType, other.mWidgetType)
-                    && Objects.equals(mWidgetVersion, other.mWidgetType);
-        }
-    }
-}
diff --git a/core/java/android/view/textclassifier/SelectionEvent.java b/core/java/android/view/textclassifier/SelectionEvent.java
index 1e978cc..b073596 100644
--- a/core/java/android/view/textclassifier/SelectionEvent.java
+++ b/core/java/android/view/textclassifier/SelectionEvent.java
@@ -150,20 +150,6 @@
         mInvocationMethod = invocationMethod;
     }
 
-    SelectionEvent(
-            int start, int end,
-            @EventType int eventType, @EntityType String entityType,
-            @InvocationMethod int invocationMethod, @Nullable String resultId,
-            Logger.Config config) {
-        this(start, end, eventType, entityType, invocationMethod, resultId);
-        Preconditions.checkNotNull(config);
-        setTextClassificationSessionContext(
-                new TextClassificationContext.Builder(
-                        config.getPackageName(), config.getWidgetType())
-                        .setWidgetVersion(config.getWidgetVersion())
-                        .build());
-    }
-
     private SelectionEvent(Parcel in) {
         mAbsoluteStart = in.readInt();
         mAbsoluteEnd = in.readInt();
@@ -362,6 +348,7 @@
             case SelectionEvent.ACTION_ABANDON:  // fall through
             case SelectionEvent.ACTION_SELECT_ALL:  // fall through
             case SelectionEvent.ACTION_RESET:  // fall through
+            case SelectionEvent.ACTION_OTHER:  // fall through
                 return;
             default:
                 throw new IllegalArgumentException(
@@ -667,4 +654,4 @@
             return new SelectionEvent[size];
         }
     };
-}
\ No newline at end of file
+}
diff --git a/core/java/android/view/textclassifier/DefaultLogger.java b/core/java/android/view/textclassifier/SelectionSessionLogger.java
similarity index 88%
rename from core/java/android/view/textclassifier/DefaultLogger.java
rename to core/java/android/view/textclassifier/SelectionSessionLogger.java
index 203ca56..f2fb63e 100644
--- a/core/java/android/view/textclassifier/DefaultLogger.java
+++ b/core/java/android/view/textclassifier/SelectionSessionLogger.java
@@ -17,28 +17,29 @@
 package android.view.textclassifier;
 
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.content.Context;
 import android.metrics.LogMaker;
-import android.util.Log;
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.logging.MetricsLogger;
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
 import com.android.internal.util.Preconditions;
 
+import java.text.BreakIterator;
 import java.util.List;
 import java.util.Locale;
 import java.util.Objects;
 import java.util.StringJoiner;
 
 /**
- * Default Logger.
- * Used internally by TextClassifierImpl.
+ * A helper for logging selection session events.
  * @hide
  */
-public final class DefaultLogger extends Logger {
+public final class SelectionSessionLogger {
 
-    private static final String LOG_TAG = "DefaultLogger";
+    private static final String LOG_TAG = "SelectionSessionLogger";
+    private static final boolean DEBUG_LOG_ENABLED = false;
     static final String CLASSIFIER_ID = "androidtc";
 
     private static final int START_EVENT_DELTA = MetricsEvent.FIELD_SELECTION_SINCE_START;
@@ -59,23 +60,16 @@
 
     private final MetricsLogger mMetricsLogger;
 
-    public DefaultLogger(@NonNull Config config) {
-        super(config);
+    public SelectionSessionLogger() {
         mMetricsLogger = new MetricsLogger();
     }
 
     @VisibleForTesting
-    public DefaultLogger(@NonNull Config config, @NonNull MetricsLogger metricsLogger) {
-        super(config);
+    public SelectionSessionLogger(@NonNull MetricsLogger metricsLogger) {
         mMetricsLogger = Preconditions.checkNotNull(metricsLogger);
     }
 
-    @Override
-    public boolean isSmartSelection(@NonNull String signature) {
-        return CLASSIFIER_ID.equals(SignatureParser.getClassifierId(signature));
-    }
-
-    @Override
+    /** Emits a selection event to the logs. */
     public void writeEvent(@NonNull SelectionEvent event) {
         Preconditions.checkNotNull(event);
         final LogMaker log = new LogMaker(MetricsEvent.TEXT_SELECTION_SESSION)
@@ -93,7 +87,7 @@
                 .addTaggedData(SMART_END, event.getSmartEnd())
                 .addTaggedData(EVENT_START, event.getStart())
                 .addTaggedData(EVENT_END, event.getEnd())
-                .addTaggedData(SESSION_ID, event.getSessionId());
+                .addTaggedData(SESSION_ID, event.getSessionId().flattenToString());
         mMetricsLogger.write(log);
         debugLog(log);
     }
@@ -225,9 +219,17 @@
         final int eventEnd = Integer.parseInt(
                 Objects.toString(log.getTaggedData(EVENT_END), ZERO));
 
-        Log.d(LOG_TAG, String.format("%2d: %s/%s/%s, range=%d,%d - smart_range=%d,%d (%s/%s)",
-                index, type, subType, entity, eventStart, eventEnd, smartStart, smartEnd, widget,
-                model));
+        Log.d(LOG_TAG,
+                String.format(Locale.US, "%2d: %s/%s/%s, range=%d,%d - smart_range=%d,%d (%s/%s)",
+                        index, type, subType, entity, eventStart, eventEnd, smartStart, smartEnd,
+                        widget, model));
+    }
+
+    /**
+     * Returns a token iterator for tokenizing text for logging purposes.
+     */
+    public static BreakIterator getTokenIterator(@NonNull Locale locale) {
+        return BreakIterator.getWordInstance(Preconditions.checkNotNull(locale));
     }
 
     /**
@@ -260,8 +262,10 @@
             return String.format(Locale.US, "%s|%s|%d", classifierId, modelName, hash);
         }
 
-        static String getClassifierId(String signature) {
-            Preconditions.checkNotNull(signature);
+        static String getClassifierId(@Nullable String signature) {
+            if (signature == null) {
+                return "";
+            }
             final int end = signature.indexOf("|");
             if (end >= 0) {
                 return signature.substring(0, end);
@@ -269,8 +273,10 @@
             return "";
         }
 
-        static String getModelName(String signature) {
-            Preconditions.checkNotNull(signature);
+        static String getModelName(@Nullable String signature) {
+            if (signature == null) {
+                return "";
+            }
             final int start = signature.indexOf("|") + 1;
             final int end = signature.indexOf("|", start);
             if (start >= 1 && end >= start) {
@@ -279,8 +285,10 @@
             return "";
         }
 
-        static int getHash(String signature) {
-            Preconditions.checkNotNull(signature);
+        static int getHash(@Nullable String signature) {
+            if (signature == null) {
+                return 0;
+            }
             final int index1 = signature.indexOf("|");
             final int index2 = signature.indexOf("|", index1);
             if (index2 > 0) {
diff --git a/core/java/android/view/textclassifier/SystemTextClassifier.java b/core/java/android/view/textclassifier/SystemTextClassifier.java
index 45fd6bf..490c389 100644
--- a/core/java/android/view/textclassifier/SystemTextClassifier.java
+++ b/core/java/android/view/textclassifier/SystemTextClassifier.java
@@ -28,7 +28,6 @@
 import android.service.textclassifier.ITextLinksCallback;
 import android.service.textclassifier.ITextSelectionCallback;
 
-import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.annotations.VisibleForTesting.Visibility;
 import com.android.internal.util.Preconditions;
@@ -49,13 +48,6 @@
     private final TextClassificationConstants mSettings;
     private final TextClassifier mFallback;
     private final String mPackageName;
-
-    private final Object mLoggerLock = new Object();
-    @GuardedBy("mLoggerLock")
-    private Logger.Config mLoggerConfig;
-    @GuardedBy("mLoggerLock")
-    private Logger mLogger;
-    @GuardedBy("mLoggerLock")
     private TextClassificationSessionId mSessionId;
 
     public SystemTextClassifier(Context context, TextClassificationConstants settings)
@@ -147,27 +139,6 @@
     }
 
     @Override
-    public Logger getLogger(@NonNull Logger.Config config) {
-        Preconditions.checkNotNull(config);
-        synchronized (mLoggerLock) {
-            if (mLogger == null || !config.equals(mLoggerConfig)) {
-                mLoggerConfig = config;
-                mLogger = new Logger(config) {
-                    @Override
-                    public void writeEvent(SelectionEvent event) {
-                        try {
-                            mManagerService.onSelectionEvent(mSessionId, event);
-                        } catch (RemoteException e) {
-                            Log.e(LOG_TAG, "Error reporting selection event.", e);
-                        }
-                    }
-                };
-            }
-        }
-        return mLogger;
-    }
-
-    @Override
     public void destroy() {
         try {
             if (mSessionId != null) {
diff --git a/core/java/android/view/textclassifier/TextClassificationSession.java b/core/java/android/view/textclassifier/TextClassificationSession.java
index e8e300a..4c64198 100644
--- a/core/java/android/view/textclassifier/TextClassificationSession.java
+++ b/core/java/android/view/textclassifier/TextClassificationSession.java
@@ -17,7 +17,6 @@
 package android.view.textclassifier;
 
 import android.annotation.WorkerThread;
-import android.view.textclassifier.DefaultLogger.SignatureParser;
 import android.view.textclassifier.SelectionEvent.InvocationMethod;
 
 import com.android.internal.util.Preconditions;
@@ -222,7 +221,8 @@
         }
 
         private static boolean isPlatformLocalTextClassifierSmartSelection(String signature) {
-            return DefaultLogger.CLASSIFIER_ID.equals(SignatureParser.getClassifierId(signature));
+            return SelectionSessionLogger.CLASSIFIER_ID.equals(
+                    SelectionSessionLogger.SignatureParser.getClassifierId(signature));
         }
     }
 }
diff --git a/core/java/android/view/textclassifier/TextClassifier.java b/core/java/android/view/textclassifier/TextClassifier.java
index f048d29..da47bcb 100644
--- a/core/java/android/view/textclassifier/TextClassifier.java
+++ b/core/java/android/view/textclassifier/TextClassifier.java
@@ -359,18 +359,6 @@
     }
 
     /**
-     * Returns a helper for logging TextClassifier related events.
-     *
-     * @param config logger configuration
-     * @hide
-     */
-    @WorkerThread
-    default Logger getLogger(@NonNull Logger.Config config) {
-        Preconditions.checkNotNull(config);
-        return Logger.DISABLED;
-    }
-
-    /**
      * Reports a selection event.
      *
      * <strong>NOTE: </strong>If a TextClassifier has been destroyed, calls to this method should
diff --git a/core/java/android/view/textclassifier/TextClassifierImpl.java b/core/java/android/view/textclassifier/TextClassifierImpl.java
index 7e3748a..2213355 100644
--- a/core/java/android/view/textclassifier/TextClassifierImpl.java
+++ b/core/java/android/view/textclassifier/TextClassifierImpl.java
@@ -94,11 +94,7 @@
 
     private final Object mLoggerLock = new Object();
     @GuardedBy("mLoggerLock") // Do not access outside this lock.
-    private Logger.Config mLoggerConfig;
-    @GuardedBy("mLoggerLock") // Do not access outside this lock.
-    private Logger mLogger;
-    @GuardedBy("mLoggerLock") // Do not access outside this lock.
-    private Logger mLogger2;  // This is the new logger. Will replace mLogger.
+    private SelectionSessionLogger mSessionLogger;
 
     private final TextClassificationConstants mSettings;
 
@@ -283,28 +279,14 @@
         }
     }
 
-    /** @inheritDoc */
-    @Override
-    public Logger getLogger(@NonNull Logger.Config config) {
-        Preconditions.checkNotNull(config);
-        synchronized (mLoggerLock) {
-            if (mLogger == null || !config.equals(mLoggerConfig)) {
-                mLoggerConfig = config;
-                mLogger = new DefaultLogger(config);
-            }
-        }
-        return mLogger;
-    }
-
     @Override
     public void onSelectionEvent(SelectionEvent event) {
         Preconditions.checkNotNull(event);
         synchronized (mLoggerLock) {
-            if (mLogger2 == null) {
-                mLogger2 = new DefaultLogger(
-                        new Logger.Config(mContext, WIDGET_TYPE_UNKNOWN, null));
+            if (mSessionLogger == null) {
+                mSessionLogger = new SelectionSessionLogger();
             }
-            mLogger2.writeEvent(event);
+            mSessionLogger.writeEvent(event);
         }
     }
 
@@ -331,7 +313,7 @@
 
     private String createId(String text, int start, int end) {
         synchronized (mLock) {
-            return DefaultLogger.createId(text, start, end, mContext, mModel.getVersion(),
+            return SelectionSessionLogger.createId(text, start, end, mContext, mModel.getVersion(),
                     mModel.getSupportedLocales());
         }
     }
diff --git a/core/java/android/webkit/FindAddress.java b/core/java/android/webkit/FindAddress.java
index 31b2427..9183227 100644
--- a/core/java/android/webkit/FindAddress.java
+++ b/core/java/android/webkit/FindAddress.java
@@ -429,20 +429,21 @@
 
                     // At this point we've matched a state; try to match a zip code after it.
                     Matcher zipMatcher = sWordRe.matcher(content);
-                    if (zipMatcher.find(stateMatch.end())
-                            && isValidZipCode(zipMatcher.group(0), stateMatch)) {
-                        return zipMatcher.end();
+                    if (zipMatcher.find(stateMatch.end())) {
+                        if (isValidZipCode(zipMatcher.group(0), stateMatch)) {
+                            return zipMatcher.end();
+                        }
+                    } else {
+                        // The content ends with a state but no zip
+                        // code. This is a legal match according to the
+                        // documentation. N.B. This is equivalent to the
+                        // original c++ implementation, which only allowed
+                        // the zip code to be optional at the end of the
+                        // string, which presumably is a bug.  We tried
+                        // relaxing this to work in other places but it
+                        // caused too many false positives.
+                        nonZipMatch = stateMatch.end();
                     }
-                    // The content ends with a state but no zip
-                    // code. This is a legal match according to the
-                    // documentation. N.B. This differs from the
-                    // original c++ implementation, which only allowed
-                    // the zip code to be optional at the end of the
-                    // string, which presumably is a bug.  Now we
-                    // prefer to find a match with a zip code, but
-                    // remember non-zip matches and return them if
-                    // necessary.
-                    nonZipMatch = stateMatch.end();
                 }
             }
         }
diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java
index 9946726..6af678b 100644
--- a/core/java/android/widget/Editor.java
+++ b/core/java/android/widget/Editor.java
@@ -6031,7 +6031,9 @@
             mSwitchedLines = false;
             final int selectionStart = mTextView.getSelectionStart();
             final int selectionEnd = mTextView.getSelectionEnd();
-            if (selectionStart > selectionEnd) {
+            if (selectionStart < 0 || selectionEnd < 0) {
+                Selection.removeSelection((Spannable) mTextView.getText());
+            } else if (selectionStart > selectionEnd) {
                 Selection.setSelection((Spannable) mTextView.getText(),
                         selectionEnd, selectionStart);
             }
diff --git a/core/java/android/widget/SelectionActionModeHelper.java b/core/java/android/widget/SelectionActionModeHelper.java
index b3327a7..468abdc 100644
--- a/core/java/android/widget/SelectionActionModeHelper.java
+++ b/core/java/android/widget/SelectionActionModeHelper.java
@@ -33,9 +33,9 @@
 import android.text.TextUtils;
 import android.util.Log;
 import android.view.ActionMode;
-import android.view.textclassifier.Logger;
 import android.view.textclassifier.SelectionEvent;
 import android.view.textclassifier.SelectionEvent.InvocationMethod;
+import android.view.textclassifier.SelectionSessionLogger;
 import android.view.textclassifier.TextClassification;
 import android.view.textclassifier.TextClassificationConstants;
 import android.view.textclassifier.TextClassificationManager;
@@ -663,7 +663,6 @@
         private static final String LOG_TAG = "SelectionMetricsLogger";
         private static final Pattern PATTERN_WHITESPACE = Pattern.compile("\\s+");
 
-        private final Logger mLogger;
         private final boolean mEditTextLogger;
         private final BreakIterator mTokenIterator;
 
@@ -673,10 +672,8 @@
 
         SelectionMetricsLogger(TextView textView) {
             Preconditions.checkNotNull(textView);
-            mLogger = textView.getTextClassifier().getLogger(
-                    new Logger.Config(textView.getContext(), getWidetType(textView), null));
             mEditTextLogger = textView.isTextEditable();
-            mTokenIterator = mLogger.getTokenIterator(textView.getTextLocale());
+            mTokenIterator = SelectionSessionLogger.getTokenIterator(textView.getTextLocale());
         }
 
         @TextClassifier.WidgetType
@@ -702,8 +699,6 @@
                 }
                 mTokenIterator.setText(mText);
                 mStartIndex = index;
-                mLogger.logSelectionStartedEvent(invocationMethod, 0);
-                // TODO: Remove the above legacy logging.
                 mClassificationSession = classificationSession;
                 mClassificationSession.onSelectionEvent(
                         SelectionEvent.createSelectionStartedEvent(invocationMethod, 0));
@@ -720,27 +715,18 @@
                 Preconditions.checkArgumentInRange(end, start, mText.length(), "end");
                 int[] wordIndices = getWordDelta(start, end);
                 if (selection != null) {
-                    mLogger.logSelectionModifiedEvent(
-                            wordIndices[0], wordIndices[1], selection);
-                    // TODO: Remove the above legacy logging.
                     if (mClassificationSession != null) {
                         mClassificationSession.onSelectionEvent(
                                 SelectionEvent.createSelectionModifiedEvent(
                                         wordIndices[0], wordIndices[1], selection));
                     }
                 } else if (classification != null) {
-                    mLogger.logSelectionModifiedEvent(
-                            wordIndices[0], wordIndices[1], classification);
-                    // TODO: Remove the above legacy logging.
                     if (mClassificationSession != null) {
                         mClassificationSession.onSelectionEvent(
                                 SelectionEvent.createSelectionModifiedEvent(
                                         wordIndices[0], wordIndices[1], classification));
                     }
                 } else {
-                    mLogger.logSelectionModifiedEvent(
-                            wordIndices[0], wordIndices[1]);
-                    // TODO: Remove the above legacy logging.
                     if (mClassificationSession != null) {
                         mClassificationSession.onSelectionEvent(
                                 SelectionEvent.createSelectionModifiedEvent(
@@ -762,18 +748,12 @@
                 Preconditions.checkArgumentInRange(end, start, mText.length(), "end");
                 int[] wordIndices = getWordDelta(start, end);
                 if (classification != null) {
-                    mLogger.logSelectionActionEvent(
-                            wordIndices[0], wordIndices[1], action, classification);
-                    // TODO: Remove the above legacy logging.
                     if (mClassificationSession != null) {
                         mClassificationSession.onSelectionEvent(
                                 SelectionEvent.createSelectionActionEvent(
                                         wordIndices[0], wordIndices[1], action, classification));
                     }
                 } else {
-                    mLogger.logSelectionActionEvent(
-                            wordIndices[0], wordIndices[1], action);
-                    // TODO: Remove the above legacy logging.
                     if (mClassificationSession != null) {
                         mClassificationSession.onSelectionEvent(
                                 SelectionEvent.createSelectionActionEvent(
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index a120e9b..dbcd2d0 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -9396,7 +9396,7 @@
         final int selectionStart = getSelectionStart();
         final int selectionEnd = getSelectionEnd();
 
-        return selectionStart >= 0 && selectionStart != selectionEnd;
+        return selectionStart >= 0 && selectionEnd > 0 && selectionStart != selectionEnd;
     }
 
     String getSelectedText() {
diff --git a/core/java/com/android/internal/inputmethod/InputMethodUtils.java b/core/java/com/android/internal/inputmethod/InputMethodUtils.java
index 9a082ec..ddba477 100644
--- a/core/java/com/android/internal/inputmethod/InputMethodUtils.java
+++ b/core/java/com/android/internal/inputmethod/InputMethodUtils.java
@@ -129,16 +129,16 @@
     }
     // ----------------------------------------------------------------------
 
-    public static boolean isSystemIme(InputMethodInfo inputMethod) {
-        return (inputMethod.getServiceInfo().applicationInfo.flags
-                & ApplicationInfo.FLAG_SYSTEM) != 0;
+    @Deprecated
+    public static boolean isSystemIme(@NonNull InputMethodInfo inputMethod) {
+        return inputMethod.isSystem();
     }
 
     public static boolean isSystemImeThatHasSubtypeOf(final InputMethodInfo imi,
             final Context context, final boolean checkDefaultAttribute,
             @Nullable final Locale requiredLocale, final boolean checkCountry,
             final String requiredSubtypeMode) {
-        if (!isSystemIme(imi)) {
+        if (!imi.isSystem()) {
             return false;
         }
         if (checkDefaultAttribute && !imi.isDefault(context)) {
@@ -182,7 +182,7 @@
 
     private static boolean isSystemAuxilialyImeThatHasAutomaticSubtype(final InputMethodInfo imi,
             final Context context, final boolean checkDefaultAttribute) {
-        if (!isSystemIme(imi)) {
+        if (!imi.isSystem()) {
             return false;
         }
         if (checkDefaultAttribute && !imi.isDefault(context)) {
@@ -435,12 +435,11 @@
             if (imi.isAuxiliaryIme()) {
                 continue;
             }
-            if (InputMethodUtils.isSystemIme(imi)
-                    && containsSubtypeOf(imi, ENGLISH_LOCALE, false /* checkCountry */,
-                            SUBTYPE_MODE_KEYBOARD)) {
+            if (imi.isSystem() && containsSubtypeOf(
+                    imi, ENGLISH_LOCALE, false /* checkCountry */, SUBTYPE_MODE_KEYBOARD)) {
                 return imi;
             }
-            if (firstFoundSystemIme < 0 && InputMethodUtils.isSystemIme(imi)) {
+            if (firstFoundSystemIme < 0 && imi.isSystem()) {
                 firstFoundSystemIme = i;
             }
         }
diff --git a/core/jni/android_view_SurfaceControl.cpp b/core/jni/android_view_SurfaceControl.cpp
index b0f68cd..3f58afa 100644
--- a/core/jni/android_view_SurfaceControl.cpp
+++ b/core/jni/android_view_SurfaceControl.cpp
@@ -327,6 +327,11 @@
     transaction->setAnimationTransaction();
 }
 
+static void nativeSetEarlyWakeup(JNIEnv* env, jclass clazz, jlong transactionObj) {
+    auto transaction = reinterpret_cast<SurfaceComposerClient::Transaction*>(transactionObj);
+    transaction->setEarlyWakeup();
+}
+
 static void nativeSetLayer(JNIEnv* env, jclass clazz, jlong transactionObj,
         jlong nativeObject, jint zorder) {
     auto transaction = reinterpret_cast<SurfaceComposerClient::Transaction*>(transactionObj);
@@ -934,6 +939,8 @@
             (void*)nativeMergeTransaction },
     {"nativeSetAnimationTransaction", "(J)V",
             (void*)nativeSetAnimationTransaction },
+    {"nativeSetEarlyWakeup", "(J)V",
+            (void*)nativeSetEarlyWakeup },
     {"nativeSetLayer", "(JJI)V",
             (void*)nativeSetLayer },
     {"nativeSetRelativeLayer", "(JJLandroid/os/IBinder;I)V",
diff --git a/core/proto/android/os/incident.proto b/core/proto/android/os/incident.proto
index 2a7c256..ab15d4f 100644
--- a/core/proto/android/os/incident.proto
+++ b/core/proto/android/os/incident.proto
@@ -118,17 +118,17 @@
 
     // Stack dumps
     optional android.os.BackTraceProto native_traces = 1200 [
-        (section).type = SECTION_NONE,
+        (section).type = SECTION_TOMBSTONE,
         (section).args = "native"
     ];
 
     optional android.os.BackTraceProto hal_traces = 1201 [
-        (section).type = SECTION_NONE,
+        (section).type = SECTION_TOMBSTONE,
         (section).args = "hal"
     ];
 
     optional android.os.BackTraceProto java_traces = 1202 [
-        (section).type = SECTION_NONE,
+        (section).type = SECTION_TOMBSTONE,
         (section).args = "java"
     ];
 
diff --git a/core/proto/android/providers/settings/secure.proto b/core/proto/android/providers/settings/secure.proto
index 3de8c39..237efd8 100644
--- a/core/proto/android/providers/settings/secure.proto
+++ b/core/proto/android/providers/settings/secure.proto
@@ -213,6 +213,13 @@
     optional SettingProto keyguard_slice_uri = 29;
     optional SettingProto last_setup_shown = 30 [ (android.privacy).dest = DEST_AUTOMATIC ];
 
+    message Launcher {
+        option (android.msg_privacy).dest = DEST_EXPLICIT;
+
+        optional SettingProto swipe_up_to_switch_apps_enabled = 1 [ (android.privacy).dest = DEST_AUTOMATIC ];
+    }
+    optional Launcher launcher = 70;
+
     message Location {
         option (android.msg_privacy).dest = DEST_EXPLICIT;
 
@@ -479,5 +486,5 @@
 
     // Please insert fields in alphabetical order and group them into messages
     // if possible (to avoid reaching the method limit).
-    // Next tag = 70;
+    // Next tag = 71;
 }
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 8443a67..4ce89c9 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -974,6 +974,9 @@
     -->
     <integer name="config_longPressOnBackBehavior">0</integer>
 
+    <!-- Allows activities to be launched on a long press on power during device setup. -->
+    <bool name="config_allowStartActivityForLongPressOnPowerInSetup">false</bool>
+
     <!-- Control the behavior when the user short presses the power button.
             0 - Nothing
             1 - Go to sleep (doze)
diff --git a/core/res/res/values/styles_device_defaults.xml b/core/res/res/values/styles_device_defaults.xml
index 1a51c1d..d722961 100644
--- a/core/res/res/values/styles_device_defaults.xml
+++ b/core/res/res/values/styles_device_defaults.xml
@@ -128,6 +128,9 @@
     <style name="Widget.DeviceDefault.TextSelectHandle" parent="Widget.Material.TextSelectHandle"/>
     <style name="Widget.DeviceDefault.TextView.ListSeparator" parent="Widget.Material.TextView.ListSeparator"/>
     <style name="Widget.DeviceDefault.TimePicker" parent="Widget.Material.TimePicker"/>
+    <style name="Widget.DeviceDefault.Toolbar" parent="Widget.Material.Toolbar">
+        <item name="titleTextAppearance">@style/TextAppearance.DeviceDefault.Widget.Toolbar.Title</item>
+    </style>
 
     <style name="Widget.DeviceDefault.Light" parent="Widget.Material.Light"/>
     <style name="Widget.DeviceDefault.Light.Button" parent="Widget.Material.Light.Button"/>
@@ -186,7 +189,9 @@
     <style name="Widget.DeviceDefault.Light.ActionBar.TabView" parent="Widget.Material.Light.ActionBar.TabView"/>
     <style name="Widget.DeviceDefault.Light.ActionBar.TabText" parent="Widget.Material.Light.ActionBar.TabText"/>
     <style name="Widget.DeviceDefault.Light.ActionBar.TabBar" parent="Widget.Material.Light.ActionBar.TabBar"/>
-    <style name="Widget.DeviceDefault.Light.ActionBar.Solid" parent="Widget.Material.Light.ActionBar.Solid"/>
+    <style name="Widget.DeviceDefault.Light.ActionBar.Solid" parent="Widget.Material.Light.ActionBar.Solid">
+        <item name="titleTextStyle">@style/TextAppearance.DeviceDefault.Widget.ActionBar.Title</item>
+    </style>
     <!-- @deprecated Action bars are now themed using the inheritable android:theme attribute. -->
     <style name="Widget.DeviceDefault.Light.ActionBar.Solid.Inverse" parent="Widget.Holo.Light.ActionBar.Solid.Inverse"/>
     <!-- @deprecated Action bars are now themed using the inheritable android:theme attribute. -->
@@ -242,12 +247,18 @@
     <style name="TextAppearance.DeviceDefault.Widget.PopupMenu" parent="TextAppearance.Material.Widget.PopupMenu"/>
     <style name="TextAppearance.DeviceDefault.Widget.PopupMenu.Large" parent="TextAppearance.Material.Widget.PopupMenu.Large"/>
     <style name="TextAppearance.DeviceDefault.Widget.PopupMenu.Small" parent="TextAppearance.Material.Widget.PopupMenu.Small"/>
-    <style name="TextAppearance.DeviceDefault.Widget.ActionBar.Title" parent="TextAppearance.Material.Widget.ActionBar.Title"/>
+    <style name="TextAppearance.DeviceDefault.Widget.ActionBar.Title" parent="TextAppearance.Material.Widget.ActionBar.Title">
+        <item name="fontFamily">@string/config_headlineFontFamilyMedium</item>
+    </style>
     <style name="TextAppearance.DeviceDefault.Widget.ActionBar.Subtitle" parent="TextAppearance.Material.Widget.ActionBar.Subtitle"/>
     <style name="TextAppearance.DeviceDefault.Widget.ActionMode.Title" parent="TextAppearance.Material.Widget.ActionMode.Title"/>
     <style name="TextAppearance.DeviceDefault.Widget.ActionMode.Subtitle" parent="TextAppearance.Material.Widget.ActionMode.Subtitle"/>
-    <style name="TextAppearance.DeviceDefault.WindowTitle" parent="TextAppearance.Material.WindowTitle"/>
-    <style name="TextAppearance.DeviceDefault.DialogWindowTitle" parent="TextAppearance.Material.DialogWindowTitle"/>
+    <style name="TextAppearance.DeviceDefault.WindowTitle" parent="TextAppearance.Material.WindowTitle">
+        <item name="fontFamily">@string/config_headlineFontFamilyMedium</item>
+    </style>
+    <style name="TextAppearance.DeviceDefault.DialogWindowTitle" parent="TextAppearance.Material.DialogWindowTitle">
+        <item name="fontFamily">@string/config_headlineFontFamilyMedium</item>
+    </style>
     <!-- @deprecated Action bars are now themed using the inheritable android:theme attribute. -->
     <style name="TextAppearance.DeviceDefault.Widget.ActionBar.Title.Inverse" parent="TextAppearance.Material.Widget.ActionBar.Title.Inverse"/>
     <!-- @deprecated Action bars are now themed using the inheritable android:theme attribute. -->
@@ -257,6 +268,7 @@
     <!-- @deprecated Action bars are now themed using the inheritable android:theme attribute. -->
     <style name="TextAppearance.DeviceDefault.Widget.ActionMode.Subtitle.Inverse" parent="TextAppearance.Material.Widget.ActionMode.Subtitle.Inverse"/>
     <style name="TextAppearance.DeviceDefault.Widget.ActionBar.Menu" parent="TextAppearance.Material.Widget.ActionBar.Menu"/>
+    <style name="TextAppearance.DeviceDefault.Widget.Toolbar.Title" parent="TextAppearance.DeviceDefault.Widget.ActionBar.Title" />
 
     <!-- Preference Styles -->
     <style name="Preference.DeviceDefault" parent="Preference.Material"/>
@@ -280,11 +292,15 @@
     <style name="Animation.DeviceDefault.Dialog" parent="Animation.Material.Dialog"/>
 
     <!-- DialogWindowTitle Styles -->
-    <style name="DialogWindowTitle.DeviceDefault" parent="DialogWindowTitle.Material"/>
-    <style name="DialogWindowTitle.DeviceDefault.Light" parent="DialogWindowTitle.Material.Light"/>
+    <style name="DialogWindowTitle.DeviceDefault" parent="DialogWindowTitle.Material">
+        <item name="textAppearance">@style/TextAppearance.DeviceDefault.DialogWindowTitle</item>
+    </style>
+    <style name="DialogWindowTitle.DeviceDefault.Light"/>
 
     <!-- WindowTitle Styles -->
-    <style name="WindowTitle.DeviceDefault" parent="WindowTitle.Material"/>
+    <style name="WindowTitle.DeviceDefault" parent="WindowTitle.Material">
+        <item name="textAppearance">@style/TextAppearance.DeviceDefault.WindowTitle</item>
+    </style>
     <style name="WindowTitleBackground.DeviceDefault" parent="WindowTitleBackground.Material"/>
 
     <!-- Other Styles -->
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 5856648..ce7f1ff 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -434,6 +434,7 @@
   <java-symbol type="integer" name="config_veryLongPressOnPowerBehavior" />
   <java-symbol type="integer" name="config_veryLongPressTimeout" />
   <java-symbol type="integer" name="config_longPressOnBackBehavior" />
+  <java-symbol type="bool" name="config_allowStartActivityForLongPressOnPowerInSetup" />
   <java-symbol type="integer" name="config_lowMemoryKillerMinFreeKbytesAdjust" />
   <java-symbol type="integer" name="config_lowMemoryKillerMinFreeKbytesAbsolute" />
   <java-symbol type="integer" name="config_max_pan_devices" />
diff --git a/core/res/res/values/themes_device_defaults.xml b/core/res/res/values/themes_device_defaults.xml
index 2327b33..fd6cc95 100644
--- a/core/res/res/values/themes_device_defaults.xml
+++ b/core/res/res/values/themes_device_defaults.xml
@@ -181,6 +181,9 @@
         <item name="actionBarStyle">@style/Widget.DeviceDefault.ActionBar.Solid</item>
         <item name="actionModePopupWindowStyle">@style/Widget.DeviceDefault.PopupWindow.ActionMode</item>
 
+        <!-- Toolbar attributes -->
+        <item name="toolbarStyle">@style/Widget.DeviceDefault.Toolbar</item>
+
         <item name="buttonBarStyle">@style/DeviceDefault.ButtonBar</item>
         <item name="segmentedButtonStyle">@style/DeviceDefault.SegmentedButton</item>
 
@@ -233,6 +236,9 @@
         <!-- Progress bar attributes -->
         <item name="colorProgressBackgroundNormal">@color/config_progress_background_tint</item>
         <item name="progressBarCornerRadius">@dimen/config_progressBarCornerRadius</item>
+
+        <!-- Toolbar attributes -->
+        <item name="toolbarStyle">@style/Widget.DeviceDefault.Toolbar</item>
     </style>
 
     <!-- Variant of {@link #Theme_DeviceDefault} with no action bar and no status bar.  This theme
@@ -257,6 +263,9 @@
         <!-- Progress bar attributes -->
         <item name="colorProgressBackgroundNormal">@color/config_progress_background_tint</item>
         <item name="progressBarCornerRadius">@dimen/config_progressBarCornerRadius</item>
+
+        <!-- Toolbar attributes -->
+        <item name="toolbarStyle">@style/Widget.DeviceDefault.Toolbar</item>
     </style>
 
     <!-- Variant of {@link #Theme_DeviceDefault} with no action bar and no status bar and
@@ -283,6 +292,9 @@
         <!-- Progress bar attributes -->
         <item name="colorProgressBackgroundNormal">@color/config_progress_background_tint</item>
         <item name="progressBarCornerRadius">@dimen/config_progressBarCornerRadius</item>
+
+        <!-- Toolbar attributes -->
+        <item name="toolbarStyle">@style/Widget.DeviceDefault.Toolbar</item>
     </style>
 
     <!-- Variant of {@link #Theme_DeviceDefault} that has no title bar and translucent
@@ -308,6 +320,9 @@
         <!-- Progress bar attributes -->
         <item name="colorProgressBackgroundNormal">@color/config_progress_background_tint</item>
         <item name="progressBarCornerRadius">@dimen/config_progressBarCornerRadius</item>
+
+        <!-- Toolbar attributes -->
+        <item name="toolbarStyle">@style/Widget.DeviceDefault.Toolbar</item>
     </style>
 
     <!-- DeviceDefault theme for dialog windows and activities. This changes the window to be
@@ -341,6 +356,9 @@
         <!-- Progress bar attributes -->
         <item name="colorProgressBackgroundNormal">@color/config_progress_background_tint</item>
         <item name="progressBarCornerRadius">@dimen/config_progressBarCornerRadius</item>
+
+        <!-- Toolbar attributes -->
+        <item name="toolbarStyle">@style/Widget.DeviceDefault.Toolbar</item>
     </style>
 
     <!-- Variant of {@link #Theme_DeviceDefault_Dialog} that has a nice minimum width for a
@@ -365,6 +383,9 @@
         <!-- Progress bar attributes -->
         <item name="colorProgressBackgroundNormal">@color/config_progress_background_tint</item>
         <item name="progressBarCornerRadius">@dimen/config_progressBarCornerRadius</item>
+
+        <!-- Toolbar attributes -->
+        <item name="toolbarStyle">@style/Widget.DeviceDefault.Toolbar</item>
     </style>
 
     <!-- Variant of {@link #Theme_DeviceDefault_Dialog} without an action bar -->
@@ -388,6 +409,9 @@
         <!-- Progress bar attributes -->
         <item name="colorProgressBackgroundNormal">@color/config_progress_background_tint</item>
         <item name="progressBarCornerRadius">@dimen/config_progressBarCornerRadius</item>
+
+        <!-- Toolbar attributes -->
+        <item name="toolbarStyle">@style/Widget.DeviceDefault.Toolbar</item>
     </style>
 
     <!-- Variant of {@link #Theme_DeviceDefault_Dialog_NoActionBar} that has a nice minimum width
@@ -412,6 +436,9 @@
         <!-- Progress bar attributes -->
         <item name="colorProgressBackgroundNormal">@color/config_progress_background_tint</item>
         <item name="progressBarCornerRadius">@dimen/config_progressBarCornerRadius</item>
+
+        <!-- Toolbar attributes -->
+        <item name="toolbarStyle">@style/Widget.DeviceDefault.Toolbar</item>
     </style>
 
     <!-- Variant of Theme.DeviceDefault.Dialog that has a fixed size. -->
@@ -452,6 +479,9 @@
         <!-- Progress bar attributes -->
         <item name="colorProgressBackgroundNormal">@color/config_progress_background_tint</item>
         <item name="progressBarCornerRadius">@dimen/config_progressBarCornerRadius</item>
+
+        <!-- Toolbar attributes -->
+        <item name="toolbarStyle">@style/Widget.DeviceDefault.Toolbar</item>
     </style>
 
     <!-- DeviceDefault theme for a window without an action bar that will be displayed either
@@ -477,6 +507,9 @@
         <!-- Progress bar attributes -->
         <item name="colorProgressBackgroundNormal">@color/config_progress_background_tint</item>
         <item name="progressBarCornerRadius">@dimen/config_progressBarCornerRadius</item>
+
+        <!-- Toolbar attributes -->
+        <item name="toolbarStyle">@style/Widget.DeviceDefault.Toolbar</item>
     </style>
 
     <!-- DeviceDefault theme for a presentation window on a secondary display. -->
@@ -500,6 +533,9 @@
         <!-- Progress bar attributes -->
         <item name="colorProgressBackgroundNormal">@color/config_progress_background_tint</item>
         <item name="progressBarCornerRadius">@dimen/config_progressBarCornerRadius</item>
+
+        <!-- Toolbar attributes -->
+        <item name="toolbarStyle">@style/Widget.DeviceDefault.Toolbar</item>
     </style>
 
     <!-- DeviceDefault theme for panel windows. This removes all extraneous window
@@ -525,6 +561,9 @@
         <!-- Progress bar attributes -->
         <item name="colorProgressBackgroundNormal">@color/config_progress_background_tint</item>
         <item name="progressBarCornerRadius">@dimen/config_progressBarCornerRadius</item>
+
+        <!-- Toolbar attributes -->
+        <item name="toolbarStyle">@style/Widget.DeviceDefault.Toolbar</item>
     </style>
 
     <!-- DeviceDefault theme for windows that want to have the user's selected wallpaper appear
@@ -549,6 +588,9 @@
         <!-- Progress bar attributes -->
         <item name="colorProgressBackgroundNormal">@color/config_progress_background_tint</item>
         <item name="progressBarCornerRadius">@dimen/config_progressBarCornerRadius</item>
+
+        <!-- Toolbar attributes -->
+        <item name="toolbarStyle">@style/Widget.DeviceDefault.Toolbar</item>
     </style>
 
     <!-- DeviceDefault theme for windows that want to have the user's selected wallpaper appear
@@ -573,6 +615,9 @@
         <!-- Progress bar attributes -->
         <item name="colorProgressBackgroundNormal">@color/config_progress_background_tint</item>
         <item name="progressBarCornerRadius">@dimen/config_progressBarCornerRadius</item>
+
+        <!-- Toolbar attributes -->
+        <item name="toolbarStyle">@style/Widget.DeviceDefault.Toolbar</item>
     </style>
 
     <!-- DeviceDefault style for input methods, which is used by the
@@ -597,6 +642,9 @@
         <!-- Progress bar attributes -->
         <item name="colorProgressBackgroundNormal">@color/config_progress_background_tint</item>
         <item name="progressBarCornerRadius">@dimen/config_progressBarCornerRadius</item>
+
+        <!-- Toolbar attributes -->
+        <item name="toolbarStyle">@style/Widget.DeviceDefault.Toolbar</item>
     </style>
 
     <!-- DeviceDefault style for input methods, which is used by the
@@ -621,6 +669,9 @@
         <!-- Progress bar attributes -->
         <item name="colorProgressBackgroundNormal">@color/config_progress_background_tint</item>
         <item name="progressBarCornerRadius">@dimen/config_progressBarCornerRadius</item>
+
+        <!-- Toolbar attributes -->
+        <item name="toolbarStyle">@style/Widget.DeviceDefault.Toolbar</item>
     </style>
 
     <style name="Theme.DeviceDefault.Dialog.Alert" parent="Theme.Material.Dialog.Alert">
@@ -645,6 +696,9 @@
         <!-- Progress bar attributes -->
         <item name="colorProgressBackgroundNormal">@color/config_progress_background_tint</item>
         <item name="progressBarCornerRadius">@dimen/config_progressBarCornerRadius</item>
+
+        <!-- Toolbar attributes -->
+        <item name="toolbarStyle">@style/Widget.DeviceDefault.Toolbar</item>
     </style>
 
     <style name="Theme.DeviceDefault.SearchBar" parent="Theme.Material.SearchBar">
@@ -667,6 +721,9 @@
         <!-- Progress bar attributes -->
         <item name="colorProgressBackgroundNormal">@color/config_progress_background_tint</item>
         <item name="progressBarCornerRadius">@dimen/config_progressBarCornerRadius</item>
+
+        <!-- Toolbar attributes -->
+        <item name="toolbarStyle">@style/Widget.DeviceDefault.Toolbar</item>
     </style>
 
     <style name="Theme.DeviceDefault.Dialog.NoFrame" parent="Theme.Material.Dialog.NoFrame">
@@ -689,6 +746,9 @@
         <!-- Progress bar attributes -->
         <item name="colorProgressBackgroundNormal">@color/config_progress_background_tint</item>
         <item name="progressBarCornerRadius">@dimen/config_progressBarCornerRadius</item>
+
+        <!-- Toolbar attributes -->
+        <item name="toolbarStyle">@style/Widget.DeviceDefault.Toolbar</item>
     </style>
 
     <!-- Variant of {@link #Theme_DeviceDefault} with a light-colored style -->
@@ -822,6 +882,9 @@
         <item name="actionBarStyle">@style/Widget.DeviceDefault.Light.ActionBar.Solid</item>
         <item name="actionModePopupWindowStyle">@style/Widget.DeviceDefault.Light.PopupWindow.ActionMode</item>
 
+        <!-- Toolbar attributes -->
+        <item name="toolbarStyle">@style/Widget.DeviceDefault.Toolbar</item>
+
         <item name="buttonBarStyle">@style/DeviceDefault.Light.ButtonBar</item>
         <item name="segmentedButtonStyle">@style/DeviceDefault.Light.SegmentedButton</item>
 
@@ -869,6 +932,9 @@
         <!-- Progress bar attributes -->
         <item name="colorProgressBackgroundNormal">@color/config_progress_background_tint</item>
         <item name="progressBarCornerRadius">@dimen/config_progressBarCornerRadius</item>
+
+        <!-- Toolbar attributes -->
+        <item name="toolbarStyle">@style/Widget.DeviceDefault.Toolbar</item>
     </style>
 
     <!-- Variant of {@link #Theme_DeviceDefault_Light} with no action bar -->
@@ -892,6 +958,9 @@
         <!-- Progress bar attributes -->
         <item name="colorProgressBackgroundNormal">@color/config_progress_background_tint</item>
         <item name="progressBarCornerRadius">@dimen/config_progressBarCornerRadius</item>
+
+        <!-- Toolbar attributes -->
+        <item name="toolbarStyle">@style/Widget.DeviceDefault.Toolbar</item>
     </style>
 
     <!-- Variant of {@link #Theme_DeviceDefault_Light} with no action bar and no status bar.
@@ -916,6 +985,9 @@
         <!-- Progress bar attributes -->
         <item name="colorProgressBackgroundNormal">@color/config_progress_background_tint</item>
         <item name="progressBarCornerRadius">@dimen/config_progressBarCornerRadius</item>
+
+        <!-- Toolbar attributes -->
+        <item name="toolbarStyle">@style/Widget.DeviceDefault.Toolbar</item>
     </style>
 
     <!-- Variant of {@link #Theme_DeviceDefault_Light} with no action bar and no status bar
@@ -942,6 +1014,9 @@
         <!-- Progress bar attributes -->
         <item name="colorProgressBackgroundNormal">@color/config_progress_background_tint</item>
         <item name="progressBarCornerRadius">@dimen/config_progressBarCornerRadius</item>
+
+        <!-- Toolbar attributes -->
+        <item name="toolbarStyle">@style/Widget.DeviceDefault.Toolbar</item>
     </style>
 
     <!-- Variant of {@link #Theme_DeviceDefault_Light} that has no title bar and translucent
@@ -967,6 +1042,9 @@
         <!-- Progress bar attributes -->
         <item name="colorProgressBackgroundNormal">@color/config_progress_background_tint</item>
         <item name="progressBarCornerRadius">@dimen/config_progressBarCornerRadius</item>
+
+        <!-- Toolbar attributes -->
+        <item name="toolbarStyle">@style/Widget.DeviceDefault.Toolbar</item>
     </style>
 
     <!-- DeviceDefault light theme for dialog windows and activities. This changes the window to be
@@ -998,11 +1076,17 @@
         <!-- Progress bar attributes -->
         <item name="colorProgressBackgroundNormal">@color/config_progress_background_tint</item>
         <item name="progressBarCornerRadius">@dimen/config_progressBarCornerRadius</item>
+
+        <!-- Toolbar attributes -->
+        <item name="toolbarStyle">@style/Widget.DeviceDefault.Toolbar</item>
     </style>
 
     <!-- Variant of {@link #Theme_DeviceDefault_Light_Dialog} that has a nice minimum width for a
     regular dialog. -->
     <style name="Theme.DeviceDefault.Light.Dialog.MinWidth" parent="Theme.Material.Light.Dialog.MinWidth">
+        <item name="windowTitleStyle">@style/DialogWindowTitle.DeviceDefault.Light</item>
+        <item name="windowAnimationStyle">@style/Animation.DeviceDefault.Dialog</item>
+
         <!-- Color palette -->
         <item name="colorPrimary">@color/primary_device_default_light</item>
         <item name="colorPrimaryDark">@color/primary_dark_device_default_light</item>
@@ -1022,10 +1106,16 @@
         <!-- Progress bar attributes -->
         <item name="colorProgressBackgroundNormal">@color/config_progress_background_tint</item>
         <item name="progressBarCornerRadius">@dimen/config_progressBarCornerRadius</item>
+
+        <!-- Toolbar attributes -->
+        <item name="toolbarStyle">@style/Widget.DeviceDefault.Toolbar</item>
     </style>
 
      <!-- Variant of {@link #Theme_DeviceDefault_Light_Dialog} without an action bar -->
     <style name="Theme.DeviceDefault.Light.Dialog.NoActionBar" parent="Theme.Material.Light.Dialog.NoActionBar">
+        <item name="windowTitleStyle">@style/DialogWindowTitle.DeviceDefault.Light</item>
+        <item name="windowAnimationStyle">@style/Animation.DeviceDefault.Dialog</item>
+
         <!-- Color palette -->
         <item name="colorPrimary">@color/primary_device_default_light</item>
         <item name="colorPrimaryDark">@color/primary_dark_device_default_light</item>
@@ -1045,11 +1135,17 @@
         <!-- Progress bar attributes -->
         <item name="colorProgressBackgroundNormal">@color/config_progress_background_tint</item>
         <item name="progressBarCornerRadius">@dimen/config_progressBarCornerRadius</item>
+
+        <!-- Toolbar attributes -->
+        <item name="toolbarStyle">@style/Widget.DeviceDefault.Toolbar</item>
     </style>
 
     <!-- Variant of {@link #Theme_DeviceDefault_Light_Dialog_NoActionBar} that has a nice minimum
     width for a regular dialog. -->
     <style name="Theme.DeviceDefault.Light.Dialog.NoActionBar.MinWidth" parent="Theme.Material.Light.Dialog.NoActionBar.MinWidth">
+        <item name="windowTitleStyle">@style/DialogWindowTitle.DeviceDefault.Light</item>
+        <item name="windowAnimationStyle">@style/Animation.DeviceDefault.Dialog</item>
+
         <!-- Color palette -->
         <item name="colorPrimary">@color/primary_device_default_light</item>
         <item name="colorPrimaryDark">@color/primary_dark_device_default_light</item>
@@ -1069,6 +1165,9 @@
         <!-- Progress bar attributes -->
         <item name="colorProgressBackgroundNormal">@color/config_progress_background_tint</item>
         <item name="progressBarCornerRadius">@dimen/config_progressBarCornerRadius</item>
+
+        <!-- Toolbar attributes -->
+        <item name="toolbarStyle">@style/Widget.DeviceDefault.Toolbar</item>
     </style>
 
     <!-- Variant of Theme.DeviceDefault.Dialog that has a fixed size. -->
@@ -1100,6 +1199,9 @@
     <!-- DeviceDefault light theme for a window that will be displayed either full-screen on smaller
     screens (small, normal) or as a dialog on larger screens (large, xlarge). -->
     <style name="Theme.DeviceDefault.Light.DialogWhenLarge" parent="Theme.Material.Light.DialogWhenLarge">
+        <item name="windowTitleStyle">@style/DialogWindowTitle.DeviceDefault.Light</item>
+        <item name="windowAnimationStyle">@style/Animation.DeviceDefault.Dialog</item>
+
         <!-- Color palette -->
         <item name="colorPrimary">@color/primary_device_default_light</item>
         <item name="colorPrimaryDark">@color/primary_dark_device_default_light</item>
@@ -1119,12 +1221,18 @@
         <!-- Progress bar attributes -->
         <item name="colorProgressBackgroundNormal">@color/config_progress_background_tint</item>
         <item name="progressBarCornerRadius">@dimen/config_progressBarCornerRadius</item>
+
+        <!-- Toolbar attributes -->
+        <item name="toolbarStyle">@style/Widget.DeviceDefault.Toolbar</item>
     </style>
 
     <!-- DeviceDefault light theme for a window without an action bar that will be displayed either
     full-screen on smaller screens (small, normal) or as a dialog on larger screens (large,
     xlarge). -->
     <style name="Theme.DeviceDefault.Light.DialogWhenLarge.NoActionBar" parent="Theme.Material.Light.DialogWhenLarge.NoActionBar">
+        <item name="windowTitleStyle">@style/DialogWindowTitle.DeviceDefault.Light</item>
+        <item name="windowAnimationStyle">@style/Animation.DeviceDefault.Dialog</item>
+
         <!-- Color palette -->
         <item name="colorPrimary">@color/primary_device_default_light</item>
         <item name="colorPrimaryDark">@color/primary_dark_device_default_light</item>
@@ -1144,10 +1252,16 @@
         <!-- Progress bar attributes -->
         <item name="colorProgressBackgroundNormal">@color/config_progress_background_tint</item>
         <item name="progressBarCornerRadius">@dimen/config_progressBarCornerRadius</item>
+
+        <!-- Toolbar attributes -->
+        <item name="toolbarStyle">@style/Widget.DeviceDefault.Toolbar</item>
     </style>
 
     <!-- DeviceDefault light theme for a presentation window on a secondary display. -->
     <style name="Theme.DeviceDefault.Light.Dialog.Presentation" parent="Theme.Material.Light.Dialog.Presentation">
+        <item name="windowTitleStyle">@style/DialogWindowTitle.DeviceDefault.Light</item>
+        <item name="windowAnimationStyle">@style/Animation.DeviceDefault.Dialog</item>
+
         <!-- Color palette -->
         <item name="colorPrimary">@color/primary_device_default_light</item>
         <item name="colorPrimaryDark">@color/primary_dark_device_default_light</item>
@@ -1167,6 +1281,9 @@
         <!-- Progress bar attributes -->
         <item name="colorProgressBackgroundNormal">@color/config_progress_background_tint</item>
         <item name="progressBarCornerRadius">@dimen/config_progressBarCornerRadius</item>
+
+        <!-- Toolbar attributes -->
+        <item name="toolbarStyle">@style/Widget.DeviceDefault.Toolbar</item>
     </style>
 
     <!-- DeviceDefault light theme for panel windows. This removes all extraneous window
@@ -1192,6 +1309,9 @@
         <!-- Progress bar attributes -->
         <item name="colorProgressBackgroundNormal">@color/config_progress_background_tint</item>
         <item name="progressBarCornerRadius">@dimen/config_progressBarCornerRadius</item>
+
+        <!-- Toolbar attributes -->
+        <item name="toolbarStyle">@style/Widget.DeviceDefault.Toolbar</item>
     </style>
 
     <style name="Theme.DeviceDefault.Light.Dialog.Alert" parent="Theme.Material.Light.Dialog.Alert">
@@ -1216,6 +1336,9 @@
         <!-- Progress bar attributes -->
         <item name="colorProgressBackgroundNormal">@color/config_progress_background_tint</item>
         <item name="progressBarCornerRadius">@dimen/config_progressBarCornerRadius</item>
+
+        <!-- Toolbar attributes -->
+        <item name="toolbarStyle">@style/Widget.DeviceDefault.Toolbar</item>
     </style>
 
     <style name="Theme.DeviceDefault.Light.SearchBar" parent="Theme.Material.Light.SearchBar">
@@ -1238,6 +1361,9 @@
         <!-- Progress bar attributes -->
         <item name="colorProgressBackgroundNormal">@color/config_progress_background_tint</item>
         <item name="progressBarCornerRadius">@dimen/config_progressBarCornerRadius</item>
+
+        <!-- Toolbar attributes -->
+        <item name="toolbarStyle">@style/Widget.DeviceDefault.Toolbar</item>
     </style>
 
     <style name="Theme.DeviceDefault.Light.Voice" parent="Theme.Material.Light.Voice">
@@ -1260,11 +1386,15 @@
         <!-- Progress bar attributes -->
         <item name="colorProgressBackgroundNormal">@color/config_progress_background_tint</item>
         <item name="progressBarCornerRadius">@dimen/config_progressBarCornerRadius</item>
+
+        <!-- Toolbar attributes -->
+        <item name="toolbarStyle">@style/Widget.DeviceDefault.Toolbar</item>
     </style>
 
     <!-- DeviceDefault theme for a window that should look like the Settings app.  -->
     <style name="Theme.DeviceDefault.Settings" parent="Theme.Material.Settings">
         <!-- action bar -->
+        <item name="actionBarStyle">@style/Widget.DeviceDefault.Light.ActionBar.Solid</item>
         <item name="actionBarTheme">@style/ThemeOverlay.DeviceDefault.ActionBar.Accent</item>
         <item name="popupTheme">@style/ThemeOverlay.DeviceDefault.Popup.Light</item>
 
@@ -1295,6 +1425,9 @@
         <!-- Progress bar attributes -->
         <item name="colorProgressBackgroundNormal">@color/config_progress_background_tint</item>
         <item name="progressBarCornerRadius">@dimen/config_progressBarCornerRadius</item>
+
+        <!-- Toolbar attributes -->
+        <item name="toolbarStyle">@style/Widget.DeviceDefault.Toolbar</item>
     </style>
 
     <!-- @hide DeviceDefault theme for a window that should use Settings theme colors
@@ -1319,6 +1452,9 @@
         <item name="colorProgressBackgroundNormal">@color/config_progress_background_tint</item>
         <item name="progressBarCornerRadius">@dimen/config_progressBarCornerRadius</item>
 
+        <!-- Toolbar attributes -->
+        <item name="toolbarStyle">@style/Widget.DeviceDefault.Toolbar</item>
+
         <!-- volume background -->
         <item name="panelColorBackground">@color/primary_material_light</item>
     </style>
@@ -1334,6 +1470,9 @@
         <!-- Progress bar attributes -->
         <item name="colorProgressBackgroundNormal">@color/config_progress_background_tint</item>
         <item name="progressBarCornerRadius">@dimen/config_progressBarCornerRadius</item>
+
+        <!-- Toolbar attributes -->
+        <item name="toolbarStyle">@style/Widget.DeviceDefault.Toolbar</item>
     </style>
 
     <!-- Variant of {@link #Theme_DeviceDefault_Settings_Dark} with no action bar -->
@@ -1358,9 +1497,15 @@
         <!-- Progress bar attributes -->
         <item name="colorProgressBackgroundNormal">@color/config_progress_background_tint</item>
         <item name="progressBarCornerRadius">@dimen/config_progressBarCornerRadius</item>
+
+        <!-- Toolbar attributes -->
+        <item name="toolbarStyle">@style/Widget.DeviceDefault.Toolbar</item>
     </style>
 
     <style name="Theme.DeviceDefault.Settings.Dialog" parent="Theme.Material.Settings.Dialog">
+        <item name="windowTitleStyle">@style/DialogWindowTitle.DeviceDefault</item>
+        <item name="windowAnimationStyle">@style/Animation.DeviceDefault.Dialog</item>
+
         <!-- Color palette -->
         <item name="colorPrimary">@color/primary_device_default_settings</item>
         <item name="colorPrimaryDark">@color/primary_dark_device_default_settings</item>
@@ -1381,9 +1526,15 @@
         <!-- Progress bar attributes -->
         <item name="colorProgressBackgroundNormal">@color/config_progress_background_tint</item>
         <item name="progressBarCornerRadius">@dimen/config_progressBarCornerRadius</item>
+
+        <!-- Toolbar attributes -->
+        <item name="toolbarStyle">@style/Widget.DeviceDefault.Toolbar</item>
     </style>
 
     <style name="Theme.DeviceDefault.Settings.DialogWhenLarge" parent="Theme.Material.Settings.DialogWhenLarge">
+        <item name="windowTitleStyle">@style/DialogWindowTitle.DeviceDefault</item>
+        <item name="windowAnimationStyle">@style/Animation.DeviceDefault.Dialog</item>
+
         <!-- Color palette -->
         <item name="colorPrimary">@color/primary_device_default_settings</item>
         <item name="colorPrimaryDark">@color/primary_dark_device_default_settings</item>
@@ -1404,9 +1555,15 @@
         <!-- Progress bar attributes -->
         <item name="colorProgressBackgroundNormal">@color/config_progress_background_tint</item>
         <item name="progressBarCornerRadius">@dimen/config_progressBarCornerRadius</item>
+
+        <!-- Toolbar attributes -->
+        <item name="toolbarStyle">@style/Widget.DeviceDefault.Toolbar</item>
     </style>
 
     <style name="Theme.DeviceDefault.Settings.Dialog.Alert" parent="Theme.Material.Settings.Dialog.Alert">
+        <item name="windowTitleStyle">@style/DialogWindowTitle.DeviceDefault</item>
+        <item name="windowAnimationStyle">@style/Animation.DeviceDefault.Dialog</item>
+
         <!-- Color palette -->
         <item name="colorPrimary">@color/primary_device_default_settings</item>
         <item name="colorPrimaryDark">@color/primary_dark_device_default_settings</item>
@@ -1427,6 +1584,9 @@
         <!-- Progress bar attributes -->
         <item name="colorProgressBackgroundNormal">@color/config_progress_background_tint</item>
         <item name="progressBarCornerRadius">@dimen/config_progressBarCornerRadius</item>
+
+        <!-- Toolbar attributes -->
+        <item name="toolbarStyle">@style/Widget.DeviceDefault.Toolbar</item>
     </style>
 
     <style name="Theme.DeviceDefault.Settings.Dialog.NoActionBar" parent="Theme.DeviceDefault.Light.Dialog.NoActionBar" />
@@ -1461,6 +1621,9 @@
         <!-- Progress bar attributes -->
         <item name="colorProgressBackgroundNormal">@color/config_progress_background_tint</item>
         <item name="progressBarCornerRadius">@dimen/config_progressBarCornerRadius</item>
+
+        <!-- Toolbar attributes -->
+        <item name="toolbarStyle">@style/Widget.DeviceDefault.Toolbar</item>
     </style>
 
     <!-- DeviceDefault theme for the default system theme.  -->
diff --git a/core/tests/coretests/src/android/provider/SettingsBackupTest.java b/core/tests/coretests/src/android/provider/SettingsBackupTest.java
index 7c6d7b1..613ab6d 100644
--- a/core/tests/coretests/src/android/provider/SettingsBackupTest.java
+++ b/core/tests/coretests/src/android/provider/SettingsBackupTest.java
@@ -303,6 +303,12 @@
                     Settings.Global.NETSTATS_UID_TAG_DELETE_AGE,
                     Settings.Global.NETSTATS_UID_TAG_PERSIST_BYTES,
                     Settings.Global.NETSTATS_UID_TAG_ROTATE_AGE,
+                    Settings.Global.NETPOLICY_QUOTA_ENABLED,
+                    Settings.Global.NETPOLICY_QUOTA_UNLIMITED,
+                    Settings.Global.NETPOLICY_QUOTA_LIMITED,
+                    Settings.Global.NETPOLICY_QUOTA_FRAC_JOBS,
+                    Settings.Global.NETPOLICY_QUOTA_FRAC_MULTIPATH,
+                    Settings.Global.NETPOLICY_OVERRIDE_ENABLED,
                     Settings.Global.NETWORK_AVOID_BAD_WIFI,
                     Settings.Global.NETWORK_DEFAULT_DAILY_MULTIPATH_QUOTA_BYTES,
                     Settings.Global.NETWORK_METERED_MULTIPATH_PREFERENCE,
@@ -413,6 +419,7 @@
                     Settings.Global.UNINSTALLED_INSTANT_APP_MIN_CACHE_PERIOD,
                     Settings.Global.UNINSTALLED_INSTANT_APP_MAX_CACHE_PERIOD,
                     Settings.Global.UNUSED_STATIC_SHARED_LIB_MIN_CACHE_PERIOD,
+                    Settings.Global.UNGAZE_SLEEP_ENABLED,
                     Settings.Global.UNLOCK_SOUND,
                     Settings.Global.USE_GOOGLE_MAIL,
                     Settings.Global.VT_IMS_ENABLED,
diff --git a/core/tests/coretests/src/android/text/SpannableTest.java b/core/tests/coretests/src/android/text/SpannableTest.java
index 307ac62..f368428 100644
--- a/core/tests/coretests/src/android/text/SpannableTest.java
+++ b/core/tests/coretests/src/android/text/SpannableTest.java
@@ -16,6 +16,8 @@
 
 package android.text;
 
+import static org.junit.Assert.assertEquals;
+
 import android.platform.test.annotations.Presubmit;
 import android.support.test.filters.SmallTest;
 import android.support.test.runner.AndroidJUnit4;
@@ -24,6 +26,8 @@
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
+import java.util.concurrent.CountDownLatch;
+
 @Presubmit
 @SmallTest
 @RunWith(AndroidJUnit4.class)
@@ -53,4 +57,34 @@
         spans = spannable.getSpans(2, 2, Object.class);
         MoreAsserts.assertEquals(new Object[]{unemptySpan}, spans);
     }
+
+    @Test
+    public void testRemoveSpanWithIntermediateFlag() {
+        Spannable spannable = newSpannableWithText("abcdef");
+        Object emptySpan = new Object();
+        spannable.setSpan(emptySpan, 1, 1, 0);
+        Object unemptySpan = new Object();
+        spannable.setSpan(unemptySpan, 1, 2, 0);
+
+        final CountDownLatch latch = new CountDownLatch(1);
+        SpanWatcher watcher = new SpanWatcher() {
+            @Override
+            public void onSpanAdded(Spannable text, Object what, int start, int end) {}
+
+            @Override
+            public void onSpanRemoved(Spannable text, Object what, int start, int end) {
+                latch.countDown();
+            }
+
+            @Override
+            public void onSpanChanged(Spannable text, Object what, int ostart, int oend, int nstart,
+                    int nend) {}
+        };
+        spannable.setSpan(watcher, 0, 2, 0);
+
+        spannable.removeSpan(emptySpan, Spanned.SPAN_INTERMEDIATE);
+        assertEquals(1, latch.getCount());
+        spannable.removeSpan(unemptySpan);
+        assertEquals(0, latch.getCount());
+    }
 }
diff --git a/core/tests/coretests/src/android/view/textclassifier/SelectionEventTest.java b/core/tests/coretests/src/android/view/textclassifier/SelectionEventTest.java
index 861a43a..a3c6179 100644
--- a/core/tests/coretests/src/android/view/textclassifier/SelectionEventTest.java
+++ b/core/tests/coretests/src/android/view/textclassifier/SelectionEventTest.java
@@ -19,7 +19,6 @@
 import static org.junit.Assert.assertEquals;
 
 import android.os.Parcel;
-import android.support.test.InstrumentationRegistry;
 import android.support.test.filters.SmallTest;
 import android.support.test.runner.AndroidJUnit4;
 
@@ -31,20 +30,34 @@
 public class SelectionEventTest {
 
     @Test
+    public void testCreateSelectionActionEvent_valid() {
+        SelectionEvent.createSelectionActionEvent(0, 1, SelectionEvent.ACTION_OVERTYPE);
+        SelectionEvent.createSelectionActionEvent(0, 1, SelectionEvent.ACTION_COPY);
+        SelectionEvent.createSelectionActionEvent(0, 1, SelectionEvent.ACTION_PASTE);
+        SelectionEvent.createSelectionActionEvent(0, 1, SelectionEvent.ACTION_CUT);
+        SelectionEvent.createSelectionActionEvent(0, 1, SelectionEvent.ACTION_SHARE);
+        SelectionEvent.createSelectionActionEvent(0, 1, SelectionEvent.ACTION_SMART_SHARE);
+        SelectionEvent.createSelectionActionEvent(0, 1, SelectionEvent.ACTION_DRAG);
+        SelectionEvent.createSelectionActionEvent(0, 1, SelectionEvent.ACTION_ABANDON);
+        SelectionEvent.createSelectionActionEvent(0, 1, SelectionEvent.ACTION_OTHER);
+        SelectionEvent.createSelectionActionEvent(0, 1, SelectionEvent.ACTION_SELECT_ALL);
+        SelectionEvent.createSelectionActionEvent(0, 1, SelectionEvent.ACTION_RESET);
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void testCreateSelectionActionEvent_badRange() {
+        SelectionEvent.createSelectionActionEvent(0, -1, SelectionEvent.ACTION_OVERTYPE);
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void testCreateSelectionActionEvent_badAction() {
+        SelectionEvent.createSelectionActionEvent(0, 1, SelectionEvent.EVENT_SELECTION_STARTED);
+    }
+
+    @Test
     public void testParcel() {
-        final SelectionEvent[] captured = new SelectionEvent[1];
-        final Logger logger = new Logger(
-                new Logger.Config(
-                        InstrumentationRegistry.getTargetContext(),
-                        TextClassifier.WIDGET_TYPE_TEXTVIEW,
-                        null)) {
-            @Override
-            public void writeEvent(SelectionEvent event) {
-                captured[0] = event;
-            }
-        };
-        logger.logSelectionStartedEvent(SelectionEvent.INVOCATION_MANUAL, 0);
-        final SelectionEvent event = captured[0];
+        final SelectionEvent event = SelectionEvent.createSelectionStartedEvent(
+                SelectionEvent.INVOCATION_MANUAL, 0);
         final Parcel parcel = Parcel.obtain();
         event.writeToParcel(parcel, event.describeContents());
         parcel.setDataPosition(0);
diff --git a/graphics/java/android/graphics/Typeface.java b/graphics/java/android/graphics/Typeface.java
index 20c22b7..18dd97f 100644
--- a/graphics/java/android/graphics/Typeface.java
+++ b/graphics/java/android/graphics/Typeface.java
@@ -738,6 +738,23 @@
     /**
      * Creates a typeface object that best matches the specified existing typeface and the specified
      * weight and italic style
+     * <p>Below are numerical values and corresponding common weight names.</p>
+     * <table>
+     * <thead>
+     * <tr><th>Value</th><th>Common weight name</th></tr>
+     * </thead>
+     * <tbody>
+     * <tr><td>100</td><td>Thin</td></tr>
+     * <tr><td>200</td><td>Extra Light</td></tr>
+     * <tr><td>300</td><td>Light</td></tr>
+     * <tr><td>400</td><td>Normal</td></tr>
+     * <tr><td>500</td><td>Medium</td></tr>
+     * <tr><td>600</td><td>Semi Bold</td></tr>
+     * <tr><td>700</td><td>Bold</td></tr>
+     * <tr><td>800</td><td>Extra Bold</td></tr>
+     * <tr><td>900</td><td>Black</td></tr>
+     * </tbody>
+     * </table>
      *
      * <p>
      * This method is thread safe.
@@ -749,6 +766,9 @@
      * @param italic {@code true} if italic style is desired to be drawn. Otherwise, {@code false}
      * @return A {@link Typeface} object for drawing specified weight and italic style. Never
      *         returns {@code null}
+     *
+     * @see #getWeight()
+     * @see #isItalic()
      */
     public static @NonNull Typeface create(@Nullable Typeface family,
             @IntRange(from = 1, to = 1000) int weight, boolean italic) {
diff --git a/libs/hwui/ProfileData.cpp b/libs/hwui/ProfileData.cpp
index f9cf549..1696661 100644
--- a/libs/hwui/ProfileData.cpp
+++ b/libs/hwui/ProfileData.cpp
@@ -104,7 +104,8 @@
     dprintf(fd, "\nStats since: %" PRIu64 "ns", mStatStartTime);
     dprintf(fd, "\nTotal frames rendered: %u", mTotalFrameCount);
     dprintf(fd, "\nJanky frames: %u (%.2f%%)", mJankFrameCount,
-            (float)mJankFrameCount / (float)mTotalFrameCount * 100.0f);
+            mTotalFrameCount == 0 ? 0.0f :
+                (float)mJankFrameCount / (float)mTotalFrameCount * 100.0f);
     dprintf(fd, "\n50th percentile: %ums", findPercentile(50));
     dprintf(fd, "\n90th percentile: %ums", findPercentile(90));
     dprintf(fd, "\n95th percentile: %ums", findPercentile(95));
diff --git a/location/java/android/location/LocationManager.java b/location/java/android/location/LocationManager.java
index 38286a3..6eb3d8d 100644
--- a/location/java/android/location/LocationManager.java
+++ b/location/java/android/location/LocationManager.java
@@ -29,6 +29,7 @@
 import android.annotation.SuppressLint;
 import android.annotation.SystemApi;
 import android.annotation.SystemService;
+import android.annotation.TestApi;
 import android.app.PendingIntent;
 import android.content.Context;
 import android.content.Intent;
@@ -392,6 +393,18 @@
     }
 
     /**
+     * @hide
+     */
+    @TestApi
+    public String[] getBackgroundThrottlingWhitelist() {
+        try {
+            return mService.getBackgroundThrottlingWhitelist();
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
      * @hide - hide this constructor because it has a parameter
      * of type ILocationManager, which is a system private class. The
      * right way to create an instance of this class is using the
diff --git a/media/java/android/media/AudioFocusRequest.java b/media/java/android/media/AudioFocusRequest.java
index 7104dad..fe89b89 100644
--- a/media/java/android/media/AudioFocusRequest.java
+++ b/media/java/android/media/AudioFocusRequest.java
@@ -19,6 +19,7 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.SystemApi;
+import android.annotation.TestApi;
 import android.media.AudioManager.OnAudioFocusChangeListener;
 import android.os.Bundle;
 import android.os.Handler;
@@ -262,6 +263,7 @@
      * Returns the focus change listener set for this {@code AudioFocusRequest}.
      * @return null if no {@link AudioManager.OnAudioFocusChangeListener} was set.
      */
+    @TestApi
     public @Nullable OnAudioFocusChangeListener getOnAudioFocusChangeListener() {
         return mFocusListener;
     }
diff --git a/media/java/android/media/AudioPresentation.java b/media/java/android/media/AudioPresentation.java
index e39cb7d..ce71436 100644
--- a/media/java/android/media/AudioPresentation.java
+++ b/media/java/android/media/AudioPresentation.java
@@ -18,8 +18,7 @@
 
 import android.annotation.IntDef;
 import android.annotation.NonNull;
-
-import com.android.internal.annotations.VisibleForTesting;
+import android.annotation.TestApi;
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
@@ -94,7 +93,7 @@
     /**
      * @hide
      */
-    @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
+    @TestApi
     public AudioPresentation(int presentationId,
                         int programId,
                         @NonNull Map<String, String> labels,
@@ -119,7 +118,7 @@
      * decoder. Presentation id is typically sequential, but does not have to be.
      * @hide
      */
-    @VisibleForTesting
+    @TestApi
     public int getPresentationId() {
         return mPresentationId;
     }
@@ -129,7 +128,7 @@
      * Program id can be used to further uniquely identify the presentation to a decoder.
      * @hide
      */
-    @VisibleForTesting
+    @TestApi
     public int getProgramId() {
         return mProgramId;
     }
diff --git a/media/java/android/media/PlaybackParams.java b/media/java/android/media/PlaybackParams.java
index 938a953..b85e4d0 100644
--- a/media/java/android/media/PlaybackParams.java
+++ b/media/java/android/media/PlaybackParams.java
@@ -17,6 +17,7 @@
 package android.media;
 
 import android.annotation.IntDef;
+import android.annotation.TestApi;
 import android.os.Parcel;
 import android.os.Parcelable;
 
@@ -151,6 +152,7 @@
      * @param audioStretchMode
      * @return this <code>PlaybackParams</code> instance.
      */
+    @TestApi
     public PlaybackParams setAudioStretchMode(@AudioStretchMode int audioStretchMode) {
         mAudioStretchMode = audioStretchMode;
         mSet |= SET_AUDIO_STRETCH_MODE;
@@ -163,6 +165,7 @@
      * @return audio stretch mode
      * @throws IllegalStateException if the audio stretch mode is not set.
      */
+    @TestApi
     public @AudioStretchMode int getAudioStretchMode() {
         if ((mSet & SET_AUDIO_STRETCH_MODE) == 0) {
             throw new IllegalStateException("audio stretch mode not set");
diff --git a/media/jni/audioeffect/android_media_AudioEffect.cpp b/media/jni/audioeffect/android_media_AudioEffect.cpp
index 48ddf9a..51b080a3 100644
--- a/media/jni/audioeffect/android_media_AudioEffect.cpp
+++ b/media/jni/audioeffect/android_media_AudioEffect.cpp
@@ -110,16 +110,16 @@
     effect_callback_cookie *callbackInfo = (effect_callback_cookie *)user;
     JNIEnv *env = AndroidRuntime::getJNIEnv();
 
-    ALOGV("effectCallback: callbackInfo %p, audioEffect_ref %p audioEffect_class %p",
-            callbackInfo,
-            callbackInfo->audioEffect_ref,
-            callbackInfo->audioEffect_class);
-
     if (!user || !env) {
         ALOGW("effectCallback error user %p, env %p", user, env);
         return;
     }
 
+    ALOGV("effectCallback: callbackInfo %p, audioEffect_ref %p audioEffect_class %p",
+            callbackInfo,
+            callbackInfo->audioEffect_ref,
+            callbackInfo->audioEffect_class);
+
     switch (event) {
     case AudioEffect::EVENT_CONTROL_STATUS_CHANGED:
         if (info == 0) {
diff --git a/packages/SettingsLib/res/layout/preference_category_material_settings.xml b/packages/SettingsLib/res/layout/preference_category_material_settings.xml
index 245e3b7..1086106 100644
--- a/packages/SettingsLib/res/layout/preference_category_material_settings.xml
+++ b/packages/SettingsLib/res/layout/preference_category_material_settings.xml
@@ -50,6 +50,7 @@
             android:layout_marginTop="16dp"
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
+            android:fontFamily="@*android:string/config_headlineFontFamilyMedium"
             android:textAppearance="@android:style/TextAppearance.Material.Body2"
             android:textAlignment="viewStart"
             android:textColor="?android:attr/colorAccent"
diff --git a/packages/SettingsLib/res/values/dimens.xml b/packages/SettingsLib/res/values/dimens.xml
index 7b09ef7..cf4261c 100644
--- a/packages/SettingsLib/res/values/dimens.xml
+++ b/packages/SettingsLib/res/values/dimens.xml
@@ -33,6 +33,8 @@
     <dimen name="user_spinner_item_height">56dp</dimen>
 
     <dimen name="two_target_pref_small_icon_size">24dp</dimen>
+    <dimen name="two_target_pref_medium_icon_size">32dp</dimen>
+
     <!-- Lock icon for preferences locked by admin -->
     <dimen name="restricted_icon_size">16dp</dimen>
     <dimen name="restricted_icon_padding">4dp</dimen>
diff --git a/packages/SettingsLib/src/com/android/settingslib/TwoTargetPreference.java b/packages/SettingsLib/src/com/android/settingslib/TwoTargetPreference.java
index 8b39f60..02b68d8 100644
--- a/packages/SettingsLib/src/com/android/settingslib/TwoTargetPreference.java
+++ b/packages/SettingsLib/src/com/android/settingslib/TwoTargetPreference.java
@@ -16,6 +16,7 @@
 
 package com.android.settingslib;
 
+import android.annotation.IntDef;
 import android.content.Context;
 import android.support.v7.preference.Preference;
 import android.support.v7.preference.PreferenceViewHolder;
@@ -24,10 +25,24 @@
 import android.widget.ImageView;
 import android.widget.LinearLayout;
 
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
 public class TwoTargetPreference extends Preference {
 
-    private boolean mUseSmallIcon;
+    @IntDef({ICON_SIZE_DEFAULT, ICON_SIZE_MEDIUM, ICON_SIZE_SMALL})
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface IconSize {
+    }
+
+    public static final int ICON_SIZE_DEFAULT = 0;
+    public static final int ICON_SIZE_MEDIUM = 1;
+    public static final int ICON_SIZE_SMALL = 2;
+
+    @IconSize
+    private int mIconSize;
     private int mSmallIconSize;
+    private int mMediumIconSize;
 
     public TwoTargetPreference(Context context, AttributeSet attrs,
             int defStyleAttr, int defStyleRes) {
@@ -54,22 +69,30 @@
         setLayoutResource(R.layout.preference_two_target);
         mSmallIconSize = context.getResources().getDimensionPixelSize(
                 R.dimen.two_target_pref_small_icon_size);
+        mMediumIconSize = context.getResources().getDimensionPixelSize(
+                R.dimen.two_target_pref_medium_icon_size);
         final int secondTargetResId = getSecondTargetResId();
         if (secondTargetResId != 0) {
             setWidgetLayoutResource(secondTargetResId);
         }
     }
 
-    public void setUseSmallIcon(boolean useSmallIcon) {
-        mUseSmallIcon = useSmallIcon;
+    public void setIconSize(@IconSize int iconSize) {
+        mIconSize = iconSize;
     }
 
     @Override
     public void onBindViewHolder(PreferenceViewHolder holder) {
         super.onBindViewHolder(holder);
-        if (mUseSmallIcon) {
-            ImageView icon = holder.itemView.findViewById(android.R.id.icon);
-            icon.setLayoutParams(new LinearLayout.LayoutParams(mSmallIconSize, mSmallIconSize));
+        final ImageView icon = holder.itemView.findViewById(android.R.id.icon);
+        switch (mIconSize) {
+            case ICON_SIZE_SMALL:
+                icon.setLayoutParams(new LinearLayout.LayoutParams(mSmallIconSize, mSmallIconSize));
+                break;
+            case ICON_SIZE_MEDIUM:
+                icon.setLayoutParams(
+                        new LinearLayout.LayoutParams(mMediumIconSize, mMediumIconSize));
+                break;
         }
         final View divider = holder.findViewById(R.id.two_target_divider);
         final View widgetFrame = holder.findViewById(android.R.id.widget_frame);
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HidDeviceProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HidDeviceProfile.java
new file mode 100644
index 0000000..941964a
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HidDeviceProfile.java
@@ -0,0 +1,200 @@
+/*
+ * 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.bluetooth;
+
+import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothClass;
+import android.bluetooth.BluetoothDevice;
+import android.bluetooth.BluetoothHidDevice;
+import android.bluetooth.BluetoothProfile;
+import android.content.Context;
+import android.util.Log;
+
+import com.android.settingslib.R;
+
+import java.util.Collection;
+import java.util.List;
+
+/**
+ * HidProfile handles Bluetooth HID profile.
+ */
+public class HidDeviceProfile implements LocalBluetoothProfile {
+    private static final String TAG = "HidDeviceProfile";
+    // Order of this profile in device profiles list
+    private static final int ORDINAL = 18;
+    // HID Device Profile is always preferred.
+    private static final int PREFERRED_VALUE = -1;
+    private static final boolean DEBUG = true;
+
+    private final LocalBluetoothAdapter mLocalAdapter;
+    private final CachedBluetoothDeviceManager mDeviceManager;
+    private final LocalBluetoothProfileManager mProfileManager;
+    static final String NAME = "HID DEVICE";
+
+    private BluetoothHidDevice mService;
+    private boolean mIsProfileReady;
+
+    HidDeviceProfile(Context context, LocalBluetoothAdapter adapter,
+            CachedBluetoothDeviceManager deviceManager,
+            LocalBluetoothProfileManager profileManager) {
+        mLocalAdapter = adapter;
+        mDeviceManager = deviceManager;
+        mProfileManager = profileManager;
+        adapter.getProfileProxy(context, new HidDeviceServiceListener(),
+                BluetoothProfile.HID_DEVICE);
+    }
+
+    // These callbacks run on the main thread.
+    private final class HidDeviceServiceListener
+            implements BluetoothProfile.ServiceListener {
+
+        public void onServiceConnected(int profile, BluetoothProfile proxy) {
+            if (DEBUG) {
+                Log.d(TAG,"Bluetooth service connected :-)");
+            }
+            mService = (BluetoothHidDevice) proxy;
+            // We just bound to the service, so refresh the UI for any connected HID devices.
+            List<BluetoothDevice> deviceList = mService.getConnectedDevices();
+            for (BluetoothDevice nextDevice : deviceList) {
+                CachedBluetoothDevice device = mDeviceManager.findDevice(nextDevice);
+                // we may add a new device here, but generally this should not happen
+                if (device == null) {
+                    Log.w(TAG, "HidProfile found new device: " + nextDevice);
+                    device = mDeviceManager.addDevice(mLocalAdapter, mProfileManager, nextDevice);
+                }
+                Log.d(TAG, "Connection status changed: " + device);
+                device.onProfileStateChanged(HidDeviceProfile.this,
+                        BluetoothProfile.STATE_CONNECTED);
+                device.refresh();
+            }
+            mIsProfileReady = true;
+        }
+
+        public void onServiceDisconnected(int profile) {
+            if (DEBUG) {
+                Log.d(TAG, "Bluetooth service disconnected");
+            }
+            mIsProfileReady = false;
+        }
+    }
+
+    @Override
+    public boolean isProfileReady() {
+        return mIsProfileReady;
+    }
+
+    @Override
+    public boolean isConnectable() {
+        return true;
+    }
+
+    @Override
+    public boolean isAutoConnectable() {
+        return false;
+    }
+
+    @Override
+    public boolean connect(BluetoothDevice device) {
+        return false;
+    }
+
+    @Override
+    public boolean disconnect(BluetoothDevice device) {
+        if (mService == null) {
+            return false;
+        }
+        return mService.disconnect(device);
+    }
+
+    @Override
+    public int getConnectionStatus(BluetoothDevice device) {
+        if (mService == null) {
+            return BluetoothProfile.STATE_DISCONNECTED;
+        }
+        List<BluetoothDevice> deviceList = mService.getConnectedDevices();
+
+        return !deviceList.isEmpty() && deviceList.contains(device)
+                ? mService.getConnectionState(device)
+                : BluetoothProfile.STATE_DISCONNECTED;
+    }
+
+    @Override
+    public boolean isPreferred(BluetoothDevice device) {
+        return getConnectionStatus(device) != BluetoothProfile.STATE_DISCONNECTED;
+    }
+
+    @Override
+    public int getPreferred(BluetoothDevice device) {
+        return PREFERRED_VALUE;
+    }
+
+    @Override
+    public void setPreferred(BluetoothDevice device, boolean preferred) {
+        // if set preferred to false, then disconnect to the current device
+        if (!preferred) {
+            mService.disconnect(device);
+        }
+    }
+
+    @Override
+    public String toString() {
+        return NAME;
+    }
+
+    @Override
+    public int getOrdinal() {
+        return ORDINAL;
+    }
+
+    @Override
+    public int getNameResource(BluetoothDevice device) {
+        return R.string.bluetooth_profile_hid;
+    }
+
+    @Override
+    public int getSummaryResourceForDevice(BluetoothDevice device) {
+        final int state = getConnectionStatus(device);
+        switch (state) {
+            case BluetoothProfile.STATE_DISCONNECTED:
+                return R.string.bluetooth_hid_profile_summary_use_for;
+            case BluetoothProfile.STATE_CONNECTED:
+                return R.string.bluetooth_hid_profile_summary_connected;
+            default:
+                return Utils.getConnectionStateSummary(state);
+        }
+    }
+
+    @Override
+    public int getDrawableResource(BluetoothClass btClass) {
+        return R.drawable.ic_bt_misc_hid;
+    }
+
+    protected void finalize() {
+        if (DEBUG) {
+            Log.d(TAG, "finalize()");
+        }
+        if (mService != null) {
+            try {
+                BluetoothAdapter.getDefaultAdapter().closeProfileProxy(BluetoothProfile.HID_DEVICE,
+                        mService);
+                mService = null;
+            } catch (Throwable t) {
+                Log.w(TAG, "Error cleaning up HID proxy", t);
+            }
+        }
+    }
+}
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HidProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HidProfile.java
index 213002f..93c4017 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HidProfile.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HidProfile.java
@@ -48,7 +48,7 @@
     private static final int ORDINAL = 3;
 
     // These callbacks run on the main thread.
-    private final class InputDeviceServiceListener
+    private final class HidHostServiceListener
             implements BluetoothProfile.ServiceListener {
 
         public void onServiceConnected(int profile, BluetoothProfile proxy) {
@@ -86,7 +86,7 @@
         mLocalAdapter = adapter;
         mDeviceManager = deviceManager;
         mProfileManager = profileManager;
-        adapter.getProfileProxy(context, new InputDeviceServiceListener(),
+        adapter.getProfileProxy(context, new HidHostServiceListener(),
                 BluetoothProfile.HID_HOST);
     }
 
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManager.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManager.java
index 00ee575..6413aab 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManager.java
@@ -22,6 +22,7 @@
 import android.bluetooth.BluetoothHeadset;
 import android.bluetooth.BluetoothHeadsetClient;
 import android.bluetooth.BluetoothHearingAid;
+import android.bluetooth.BluetoothHidDevice;
 import android.bluetooth.BluetoothHidHost;
 import android.bluetooth.BluetoothMap;
 import android.bluetooth.BluetoothMapClient;
@@ -86,6 +87,7 @@
     private MapProfile mMapProfile;
     private MapClientProfile mMapClientProfile;
     private final HidProfile mHidProfile;
+    private HidDeviceProfile mHidDeviceProfile;
     private OppProfile mOppProfile;
     private final PanProfile mPanProfile;
     private PbapClientProfile mPbapClientProfile;
@@ -123,7 +125,7 @@
             updateLocalProfiles(uuids);
         }
 
-        // Always add HID and PAN profiles
+        // Always add HID host, HID device, and PAN profiles
         mHidProfile = new HidProfile(context, mLocalAdapter, mDeviceManager, this);
         addProfile(mHidProfile, HidProfile.NAME,
                 BluetoothHidHost.ACTION_CONNECTION_STATE_CHANGED);
@@ -132,6 +134,10 @@
         addPanProfile(mPanProfile, PanProfile.NAME,
                 BluetoothPan.ACTION_CONNECTION_STATE_CHANGED);
 
+        mHidDeviceProfile = new HidDeviceProfile(context, mLocalAdapter, mDeviceManager, this);
+        addProfile(mHidDeviceProfile, HidDeviceProfile.NAME,
+                BluetoothHidDevice.ACTION_CONNECTION_STATE_CHANGED);
+
         if(DEBUG) Log.d(TAG, "Adding local MAP profile");
         if (mUseMapClient) {
             mMapClientProfile = new MapClientProfile(mContext, mLocalAdapter, mDeviceManager, this);
@@ -547,6 +553,12 @@
             removedProfiles.remove(mHidProfile);
         }
 
+        if (mHidProfile != null && mHidDeviceProfile.getConnectionStatus(device)
+                != BluetoothProfile.STATE_DISCONNECTED) {
+            profiles.add(mHidDeviceProfile);
+            removedProfiles.remove(mHidDeviceProfile);
+        }
+
         if(isPanNapConnected)
             if(DEBUG) Log.d(TAG, "Valid PAN-NAP connection exists.");
         if ((BluetoothUuid.isUuidPresent(uuids, BluetoothUuid.NAP) &&
diff --git a/packages/SettingsLib/src/com/android/settingslib/fuelgauge/BatterySaverUtils.java b/packages/SettingsLib/src/com/android/settingslib/fuelgauge/BatterySaverUtils.java
index 835ff07..f7b16f8 100644
--- a/packages/SettingsLib/src/com/android/settingslib/fuelgauge/BatterySaverUtils.java
+++ b/packages/SettingsLib/src/com/android/settingslib/fuelgauge/BatterySaverUtils.java
@@ -148,15 +148,32 @@
         Secure.putInt(context.getContentResolver(), Secure.LOW_POWER_WARNING_ACKNOWLEDGED, 1);
     }
 
+    /**
+     * Don't show the automatic battery suggestion notification in the future.
+     */
     public static void suppressAutoBatterySaver(Context context) {
         Secure.putInt(context.getContentResolver(),
                 Secure.SUPPRESS_AUTO_BATTERY_SAVER_SUGGESTION, 1);
     }
 
-    public static void scheduleAutoBatterySaver(Context context, int level) {
+    /**
+     * Set the automatic battery saver trigger level to {@code level}.
+     */
+    public static void setAutoBatterySaverTriggerLevel(Context context, int level) {
+        if (level > 0) {
+            suppressAutoBatterySaver(context);
+        }
+        Global.putInt(context.getContentResolver(), Global.LOW_POWER_MODE_TRIGGER_LEVEL, level);
+    }
+
+    /**
+     * Set the automatic battery saver trigger level to {@code level}, but only when
+     * automatic battery saver isn't enabled yet.
+     */
+    public static void ensureAutoBatterySaver(Context context, int level) {
         if (Global.getInt(context.getContentResolver(), Global.LOW_POWER_MODE_TRIGGER_LEVEL, 0)
                 == 0) {
-            Global.putInt(context.getContentResolver(), Global.LOW_POWER_MODE_TRIGGER_LEVEL, level);
+            setAutoBatterySaverTriggerLevel(context, level);
         }
     }
 }
diff --git a/packages/SettingsLib/src/com/android/settingslib/inputmethod/InputMethodAndSubtypeUtil.java b/packages/SettingsLib/src/com/android/settingslib/inputmethod/InputMethodAndSubtypeUtil.java
index 55be137..e63bdd6 100644
--- a/packages/SettingsLib/src/com/android/settingslib/inputmethod/InputMethodAndSubtypeUtil.java
+++ b/packages/SettingsLib/src/com/android/settingslib/inputmethod/InputMethodAndSubtypeUtil.java
@@ -35,7 +35,6 @@
 import android.view.inputmethod.InputMethodSubtype;
 
 import com.android.internal.app.LocaleHelper;
-import com.android.internal.inputmethod.InputMethodUtils;
 
 import java.util.HashMap;
 import java.util.HashSet;
@@ -176,7 +175,7 @@
                     ((TwoStatePreference) pref).isChecked()
                     : enabledIMEsAndSubtypesMap.containsKey(imiId);
             final boolean isCurrentInputMethod = imiId.equals(currentInputMethodId);
-            final boolean systemIme = InputMethodUtils.isSystemIme(imi);
+            final boolean systemIme = imi.isSystem();
             if ((!hasHardKeyboard && InputMethodSettingValuesWrapper.getInstance(
                     context.getActivity()).isAlwaysCheckedIme(imi, context.getActivity()))
                     || isImeChecked) {
diff --git a/packages/SettingsLib/src/com/android/settingslib/inputmethod/InputMethodPreference.java b/packages/SettingsLib/src/com/android/settingslib/inputmethod/InputMethodPreference.java
index 5e25f51..649a864 100644
--- a/packages/SettingsLib/src/com/android/settingslib/inputmethod/InputMethodPreference.java
+++ b/packages/SettingsLib/src/com/android/settingslib/inputmethod/InputMethodPreference.java
@@ -35,7 +35,6 @@
 import android.widget.Toast;
 
 import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.inputmethod.InputMethodUtils;
 import com.android.settingslib.R;
 import com.android.settingslib.RestrictedLockUtils;
 import com.android.settingslib.RestrictedSwitchPreference;
@@ -124,7 +123,7 @@
             setIntent(intent);
         }
         mInputMethodSettingValues = InputMethodSettingValuesWrapper.getInstance(context);
-        mHasPriorityInSorting = InputMethodUtils.isSystemIme(imi)
+        mHasPriorityInSorting = imi.isSystem()
                 && mInputMethodSettingValues.isValidSystemNonAuxAsciiCapableIme(imi, context);
         setOnPreferenceClickListener(this);
         setOnPreferenceChangeListener(this);
@@ -153,7 +152,7 @@
             setCheckedInternal(false);
             return false;
         }
-        if (InputMethodUtils.isSystemIme(mImi)) {
+        if (mImi.isSystem()) {
             // Enable a system IME. No need to show a security warning dialog,
             // but we might need to prompt if it's not Direct Boot aware.
             // TV doesn't doesn't need to worry about this, but other platforms should show
diff --git a/packages/SettingsLib/src/com/android/settingslib/inputmethod/InputMethodSettingValuesWrapper.java b/packages/SettingsLib/src/com/android/settingslib/inputmethod/InputMethodSettingValuesWrapper.java
index fac50bd..554fb35 100644
--- a/packages/SettingsLib/src/com/android/settingslib/inputmethod/InputMethodSettingValuesWrapper.java
+++ b/packages/SettingsLib/src/com/android/settingslib/inputmethod/InputMethodSettingValuesWrapper.java
@@ -129,7 +129,7 @@
 
         return enabledValidSystemNonAuxAsciiCapableImeCount <= 1
                 && !(enabledValidSystemNonAuxAsciiCapableImeCount == 1 && !isEnabled)
-                && InputMethodUtils.isSystemIme(imi)
+                && imi.isSystem()
                 && isValidSystemNonAuxAsciiCapableIme(imi, context);
 
     }
diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiStatusTracker.java b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiStatusTracker.java
index 9347674..547cd9a 100644
--- a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiStatusTracker.java
+++ b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiStatusTracker.java
@@ -41,8 +41,9 @@
     private final WifiManager mWifiManager;
     private final NetworkScoreManager mNetworkScoreManager;
     private final ConnectivityManager mConnectivityManager;
+    private final Handler mHandler = new Handler(Looper.getMainLooper());
     private final WifiNetworkScoreCache.CacheListener mCacheListener =
-            new WifiNetworkScoreCache.CacheListener(new Handler(Looper.getMainLooper())) {
+            new WifiNetworkScoreCache.CacheListener(mHandler) {
                 @Override
                 public void networkCacheUpdated(List<ScoredNetwork> updatedNetworks) {
                     updateStatusLabel();
@@ -89,7 +90,8 @@
             mNetworkScoreManager.registerNetworkScoreCache(NetworkKey.TYPE_WIFI,
                     mWifiNetworkScoreCache, NetworkScoreManager.CACHE_FILTER_CURRENT_NETWORK);
             mWifiNetworkScoreCache.registerListener(mCacheListener);
-            mConnectivityManager.registerNetworkCallback(mNetworkRequest, mNetworkCallback);
+            mConnectivityManager.registerNetworkCallback(
+                    mNetworkRequest, mNetworkCallback, mHandler);
         } else {
             mNetworkScoreManager.unregisterNetworkScoreCache(NetworkKey.TYPE_WIFI,
                     mWifiNetworkScoreCache);
diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java
index a128b54..d8f0886 100644
--- a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java
+++ b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java
@@ -313,7 +313,8 @@
             mContext.registerReceiver(mReceiver, mFilter, null /* permission */, mWorkHandler);
             // NetworkCallback objects cannot be reused. http://b/20701525 .
             mNetworkCallback = new WifiTrackerNetworkCallback();
-            mConnectivityManager.registerNetworkCallback(mNetworkRequest, mNetworkCallback);
+            mConnectivityManager.registerNetworkCallback(
+                    mNetworkRequest, mNetworkCallback, mWorkHandler);
             mRegistered = true;
         }
     }
@@ -788,7 +789,7 @@
                 // We don't send a NetworkInfo object along with this message, because even if we
                 // fetch one from ConnectivityManager, it might be older than the most recent
                 // NetworkInfo message we got via a WIFI_STATE_CHANGED broadcast.
-                mWorkHandler.post(() -> updateNetworkInfo(null));
+                updateNetworkInfo(null);
             }
         }
     }
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/TwoTargetPreferenceTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/TwoTargetPreferenceTest.java
index c5e93f0..480143a7 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/TwoTargetPreferenceTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/TwoTargetPreferenceTest.java
@@ -16,6 +16,9 @@
 
 package com.android.settingslib;
 
+import static com.android.settingslib.TwoTargetPreference.ICON_SIZE_DEFAULT;
+import static com.android.settingslib.TwoTargetPreference.ICON_SIZE_MEDIUM;
+import static com.android.settingslib.TwoTargetPreference.ICON_SIZE_SMALL;
 import static com.google.common.truth.Truth.assertThat;
 import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.spy;
@@ -75,8 +78,8 @@
     }
 
     @Test
-    public void bind_smallIcon_shouldUseSmallIcon() {
-        mPreference.setUseSmallIcon(true);
+    public void bind_smallIcon_shouldUseSmallIconSize() {
+        mPreference.setIconSize(ICON_SIZE_SMALL);
 
         mPreference.onBindViewHolder(mViewHolder);
 
@@ -91,8 +94,24 @@
     }
 
     @Test
-    public void bind_normalIcon_shouldUseNormalIcon() {
-        mPreference.setUseSmallIcon(false);
+    public void bind_mediumIcon_shouldUseMediumIconSize() {
+        mPreference.setIconSize(ICON_SIZE_MEDIUM);
+
+        mPreference.onBindViewHolder(mViewHolder);
+
+        final int size = mContext.getResources().getDimensionPixelSize(
+                R.dimen.two_target_pref_medium_icon_size);
+        final LinearLayout.LayoutParams layoutParams = (LinearLayout.LayoutParams) mViewHolder
+                .findViewById(android.R.id.icon)
+                .getLayoutParams();
+
+        assertThat(layoutParams.width).isEqualTo(size);
+        assertThat(layoutParams.height).isEqualTo(size);
+    }
+
+    @Test
+    public void bind_defaultIcon_shouldUseDefaultIconSize() {
+        mPreference.setIconSize(ICON_SIZE_DEFAULT);
 
         mPreference.onBindViewHolder(mViewHolder);
 
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/fuelgauge/BatterySaverUtilsTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/fuelgauge/BatterySaverUtilsTest.java
index b33df30..ba5a2c5 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/fuelgauge/BatterySaverUtilsTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/fuelgauge/BatterySaverUtilsTest.java
@@ -16,7 +16,9 @@
 
 package com.android.settingslib.fuelgauge;
 
+import static com.google.common.truth.Truth.assertThat;
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertThat;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyBoolean;
 import static org.mockito.ArgumentMatchers.eq;
@@ -28,6 +30,7 @@
 import android.content.Context;
 import android.content.Intent;
 import android.os.PowerManager;
+import android.provider.Settings.Global;
 import android.provider.Settings.Secure;
 
 import com.android.settingslib.SettingsLibRobolectricTestRunner;
@@ -41,6 +44,9 @@
 
 @RunWith(SettingsLibRobolectricTestRunner.class)
 public class BatterySaverUtilsTest {
+    final int BATTERY_SAVER_THRESHOLD_1 = 15;
+    final int BATTERY_SAVER_THRESHOLD_2 = 20;
+
     @Mock
     Context mMockContext;
 
@@ -149,4 +155,37 @@
         assertEquals(-2,
                 Secure.getInt(mMockResolver, Secure.LOW_POWER_MANUAL_ACTIVATION_COUNT, -2));
     }
+
+    @Test
+    public void testEnsureAutoBatterysaver_setNewPositiveValue_doNotOverwrite() throws Exception {
+        Global.putString(mMockResolver, Global.LOW_POWER_MODE_TRIGGER_LEVEL, "null");
+
+        BatterySaverUtils.ensureAutoBatterySaver(mMockContext, BATTERY_SAVER_THRESHOLD_1);
+
+        assertThat(Secure.getInt(mMockResolver, Global.LOW_POWER_MODE_TRIGGER_LEVEL, -1))
+                .isEqualTo(BATTERY_SAVER_THRESHOLD_1);
+
+        // Once a positive number is set, ensureAutoBatterySaver() won't overwrite it.
+        BatterySaverUtils.ensureAutoBatterySaver(mMockContext, BATTERY_SAVER_THRESHOLD_2);
+        assertThat(Secure.getInt(mMockResolver, Global.LOW_POWER_MODE_TRIGGER_LEVEL, -1))
+                .isEqualTo(BATTERY_SAVER_THRESHOLD_1);
+    }
+
+    @Test
+    public void testSetAutoBatterySaverTriggerLevel_setSuppressSuggestion() throws Exception {
+        Global.putString(mMockResolver, Global.LOW_POWER_MODE_TRIGGER_LEVEL, "null");
+        Secure.putString(mMockResolver, Secure.SUPPRESS_AUTO_BATTERY_SAVER_SUGGESTION, "null");
+
+        BatterySaverUtils.setAutoBatterySaverTriggerLevel(mMockContext, 0);
+        assertThat(Global.getInt(mMockResolver, Global.LOW_POWER_MODE_TRIGGER_LEVEL, -1))
+                .isEqualTo(0);
+        assertThat(Secure.getInt(mMockResolver, Secure.SUPPRESS_AUTO_BATTERY_SAVER_SUGGESTION, -1))
+                .isEqualTo(-1); // not set.
+
+        BatterySaverUtils.setAutoBatterySaverTriggerLevel(mMockContext, BATTERY_SAVER_THRESHOLD_1 );
+        assertThat( Global.getInt(mMockResolver, Global.LOW_POWER_MODE_TRIGGER_LEVEL, -1))
+                .isEqualTo(BATTERY_SAVER_THRESHOLD_1);
+        assertThat(Secure.getInt(mMockResolver, Secure.SUPPRESS_AUTO_BATTERY_SAVER_SUGGESTION, -1))
+                .isEqualTo(1);
+    }
 }
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
index 67053d8..a2263b4 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
@@ -2231,6 +2231,12 @@
                 Settings.Secure.WAKE_GESTURE_ENABLED,
                 SecureSettingsProto.WAKE_GESTURE_ENABLED);
 
+        final long launcherToken = p.start(SecureSettingsProto.LAUNCHER);
+        dumpSetting(s, p,
+                Settings.Secure.SWIPE_UP_TO_SWITCH_APPS_ENABLED,
+                SecureSettingsProto.Launcher.SWIPE_UP_TO_SWITCH_APPS_ENABLED);
+        p.end(launcherToken);
+
         // Please insert new settings using the same order as in SecureSettingsProto.
         p.end(token);
 
diff --git a/packages/SystemUI/res/layout/car_fullscreen_user_pod.xml b/packages/SystemUI/res/layout/car_fullscreen_user_pod.xml
index 0ee40d7..67f68d3 100644
--- a/packages/SystemUI/res/layout/car_fullscreen_user_pod.xml
+++ b/packages/SystemUI/res/layout/car_fullscreen_user_pod.xml
@@ -15,40 +15,32 @@
      limitations under the License.
 -->
 
+
 <RelativeLayout
     xmlns:android="http://schemas.android.com/apk/res/android"
     android:clipChildren="false"
     android:alpha="0"
-    android:layout_width="wrap_content"
-    android:layout_height="@dimen/car_fullscreen_user_pod_height"
-    android:layout_gravity="center_horizontal|bottom" >
+    android:layout_height="wrap_content"
+    android:layout_width="match_parent"
+    android:gravity="fill_horizontal"
+    android:layout_marginTop="@dimen/car_padding_5"
+    android:layout_marginStart="@dimen/car_padding_4">
 
     <ImageView android:id="@+id/user_avatar"
         android:layout_centerHorizontal="true"
-        android:layout_marginTop="@dimen/car_fullscreen_user_pod_margin_image_top"
         android:layout_width="@dimen/car_fullscreen_user_pod_image_avatar_width"
         android:layout_height="@dimen/car_fullscreen_user_pod_image_avatar_height"
-        android:layout_above="@id/user_name" />
+        />
 
     <TextView android:id="@+id/user_name"
-        android:layout_width="@dimen/car_fullscreen_user_pod_width"
+        android:layout_width="match_parent"
         android:layout_height="wrap_content"
-        android:layout_marginTop="@dimen/car_fullscreen_user_pod_margin_name_top"
-        android:layout_marginBottom="@dimen/car_fullscreen_user_pod_margin_name_bottom"
-        android:textSize="@dimen/car_fullscreen_user_pod_name_text_size"
+        android:layout_marginTop="@dimen/car_padding_4"
+        android:textSize="@dimen/car_body1_size"
         android:textColor="@color/qs_user_detail_name"
         android:ellipsize="end"
         android:singleLine="true"
-        android:gravity="center_horizontal"
-        android:layout_above="@id/device_name" />
+        android:gravity="center"
+        android:layout_below="@id/user_avatar"/>
 
-    <TextView android:id="@+id/device_name"
-        android:layout_width="@dimen/car_fullscreen_user_pod_width"
-        android:layout_height="wrap_content"
-        android:textSize="@dimen/car_fullscreen_user_pod_device_text_size"
-        android:textColor="@color/qs_user_detail_name"
-        android:ellipsize="end"
-        android:singleLine="true"
-        android:gravity="center_horizontal"
-        android:layout_alignParentBottom="true" />
 </RelativeLayout>
diff --git a/packages/SystemUI/res/layout/car_fullscreen_user_pod_container.xml b/packages/SystemUI/res/layout/car_fullscreen_user_pod_container.xml
deleted file mode 100644
index d666a20..0000000
--- a/packages/SystemUI/res/layout/car_fullscreen_user_pod_container.xml
+++ /dev/null
@@ -1,25 +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.
--->
-<LinearLayout
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    android:clipChildren="false"
-    android:layout_width="match_parent"
-    android:layout_height="wrap_content"
-    android:gravity="center" >
-
-    <!-- car_fullscreen_user_pods will be dynamically added here. -->
-</LinearLayout>
diff --git a/packages/SystemUI/res/layout/car_fullscreen_user_switcher.xml b/packages/SystemUI/res/layout/car_fullscreen_user_switcher.xml
index bfabe52..22452b7 100644
--- a/packages/SystemUI/res/layout/car_fullscreen_user_switcher.xml
+++ b/packages/SystemUI/res/layout/car_fullscreen_user_switcher.xml
@@ -39,27 +39,34 @@
             android:theme="@android:style/Theme"
             android:layout_alignParentTop="true"/>
 
-        <com.android.systemui.statusbar.car.UserGridView
-            android:id="@+id/user_grid"
+        <RelativeLayout
+            android:id="@+id/fullscreen_user_switcher_container"
             android:layout_width="match_parent"
-            android:layout_height="wrap_content"
-            android:layout_marginLeft="@dimen/car_margin"
-            android:layout_marginRight="@dimen/car_margin"
-            android:layout_centerInParent="true"/>
+            android:layout_height="match_parent"
+            android:layout_marginStart="@dimen/car_margin"
+            android:layout_marginEnd="@dimen/car_margin">
 
-        <com.android.systemui.statusbar.car.PageIndicator
-            android:id="@+id/user_switcher_page_indicator"
-            android:layout_width="match_parent"
-            android:layout_height="@dimen/car_page_indicator_dot_diameter"
-            android:layout_below="@+id/user_grid" />
+            <RelativeLayout
+                android:id="@+id/fullscreen_user_switcher_container_inner"
+                android:layout_width="match_parent"
+                android:layout_height="match_parent"
+                android:layout_marginEnd="@dimen/car_padding_4">
 
-        <Button
-            android:id="@+id/start_driving"
-            android:layout_width="wrap_content"
-            android:layout_height="@dimen/car_start_driving_height"
-            android:text="@string/start_driving"
-            style="@style/CarUserSwitcher.StartDrivingButton"
-            android:layout_alignParentBottom="true"
-            android:layout_centerHorizontal="true" />
+                <com.android.systemui.statusbar.car.UserGridRecyclerView
+                    android:id="@+id/user_grid"
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
+                    android:layout_below="@+id/header"
+                    android:scrollbars="vertical"
+                    android:scrollbarFadeDuration="0"
+                    android:verticalScrollbarPosition="left"
+                    android:layout_centerHorizontal="true"
+                    android:layout_centerVertical="true"
+                    android:layout_alignParentRight="true"/>
+
+            </RelativeLayout>
+
+        </RelativeLayout>
+
     </RelativeLayout>
 </FrameLayout>
diff --git a/packages/SystemUI/res/layout/car_left_navigation_bar.xml b/packages/SystemUI/res/layout/car_left_navigation_bar.xml
index 18301a8..02be457 100644
--- a/packages/SystemUI/res/layout/car_left_navigation_bar.xml
+++ b/packages/SystemUI/res/layout/car_left_navigation_bar.xml
@@ -40,7 +40,7 @@
             android:id="@+id/home"
             android:layout_height="wrap_content"
             android:layout_width="match_parent"
-            systemui:intent="intent:#Intent;action=android.intent.action.MAIN;category=android.intent.category.HOME;end"
+            systemui:intent="intent:#Intent;action=android.intent.action.MAIN;category=android.intent.category.HOME;launchFlags=0x14000000;end"
             android:src="@drawable/car_ic_overview"
             android:background="?android:attr/selectableItemBackground"
             android:paddingTop="30dp"
diff --git a/packages/SystemUI/res/layout/car_left_navigation_bar_unprovisioned.xml b/packages/SystemUI/res/layout/car_left_navigation_bar_unprovisioned.xml
index a65ff16..708f595 100644
--- a/packages/SystemUI/res/layout/car_left_navigation_bar_unprovisioned.xml
+++ b/packages/SystemUI/res/layout/car_left_navigation_bar_unprovisioned.xml
@@ -40,7 +40,7 @@
             android:id="@+id/home"
             android:layout_height="wrap_content"
             android:layout_width="match_parent"
-            systemui:intent="intent:#Intent;action=android.intent.action.MAIN;category=android.intent.category.HOME;end"
+            systemui:intent="intent:#Intent;action=android.intent.action.MAIN;category=android.intent.category.HOME;launchFlags=0x14000000;end"
             android:src="@drawable/car_ic_overview"
             android:background="?android:attr/selectableItemBackground"
             android:paddingTop="30dp"
diff --git a/packages/SystemUI/res/layout/car_navigation_bar.xml b/packages/SystemUI/res/layout/car_navigation_bar.xml
index 9ff16a2..d568d0d 100644
--- a/packages/SystemUI/res/layout/car_navigation_bar.xml
+++ b/packages/SystemUI/res/layout/car_navigation_bar.xml
@@ -38,7 +38,7 @@
             android:id="@+id/home"
             android:layout_height="match_parent"
             android:layout_width="wrap_content"
-            systemui:intent="intent:#Intent;action=android.intent.action.MAIN;category=android.intent.category.HOME;end"
+            systemui:intent="intent:#Intent;action=android.intent.action.MAIN;category=android.intent.category.HOME;launchFlags=0x14000000;end"
             android:src="@drawable/car_ic_overview"
             android:background="?android:attr/selectableItemBackground"
             android:paddingLeft="30dp"
diff --git a/packages/SystemUI/res/layout/car_navigation_bar_unprovisioned.xml b/packages/SystemUI/res/layout/car_navigation_bar_unprovisioned.xml
index b0488ae..4ba6c06 100644
--- a/packages/SystemUI/res/layout/car_navigation_bar_unprovisioned.xml
+++ b/packages/SystemUI/res/layout/car_navigation_bar_unprovisioned.xml
@@ -38,7 +38,7 @@
             android:id="@+id/home"
             android:layout_height="match_parent"
             android:layout_width="wrap_content"
-            systemui:intent="intent:#Intent;action=android.intent.action.MAIN;category=android.intent.category.HOME;end"
+            systemui:intent="intent:#Intent;action=android.intent.action.MAIN;category=android.intent.category.HOME;launchFlags=0x14000000;end"
             android:src="@drawable/car_ic_overview"
             android:background="?android:attr/selectableItemBackground"
             android:paddingLeft="30dp"
diff --git a/packages/SystemUI/res/layout/car_qs_panel.xml b/packages/SystemUI/res/layout/car_qs_panel.xml
index 447970c..c01bbce 100644
--- a/packages/SystemUI/res/layout/car_qs_panel.xml
+++ b/packages/SystemUI/res/layout/car_qs_panel.xml
@@ -30,27 +30,29 @@
     <RelativeLayout
         xmlns:android="http://schemas.android.com/apk/res/android"
         android:id="@+id/user_switcher_container"
+        android:layout_marginStart="@dimen/car_margin"
+        android:layout_marginEnd="@dimen/car_margin"
         android:clipChildren="false"
         android:layout_width="match_parent"
-        android:layout_height="@dimen/car_user_switcher_container_height"
-        android:layout_gravity="center_horizontal" >
+        android:layout_height="@dimen/car_user_switcher_container_height">
 
-        <com.android.systemui.statusbar.car.UserGridView
-            android:id="@+id/user_grid"
-            android:clipChildren="false"
+        <RelativeLayout
+            android:id="@+id/fullscreen_user_switcher_container_inner"
             android:layout_width="match_parent"
-            android:layout_height="wrap_content"
-            android:layout_marginLeft="@dimen/car_margin"
-            android:layout_marginRight="@dimen/car_margin"
-            android:layout_above="@id/user_switcher_page_indicator" />
+            android:layout_height="match_parent"
+            android:layout_marginEnd="@dimen/car_padding_4">
 
-        <com.android.systemui.statusbar.car.PageIndicator
-            android:id="@+id/user_switcher_page_indicator"
-            android:layout_width="match_parent"
-            android:layout_height="@dimen/car_page_indicator_dot_diameter"
-            android:layout_marginBottom="@dimen/car_page_indicator_margin_bottom"
-            android:alpha="0"
-            android:layout_alignParentBottom="true" />
+            <com.android.systemui.statusbar.car.UserGridRecyclerView
+                android:id="@+id/user_grid"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:scrollbars="vertical"
+                android:verticalScrollbarPosition="left"
+                android:layout_centerHorizontal="true"
+                android:layout_centerVertical="true"
+                android:layout_alignParentRight="true"
+                android:scrollbarFadeDuration="0"/>
+        </RelativeLayout>
 
     </RelativeLayout>
 
diff --git a/packages/SystemUI/res/layout/car_right_navigation_bar.xml b/packages/SystemUI/res/layout/car_right_navigation_bar.xml
index 99bd23c..91ba026 100644
--- a/packages/SystemUI/res/layout/car_right_navigation_bar.xml
+++ b/packages/SystemUI/res/layout/car_right_navigation_bar.xml
@@ -40,7 +40,7 @@
             android:id="@+id/home"
             android:layout_height="wrap_content"
             android:layout_width="match_parent"
-            systemui:intent="intent:#Intent;action=android.intent.action.MAIN;category=android.intent.category.HOME;end"
+            systemui:intent="intent:#Intent;action=android.intent.action.MAIN;category=android.intent.category.HOME;launchFlags=0x14000000;end"
             android:src="@drawable/car_ic_overview"
             android:background="?android:attr/selectableItemBackground"
             android:paddingTop="30dp"
diff --git a/packages/SystemUI/res/layout/car_right_navigation_bar_unprovisioned.xml b/packages/SystemUI/res/layout/car_right_navigation_bar_unprovisioned.xml
index a65ff16..708f595 100644
--- a/packages/SystemUI/res/layout/car_right_navigation_bar_unprovisioned.xml
+++ b/packages/SystemUI/res/layout/car_right_navigation_bar_unprovisioned.xml
@@ -40,7 +40,7 @@
             android:id="@+id/home"
             android:layout_height="wrap_content"
             android:layout_width="match_parent"
-            systemui:intent="intent:#Intent;action=android.intent.action.MAIN;category=android.intent.category.HOME;end"
+            systemui:intent="intent:#Intent;action=android.intent.action.MAIN;category=android.intent.category.HOME;launchFlags=0x14000000;end"
             android:src="@drawable/car_ic_overview"
             android:background="?android:attr/selectableItemBackground"
             android:paddingTop="30dp"
diff --git a/packages/SystemUI/res/layout/navigation_layout.xml b/packages/SystemUI/res/layout/navigation_layout.xml
index 79e54aa..d72021e 100644
--- a/packages/SystemUI/res/layout/navigation_layout.xml
+++ b/packages/SystemUI/res/layout/navigation_layout.xml
@@ -27,13 +27,16 @@
     <com.android.systemui.statusbar.phone.NearestTouchFrame
         android:id="@+id/nav_buttons"
         android:layout_width="match_parent"
-        android:layout_height="match_parent">
+        android:layout_height="match_parent"
+        android:clipChildren="false"
+        android:clipToPadding="false">
 
         <LinearLayout
             android:id="@+id/ends_group"
             android:layout_width="match_parent"
             android:layout_height="match_parent"
             android:orientation="horizontal"
+            android:clipToPadding="false"
             android:clipChildren="false" />
 
         <LinearLayout
@@ -43,6 +46,7 @@
             android:layout_gravity="center"
             android:gravity="center"
             android:orientation="horizontal"
+            android:clipToPadding="false"
             android:clipChildren="false" />
 
     </com.android.systemui.statusbar.phone.NearestTouchFrame>
diff --git a/packages/SystemUI/res/layout/remote_input.xml b/packages/SystemUI/res/layout/remote_input.xml
index e4ea08e..b5d48b4 100644
--- a/packages/SystemUI/res/layout/remote_input.xml
+++ b/packages/SystemUI/res/layout/remote_input.xml
@@ -51,12 +51,10 @@
 
         <ImageButton
                 android:layout_width="wrap_content"
-                android:layout_height="wrap_content"
+                android:layout_height="match_parent"
                 android:layout_gravity="center"
                 android:paddingStart="12dp"
                 android:paddingEnd="24dp"
-                android:paddingTop="16dp"
-                android:paddingBottom="16dp"
                 android:id="@+id/remote_input_send"
                 android:src="@drawable/ic_send"
                 android:contentDescription="@*android:string/ime_action_send"
diff --git a/packages/SystemUI/res/values/dimens_car.xml b/packages/SystemUI/res/values/dimens_car.xml
index 6caed61..2b91891 100644
--- a/packages/SystemUI/res/values/dimens_car.xml
+++ b/packages/SystemUI/res/values/dimens_car.xml
@@ -17,18 +17,14 @@
 -->
 <resources>
     <dimen name="car_margin">148dp</dimen>
+    <dimen name="car_margin_standard">112dp</dimen>
 
-    <dimen name="car_fullscreen_user_pod_margin_image_top">24dp</dimen>
-    <dimen name="car_fullscreen_user_pod_margin_name_top">24dp</dimen>
-    <dimen name="car_fullscreen_user_pod_margin_name_bottom">20dp</dimen>
-    <dimen name="car_fullscreen_user_pod_margin_between">24dp</dimen>
-    <dimen name="car_fullscreen_user_pod_icon_text_size">96dp</dimen>
-    <dimen name="car_fullscreen_user_pod_image_avatar_width">192dp</dimen>
-    <dimen name="car_fullscreen_user_pod_image_avatar_height">192dp</dimen>
-    <dimen name="car_fullscreen_user_pod_width">264dp</dimen>
+    <!-- TODO replace with car support lib sizes when available -->
+    <dimen name="car_fullscreen_user_pod_icon_text_size">32sp</dimen>
+    <dimen name="car_fullscreen_user_pod_width">243dp</dimen>
     <dimen name="car_fullscreen_user_pod_height">356dp</dimen>
-    <dimen name="car_fullscreen_user_pod_name_text_size">40sp</dimen> <!-- B1 -->
-    <dimen name="car_fullscreen_user_pod_device_text_size">@dimen/car_body2_size</dimen>
+    <dimen name="car_fullscreen_user_pod_image_avatar_width">96dp</dimen>
+    <dimen name="car_fullscreen_user_pod_image_avatar_height">96dp</dimen>
 
     <dimen name="car_navigation_button_width">64dp</dimen>
     <dimen name="car_navigation_bar_width">760dp</dimen>
diff --git a/packages/SystemUI/res/values/integers_car.xml b/packages/SystemUI/res/values/integers_car.xml
index a462576..7513fd4 100644
--- a/packages/SystemUI/res/values/integers_car.xml
+++ b/packages/SystemUI/res/values/integers_car.xml
@@ -17,4 +17,6 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android">
     <integer name="car_user_switcher_anim_cascade_delay_ms">27</integer>
+    <!-- Full screen user switcher column number TODO: move to support library-->
+    <integer name="user_fullscreen_switcher_num_col">3</integer>
 </resources>
diff --git a/packages/SystemUI/res/values/strings_car.xml b/packages/SystemUI/res/values/strings_car.xml
index 658eb4f..0b57ff8 100644
--- a/packages/SystemUI/res/values/strings_car.xml
+++ b/packages/SystemUI/res/values/strings_car.xml
@@ -17,6 +17,10 @@
  */
 -->
 <resources>
-    <string name="unknown_user_label">Unknown</string>
-    <string name="start_driving">Start Driving</string>
+    <!-- Name of Guest Profile. [CHAR LIMIT=30] -->
+    <string name="car_guest">Guest</string>
+    <!-- Name of Add User Profile. [CHAR LIMIT=30] -->
+    <string name="car_add_user">Add User</string>
+    <!-- Default name of the new user created. [CHAR LIMIT=30] -->
+    <string name="car_new_user">New User</string>
 </resources>
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/PackageManagerWrapper.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/PackageManagerWrapper.java
index 6fa7db3..32e4bbf 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/PackageManagerWrapper.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/PackageManagerWrapper.java
@@ -18,23 +18,24 @@
 
 import android.app.AppGlobals;
 import android.content.ComponentName;
+import android.content.Intent;
 import android.content.pm.ActivityInfo;
 import android.content.pm.IPackageManager;
 import android.content.pm.PackageManager;
 import android.content.pm.ResolveInfo;
 import android.os.RemoteException;
 
-import java.util.ArrayList;
 import java.util.List;
 
 public class PackageManagerWrapper {
 
-    private static final String TAG = "PackageManagerWrapper";
-
     private static final PackageManagerWrapper sInstance = new PackageManagerWrapper();
 
     private static final IPackageManager mIPackageManager = AppGlobals.getPackageManager();
 
+    public static final String ACTION_PREFERRED_ACTIVITY_CHANGED =
+            Intent.ACTION_PREFERRED_ACTIVITY_CHANGED;
+
     public static PackageManagerWrapper getInstance() {
         return sInstance;
     }
@@ -53,40 +54,15 @@
     }
 
     /**
-     * @return true if the packageName belongs to the current preferred home app on the device.
-     *
-     * If will also return false if there are multiple home apps and the user has not picked any
-     * preferred home, in which case the user would see a disambiguation screen on going to home.
+     * Report the set of 'Home' activity candidates, plus (if any) which of them
+     * is the current "always use this one" setting.
      */
-    public boolean isDefaultHomeActivity(String packageName) {
-        List<ResolveInfo> allHomeCandidates = new ArrayList<>();
-        ComponentName home;
+    public ComponentName getHomeActivities(List<ResolveInfo> allHomeCandidates) {
         try {
-            home = mIPackageManager.getHomeActivities(allHomeCandidates);
+            return mIPackageManager.getHomeActivities(allHomeCandidates);
         } catch (RemoteException e) {
             e.printStackTrace();
-            return false;
+            return null;
         }
-
-        if (home != null && packageName.equals(home.getPackageName())) {
-            return true;
-        }
-
-        // Find the launcher with the highest priority and return that component if there are no
-        // other home activity with the same priority.
-        int lastPriority = Integer.MIN_VALUE;
-        ComponentName lastComponent = null;
-        final int size = allHomeCandidates.size();
-        for (int i = 0; i < size; i++) {
-            final ResolveInfo ri = allHomeCandidates.get(i);
-            if (ri.priority > lastPriority) {
-                lastComponent = ri.activityInfo.getComponentName();
-                lastPriority = ri.priority;
-            } else if (ri.priority == lastPriority) {
-                // Two components found with same priority.
-                lastComponent = null;
-            }
-        }
-        return lastComponent != null && packageName.equals(lastComponent.getPackageName());
     }
 }
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/TransactionCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/TransactionCompat.java
index c82c519..9975c41 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/TransactionCompat.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/TransactionCompat.java
@@ -101,6 +101,11 @@
         return this;
     }
 
+    public TransactionCompat setEarlyWakeup() {
+        mTransaction.setEarlyWakeup();
+        return this;
+    }
+
     public TransactionCompat setColor(SurfaceControlCompat surfaceControl, float[] color) {
         mTransaction.setColor(surfaceControl.mSurfaceControl, color);
         return this;
diff --git a/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java b/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java
index a4f8d8c..b8a57bf 100644
--- a/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java
+++ b/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java
@@ -444,13 +444,7 @@
             final Surface surface = getSurfaceHolder().getSurface();
             surface.hwuiDestroy();
 
-            mLoader = new AsyncTask<Void, Void, Bitmap>() {
-                @Override
-                protected Bitmap doInBackground(Void... params) {
-                    mWallpaperManager.forgetLoadedWallpaper();
-                    return null;
-                }
-            }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
+            mWallpaperManager.forgetLoadedWallpaper();
         }
 
         private void scheduleUnloadWallpaper() {
diff --git a/packages/SystemUI/src/com/android/systemui/power/PowerNotificationWarnings.java b/packages/SystemUI/src/com/android/systemui/power/PowerNotificationWarnings.java
index a70b358..40ce69b 100644
--- a/packages/SystemUI/src/com/android/systemui/power/PowerNotificationWarnings.java
+++ b/packages/SystemUI/src/com/android/systemui/power/PowerNotificationWarnings.java
@@ -514,7 +514,7 @@
             autoTriggerThreshold = 15;
         }
 
-        BatterySaverUtils.scheduleAutoBatterySaver(mContext, autoTriggerThreshold);
+        BatterySaverUtils.ensureAutoBatterySaver(mContext, autoTriggerThreshold);
         showAutoSaverEnabledConfirmation();
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/qs/car/CarQSFooter.java b/packages/SystemUI/src/com/android/systemui/qs/car/CarQSFooter.java
index 23d3ebbb..24b5a34 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/car/CarQSFooter.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/car/CarQSFooter.java
@@ -29,7 +29,6 @@
 import com.android.systemui.plugins.ActivityStarter;
 import com.android.systemui.qs.QSFooter;
 import com.android.systemui.qs.QSPanel;
-import com.android.systemui.statusbar.car.UserGridView;
 import com.android.systemui.statusbar.phone.MultiUserSwitch;
 import com.android.systemui.statusbar.policy.DeviceProvisionedController;
 import com.android.systemui.statusbar.policy.UserInfoController;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/car/CarQSFragment.java b/packages/SystemUI/src/com/android/systemui/qs/car/CarQSFragment.java
index 0ee6d1f..da21aa5 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/car/CarQSFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/car/CarQSFragment.java
@@ -20,21 +20,20 @@
 import android.animation.ObjectAnimator;
 import android.animation.ValueAnimator;
 import android.app.Fragment;
+import android.content.Context;
 import android.os.Bundle;
 import android.support.annotation.Nullable;
 import android.support.annotation.VisibleForTesting;
+import android.support.v7.widget.GridLayoutManager;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.View.OnClickListener;
 import android.view.ViewGroup;
 
-import com.android.systemui.Dependency;
 import com.android.systemui.R;
 import com.android.systemui.plugins.qs.QS;
 import com.android.systemui.qs.QSFooter;
-import com.android.systemui.statusbar.car.PageIndicator;
-import com.android.systemui.statusbar.car.UserGridView;
-import com.android.systemui.statusbar.policy.UserSwitcherController;
+import com.android.systemui.statusbar.car.UserGridRecyclerView;
 
 import java.util.ArrayList;
 import java.util.List;
@@ -45,14 +44,12 @@
  * status bar, and a static row with access to the user switcher and settings.
  */
 public class CarQSFragment extends Fragment implements QS {
-    private ViewGroup mPanel;
     private View mHeader;
     private View mUserSwitcherContainer;
     private CarQSFooter mFooter;
     private View mFooterUserName;
     private View mFooterExpandIcon;
-    private UserGridView mUserGridView;
-    private PageIndicator mPageIndicator;
+    private UserGridRecyclerView mUserGridView;
     private AnimatorSet mAnimatorSet;
     private UserSwitchCallback mUserSwitchCallback;
 
@@ -65,7 +62,6 @@
     @Override
     public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
         super.onViewCreated(view, savedInstanceState);
-        mPanel = (ViewGroup) view;
         mHeader = view.findViewById(R.id.header);
         mFooter = view.findViewById(R.id.qs_footer);
         mFooterUserName = mFooter.findViewById(R.id.user_name);
@@ -75,16 +71,15 @@
 
         updateUserSwitcherHeight(0);
 
-        mUserGridView = view.findViewById(R.id.user_grid);
-        mUserGridView.init(null, Dependency.get(UserSwitcherController.class),
-                false /* overrideAlpha */);
-
-        mPageIndicator = view.findViewById(R.id.user_switcher_page_indicator);
-        mPageIndicator.setupWithViewPager(mUserGridView);
+        Context context = getContext();
+        mUserGridView = mUserSwitcherContainer.findViewById(R.id.user_grid);
+        GridLayoutManager layoutManager = new GridLayoutManager(context,
+                context.getResources().getInteger(R.integer.user_fullscreen_switcher_num_col));
+        mUserGridView.setLayoutManager(layoutManager);
+        mUserGridView.buildAdapter();
 
         mUserSwitchCallback = new UserSwitchCallback();
         mFooter.setUserSwitchCallback(mUserSwitchCallback);
-        mUserGridView.setUserSwitchCallback(mUserSwitchCallback);
     }
 
     @Override
@@ -111,13 +106,11 @@
     @Override
     public void setHeaderListening(boolean listening) {
         mFooter.setListening(listening);
-        mUserGridView.setListening(listening);
     }
 
     @Override
     public void setListening(boolean listening) {
         mFooter.setListening(listening);
-        mUserGridView.setListening(listening);
     }
 
     @Override
@@ -219,24 +212,6 @@
             mShowing = false;
             animateHeightChange(false /* opening */);
         }
-
-        public void resetShowing() {
-            if (mShowing) {
-                for (int i = 0; i < mUserGridView.getChildCount(); i++) {
-                    ViewGroup podContainer = (ViewGroup) mUserGridView.getChildAt(i);
-                    // Need to bring the last child to the front to maintain the order in the pod
-                    // container. Why? ¯\_(ツ)_/¯
-                    if (podContainer.getChildCount() > 0) {
-                        podContainer.getChildAt(podContainer.getChildCount() - 1).bringToFront();
-                    }
-                    // The alpha values are default to 0, so if the pods have been refreshed, they
-                    // need to be set to 1 when showing.
-                    for (int j = 0; j < podContainer.getChildCount(); j++) {
-                        podContainer.getChildAt(j).setAlpha(1f);
-                    }
-                }
-            }
-        }
     }
 
     private void updateUserSwitcherHeight(int height) {
@@ -260,27 +235,6 @@
         });
         allAnimators.add(heightAnimator);
 
-        // The user grid contains pod containers that each contain a number of pods.  Animate
-        // all pods to avoid any discrepancy/race conditions with possible changes during the
-        // animation.
-        int cascadeDelay = getResources().getInteger(
-                R.integer.car_user_switcher_anim_cascade_delay_ms);
-        for (int i = 0; i < mUserGridView.getChildCount(); i++) {
-            ViewGroup podContainer = (ViewGroup) mUserGridView.getChildAt(i);
-            for (int j = 0; j < podContainer.getChildCount(); j++) {
-                View pod = podContainer.getChildAt(j);
-                Animator podAnimator = AnimatorInflater.loadAnimator(getContext(),
-                        opening ? R.anim.car_user_switcher_open_pod_animation
-                                : R.anim.car_user_switcher_close_pod_animation);
-                // Add the cascading delay between pods
-                if (opening) {
-                    podAnimator.setStartDelay(podAnimator.getStartDelay() + j * cascadeDelay);
-                }
-                podAnimator.setTarget(pod);
-                allAnimators.add(podAnimator);
-            }
-        }
-
         Animator nameAnimator = AnimatorInflater.loadAnimator(getContext(),
                 opening ? R.anim.car_user_switcher_open_name_animation
                         : R.anim.car_user_switcher_close_name_animation);
@@ -293,12 +247,6 @@
         iconAnimator.setTarget(mFooterExpandIcon);
         allAnimators.add(iconAnimator);
 
-        Animator pageAnimator = AnimatorInflater.loadAnimator(getContext(),
-                opening ? R.anim.car_user_switcher_open_pages_animation
-                        : R.anim.car_user_switcher_close_pages_animation);
-        pageAnimator.setTarget(mPageIndicator);
-        allAnimators.add(pageAnimator);
-
         mAnimatorSet = new AnimatorSet();
         mAnimatorSet.addListener(new AnimatorListenerAdapter() {
             @Override
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java
index 28917ab..f7cbee6 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java
@@ -210,7 +210,7 @@
         state.slash.isSlashed = !state.value;
         state.label = getTileLabel();
         state.secondaryLabel = ZenModeConfig.getDescription(mContext,zen != Global.ZEN_MODE_OFF,
-                mController.getConfig());
+                mController.getConfig(), false);
         state.icon = ResourceIcon.get(R.drawable.ic_qs_dnd_on);
         checkIfRestrictionEnforcedByAdminOnly(state, UserManager.DISALLOW_ADJUST_VOLUME);
         switch (zen) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java
index b81e9af..29c2edc 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java
@@ -26,6 +26,7 @@
 import android.util.ArraySet;
 import android.util.AttributeSet;
 import android.util.Log;
+import android.view.MotionEvent;
 import android.view.NotificationHeaderView;
 import android.view.View;
 import android.view.ViewGroup;
@@ -1631,6 +1632,42 @@
         return null;
     }
 
+    @Override
+    public boolean dispatchTouchEvent(MotionEvent ev) {
+        float y = ev.getY();
+        // We still want to distribute touch events to the remote input even if it's outside the
+        // view boundary. We're therefore manually dispatching these events to the remote view
+        RemoteInputView riv = getRemoteInputForView(getViewForVisibleType(mVisibleType));
+        if (riv != null && riv.getVisibility() == VISIBLE) {
+            int inputStart = mUnrestrictedContentHeight - riv.getHeight();
+            if (y <= mUnrestrictedContentHeight && y >= inputStart) {
+                ev.offsetLocation(0, -inputStart);
+                return riv.dispatchTouchEvent(ev);
+            }
+        }
+        return super.dispatchTouchEvent(ev);
+    }
+
+    /**
+     * Overridden to make sure touches to the reply action bar actually go through to this view
+     */
+    @Override
+    public boolean pointInView(float localX, float localY, float slop) {
+        float top = mClipTopAmount;
+        float bottom = mUnrestrictedContentHeight;
+        return localX >= -slop && localY >= top - slop && localX < ((mRight - mLeft) + slop) &&
+                localY < (bottom + slop);
+    }
+
+    private RemoteInputView getRemoteInputForView(View child) {
+        if (child == mExpandedChild) {
+            return mExpandedRemoteInput;
+        } else if (child == mHeadsUpChild) {
+            return mHeadsUpRemoteInput;
+        }
+        return null;
+    }
+
     public int getExpandHeight() {
         int viewType = VISIBLE_TYPE_EXPANDED;
         if (mExpandedChild == null) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java
index 852239a..abc261e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java
@@ -172,6 +172,14 @@
                 }
             }
 
+            if (mediaNotification != null) {
+                mMediaNotificationKey = mediaNotification.notification.getKey();
+                if (DEBUG_MEDIA) {
+                    Log.v(TAG, "DEBUG_MEDIA: Found new media notification: key="
+                            + mMediaNotificationKey + " controller=" + mMediaController);
+                }
+            }
+
             if (controller != null && !sameSessions(mMediaController, controller)) {
                 // We have a new media session
                 clearCurrentMediaNotification();
@@ -183,13 +191,6 @@
                             + mMediaMetadata);
                 }
 
-                if (mediaNotification != null) {
-                    mMediaNotificationKey = mediaNotification.notification.getKey();
-                    if (DEBUG_MEDIA) {
-                        Log.v(TAG, "DEBUG_MEDIA: Found new media notification: key="
-                                + mMediaNotificationKey + " controller=" + mMediaController);
-                    }
-                }
                 metaDataChanged = true;
             }
         }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/car/CarNavigationButton.java b/packages/SystemUI/src/com/android/systemui/statusbar/car/CarNavigationButton.java
index e248db4..ec243fe 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/car/CarNavigationButton.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/car/CarNavigationButton.java
@@ -24,8 +24,8 @@
     private String mLongIntent;
     private boolean mBroadcastIntent;
     private boolean mSelected = false;
-    private float mSelectedAlpha;
-    private float mUnselectedAlpha;
+    private float mSelectedAlpha = 1f;
+    private float mUnselectedAlpha = 1f;
     private int mSelectedIconResourceId;
     private int mIconResourceId;
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java
index 3fb1137..008794c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java
@@ -418,8 +418,7 @@
                 Dependency.get(UserSwitcherController.class);
         if (userSwitcherController.useFullscreenUserSwitcher()) {
             mFullscreenUserSwitcher = new FullscreenUserSwitcher(this,
-                    userSwitcherController,
-                    mStatusBarWindow.findViewById(R.id.fullscreen_user_switcher_stub));
+                    mStatusBarWindow.findViewById(R.id.fullscreen_user_switcher_stub), mContext);
         } else {
             super.createUserSwitcher();
         }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/car/FullscreenUserSwitcher.java b/packages/SystemUI/src/com/android/systemui/statusbar/car/FullscreenUserSwitcher.java
index bc353f2..fb525f7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/car/FullscreenUserSwitcher.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/car/FullscreenUserSwitcher.java
@@ -18,14 +18,15 @@
 
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
-import android.content.res.Resources;
+import android.content.Context;
 import android.view.View;
 import android.view.ViewStub;
 import android.widget.ProgressBar;
 
+import android.support.v7.widget.GridLayoutManager;
+import android.support.v7.widget.RecyclerView;
 import com.android.systemui.R;
 import com.android.systemui.statusbar.phone.StatusBar;
-import com.android.systemui.statusbar.policy.UserSwitcherController;
 
 /**
  * Manages the fullscreen user switcher.
@@ -33,36 +34,25 @@
 public class FullscreenUserSwitcher {
     private final View mContainer;
     private final View mParent;
-    private final UserGridView mUserGridView;
-    private final UserSwitcherController mUserSwitcherController;
+    private final UserGridRecyclerView mUserGridView;
     private final ProgressBar mSwitchingUsers;
     private final int mShortAnimDuration;
 
     private boolean mShowing;
 
-    public FullscreenUserSwitcher(StatusBar statusBar,
-            UserSwitcherController userSwitcherController,
-            ViewStub containerStub) {
-        mUserSwitcherController = userSwitcherController;
+    public FullscreenUserSwitcher(StatusBar statusBar, ViewStub containerStub, Context context) {
         mParent = containerStub.inflate();
         mContainer = mParent.findViewById(R.id.container);
         mUserGridView = mContainer.findViewById(R.id.user_grid);
-        mUserGridView.init(statusBar, mUserSwitcherController, true /* overrideAlpha */);
-        mUserGridView.setUserSelectionListener(record -> {
-            if (!record.isCurrent) {
-                toggleSwitchInProgress(true);
-            }
-        });
+        mUserGridView.setStatusBar(statusBar);
+        GridLayoutManager layoutManager = new GridLayoutManager(context,
+                context.getResources().getInteger(R.integer.user_fullscreen_switcher_num_col));
+        mUserGridView.setLayoutManager(layoutManager);
+        mUserGridView.buildAdapter();
+        mUserGridView.setUserSelectionListener(record -> toggleSwitchInProgress(true));
 
-        PageIndicator pageIndicator = mContainer.findViewById(R.id.user_switcher_page_indicator);
-        pageIndicator.setupWithViewPager(mUserGridView);
-
-        Resources res = mContainer.getResources();
-        mShortAnimDuration = res.getInteger(android.R.integer.config_shortAnimTime);
-
-        mContainer.findViewById(R.id.start_driving).setOnClickListener(v -> {
-            automaticallySelectUser();
-        });
+        mShortAnimDuration = mContainer.getResources()
+            .getInteger(android.R.integer.config_shortAnimTime);
 
         mSwitchingUsers = mParent.findViewById(R.id.switching_users);
     }
@@ -115,10 +105,4 @@
         toggleSwitchInProgress(false);
         mParent.setVisibility(View.GONE);
     }
-
-    private void automaticallySelectUser() {
-        // TODO: Switch according to some policy. This implementation just tries to drop the
-        //       keyguard for the current user.
-        mUserGridView.showOfflineAuthUi();
-    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/car/UserGridRecyclerView.java b/packages/SystemUI/src/com/android/systemui/statusbar/car/UserGridRecyclerView.java
new file mode 100644
index 0000000..e09a360
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/car/UserGridRecyclerView.java
@@ -0,0 +1,360 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.systemui.statusbar.car;
+
+import android.app.ActivityManager;
+import android.content.Context;
+import android.content.pm.UserInfo;
+import android.content.res.Resources;
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.Paint;
+import android.graphics.Paint.Align;
+import android.graphics.drawable.GradientDrawable;
+import android.os.AsyncTask;
+import android.support.annotation.Nullable;
+import android.support.v7.widget.RecyclerView;
+import android.support.v7.widget.RecyclerView.ViewHolder;
+import android.util.AttributeSet;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ImageView;
+import android.widget.TextView;
+
+import com.android.settingslib.users.UserManagerHelper;
+import com.android.systemui.R;
+import com.android.systemui.qs.car.CarQSFragment;
+import com.android.systemui.statusbar.phone.StatusBar;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Displays a GridLayout with icons for the users in the system to allow switching between users.
+ * One of the uses of this is for the lock screen in auto.
+ */
+public class UserGridRecyclerView extends RecyclerView implements
+        UserManagerHelper.OnUsersUpdateListener {
+
+    private StatusBar mStatusBar;
+    private UserSelectionListener mUserSelectionListener;
+    private UserAdapter mAdapter;
+    private UserManagerHelper mUserManagerHelper;
+    private Context mContext;
+
+    public UserGridRecyclerView(Context context, AttributeSet attrs) {
+        super(context, attrs);
+        super.setHasFixedSize(true);
+        mContext = context;
+        mUserManagerHelper = new UserManagerHelper(mContext);
+    }
+
+    /**
+     * Register listener for any update to the users
+     */
+    @Override
+    public void onFinishInflate() {
+        mUserManagerHelper.registerOnUsersUpdateListener(this);
+    }
+
+    /**
+     * Unregisters listener checking for any change to the users
+     */
+    @Override
+    public void onDetachedFromWindow() {
+        mUserManagerHelper.unregisterOnUsersUpdateListener();
+    }
+
+    /**
+     * Initializes the adapter that populates the grid layout
+     *
+     * @return the adapter
+     */
+    public void buildAdapter() {
+        List<UserRecord> userRecords = createUserRecords(mUserManagerHelper
+                .getAllUsers());
+        mAdapter = new UserAdapter(mContext, userRecords);
+        super.setAdapter(mAdapter);
+    }
+
+    public void setStatusBar(@Nullable StatusBar statusBar) {
+        mStatusBar = statusBar;
+    }
+
+    private List<UserRecord> createUserRecords(List<UserInfo> userInfoList) {
+        List<UserRecord> userRecords = new ArrayList<>();
+        for (UserInfo userInfo : userInfoList) {
+            boolean isCurrent = false;
+            if (ActivityManager.getCurrentUser() == userInfo.id) {
+                isCurrent = true;
+            }
+            UserRecord record = new UserRecord(userInfo, false /* isGuest */,
+                    false /* isAddUser */, isCurrent);
+            userRecords.add(record);
+        }
+
+        // Add guest user record if the current user is not a guest
+        if (!mUserManagerHelper.isGuestUser()) {
+            userRecords.add(addGuestUserRecord());
+        }
+
+        // Add add user record if the current user can add users
+        if (mUserManagerHelper.canAddUsers()) {
+            userRecords.add(addUserRecord());
+        }
+
+        return userRecords;
+    }
+
+    /**
+     * Create guest user record
+     */
+    private UserRecord addGuestUserRecord() {
+        UserInfo userInfo = new UserInfo();
+        userInfo.name = mContext.getString(R.string.car_guest);
+        return new UserRecord(userInfo, true /* isGuest */,
+                false /* isAddUser */, false /* isCurrent */);
+    }
+
+    /**
+     * Create add user record
+     */
+    private UserRecord addUserRecord() {
+        UserInfo userInfo = new UserInfo();
+        userInfo.name = mContext.getString(R.string.car_add_user);
+        return new UserRecord(userInfo, false /* isGuest */,
+                true /* isAddUser */, false /* isCurrent */);
+    }
+
+    public void onUserSwitched(int newUserId) {
+        // Bring up security view after user switch is completed.
+        post(this::showOfflineAuthUi);
+    }
+
+    public void setUserSelectionListener(UserSelectionListener userSelectionListener) {
+        mUserSelectionListener = userSelectionListener;
+    }
+
+    void showOfflineAuthUi() {
+        // TODO: Show keyguard UI in-place.
+        if (mStatusBar != null) {
+            mStatusBar.executeRunnableDismissingKeyguard(null/* runnable */, null /* cancelAction */,
+                    true /* dismissShade */, true /* afterKeyguardGone */, true /* deferred */);
+        }
+    }
+
+    @Override
+    public void onUsersUpdate() {
+        mAdapter.clearUsers();
+        mAdapter.updateUsers(createUserRecords(mUserManagerHelper.getAllUsers()));
+        mAdapter.notifyDataSetChanged();
+    }
+
+    /**
+     * Adapter to populate the grid layout with the available user profiles
+     */
+    public final class UserAdapter extends RecyclerView.Adapter<UserAdapter.UserAdapterViewHolder> {
+
+        private final Context mContext;
+        private List<UserRecord> mUsers;
+        private final int mPodImageAvatarWidth;
+        private final int mPodImageAvatarHeight;
+        private final Resources mRes;
+        private final String mGuestName;
+        private final String mNewUserName;
+
+        public UserAdapter(Context context, List<UserRecord> users) {
+            mRes = context.getResources();
+            mContext = context;
+            updateUsers(users);
+            mPodImageAvatarWidth = mRes.getDimensionPixelSize(
+                    R.dimen.car_fullscreen_user_pod_image_avatar_width);
+            mPodImageAvatarHeight = mRes.getDimensionPixelSize(
+                    R.dimen.car_fullscreen_user_pod_image_avatar_height);
+            mGuestName = mRes.getString(R.string.car_guest);
+            mNewUserName = mRes.getString(R.string.car_new_user);
+        }
+
+        public void clearUsers() {
+            mUsers.clear();
+        }
+
+        public void updateUsers(List<UserRecord> users) {
+            mUsers = users;
+        }
+
+        @Override
+        public UserAdapterViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
+            View view = LayoutInflater.from(mContext)
+                    .inflate(R.layout.car_fullscreen_user_pod, parent, false);
+            view.setAlpha(1f);
+            view.bringToFront();
+            return new UserAdapterViewHolder(view);
+        }
+
+        @Override
+        public void onBindViewHolder(UserAdapterViewHolder holder, int position) {
+            UserRecord userRecord = mUsers.get(position);
+            holder.mUserAvatarImageView.setImageBitmap(getDefaultUserIcon(userRecord));
+            holder.mUserNameTextView.setText(userRecord.mInfo.name);
+            holder.mView.setOnClickListener(v -> {
+                if (userRecord == null) {
+                    return;
+                }
+
+                // Notify the listener which user was selected
+                if (mUserSelectionListener != null) {
+                    mUserSelectionListener.onUserSelected(userRecord);
+                }
+
+                // If the user selects Guest, switch to Guest profile
+                if (userRecord.mIsGuest) {
+                    mUserManagerHelper.switchToGuest(mGuestName);
+                    return;
+                }
+
+                // If the user wants to add a user, start task to add new user
+                if (userRecord.mIsAddUser) {
+                    new AddNewUserTask().execute(mNewUserName);
+                    return;
+                }
+
+                // If the user doesn't want to be a guest or add a user, switch to the user selected
+                mUserManagerHelper.switchToUser(userRecord.mInfo);
+            });
+
+        }
+
+        private class AddNewUserTask extends AsyncTask<String, Void, UserInfo> {
+
+            @Override
+            protected UserInfo doInBackground(String... userNames) {
+                return mUserManagerHelper.createNewUser(userNames[0]);
+            }
+
+            @Override
+            protected void onPreExecute() {
+            }
+
+            @Override
+            protected void onPostExecute(UserInfo user) {
+                if (user != null) {
+                    mUserManagerHelper.switchToUser(user);
+                }
+            }
+        }
+
+        @Override
+        public int getItemCount() {
+            return mUsers.size();
+        }
+
+        /**
+         * Returns the default user icon.  This icon is a circle with a letter in it.  The letter is
+         * the first character in the username.
+         *
+         * @param record the profile of the user for which the icon should be created
+         */
+        private Bitmap getDefaultUserIcon(UserRecord record) {
+            CharSequence displayText;
+            boolean isAddUserText = false;
+            if (record.mIsAddUser) {
+                displayText = "+";
+                isAddUserText = true;
+            } else {
+                displayText = record.mInfo.name.subSequence(0, 1);
+            }
+            Bitmap out = Bitmap.createBitmap(mPodImageAvatarWidth, mPodImageAvatarHeight,
+                    Bitmap.Config.ARGB_8888);
+            Canvas canvas = new Canvas(out);
+
+            // Draw the circle background.
+            GradientDrawable shape = new GradientDrawable();
+            shape.setShape(GradientDrawable.RADIAL_GRADIENT);
+            shape.setGradientRadius(1.0f);
+            shape.setColor(mContext.getColor(R.color.car_user_switcher_no_user_image_bgcolor));
+            shape.setBounds(0, 0, mPodImageAvatarWidth, mPodImageAvatarHeight);
+            shape.draw(canvas);
+
+            // Draw the letter in the center.
+            Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
+            paint.setColor(mContext.getColor(R.color.car_user_switcher_no_user_image_fgcolor));
+            paint.setTextAlign(Align.CENTER);
+            if (isAddUserText) {
+                paint.setTextSize(mRes.getDimensionPixelSize(
+                        R.dimen.car_touch_target_size));
+            } else {
+                paint.setTextSize(mRes.getDimensionPixelSize(
+                        R.dimen.car_fullscreen_user_pod_icon_text_size));
+            }
+
+            Paint.FontMetricsInt metrics = paint.getFontMetricsInt();
+            // The Y coordinate is measured by taking half the height of the pod, but that would
+            // draw the character putting the bottom of the font in the middle of the pod.  To
+            // correct this, half the difference between the top and bottom distance metrics of the
+            // font gives the offset of the font.  Bottom is a positive value, top is negative, so
+            // the different is actually a sum.  The "half" operation is then factored out.
+            canvas.drawText(displayText.toString(), mPodImageAvatarWidth / 2,
+                    (mPodImageAvatarHeight - (metrics.bottom + metrics.top)) / 2, paint);
+
+            return out;
+        }
+
+        public class UserAdapterViewHolder extends RecyclerView.ViewHolder {
+
+            public ImageView mUserAvatarImageView;
+            public TextView mUserNameTextView;
+            public View mView;
+
+            public UserAdapterViewHolder(View view) {
+                super(view);
+                mView = view;
+                mUserAvatarImageView = (ImageView) view.findViewById(R.id.user_avatar);
+                mUserNameTextView = (TextView) view.findViewById(R.id.user_name);
+            }
+        }
+    }
+
+    /**
+     * Object wrapper class for the userInfo.  Use it to distinguish if a profile is a
+     * guest profile, add user profile, or a current user.
+     */
+    public static final class UserRecord {
+
+        public final UserInfo mInfo;
+        public final boolean mIsGuest;
+        public final boolean mIsAddUser;
+        public final boolean mIsCurrent;
+
+        public UserRecord(UserInfo userInfo, boolean isGuest, boolean isAddUser,
+                boolean isCurrent) {
+            mInfo = userInfo;
+            mIsGuest = isGuest;
+            mIsAddUser = isAddUser;
+            mIsCurrent = isCurrent;
+        }
+    }
+
+    /**
+     * Listener used to notify when a user has been selected
+     */
+    interface UserSelectionListener {
+
+        void onUserSelected(UserRecord record);
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/car/UserGridView.java b/packages/SystemUI/src/com/android/systemui/statusbar/car/UserGridView.java
deleted file mode 100644
index 1bd820d..0000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/car/UserGridView.java
+++ /dev/null
@@ -1,364 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License
- */
-
-package com.android.systemui.statusbar.car;
-
-import android.content.Context;
-import android.content.res.Resources;
-import android.graphics.Bitmap;
-import android.graphics.Canvas;
-import android.graphics.Paint;
-import android.graphics.Paint.Align;
-import android.graphics.drawable.Drawable;
-import android.graphics.drawable.GradientDrawable;
-import android.support.v4.view.PagerAdapter;
-import android.support.v4.view.ViewPager;
-import android.util.AttributeSet;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.ImageView;
-import android.widget.TextView;
-
-import com.android.systemui.Dependency;
-import com.android.systemui.R;
-import com.android.systemui.qs.car.CarQSFragment;
-import com.android.systemui.statusbar.phone.StatusBar;
-import com.android.systemui.statusbar.policy.UserInfoController;
-import com.android.systemui.statusbar.policy.UserSwitcherController;
-
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.Comparator;
-import java.util.Vector;
-
-/**
- * Displays a ViewPager with icons for the users in the system to allow switching between users.
- * One of the uses of this is for the lock screen in auto.
- */
-public class UserGridView extends ViewPager implements
-        UserInfoController.OnUserInfoChangedListener {
-    private StatusBar mStatusBar;
-    private UserSwitcherController mUserSwitcherController;
-    private Adapter mAdapter;
-    private UserSelectionListener mUserSelectionListener;
-    private UserInfoController mUserInfoController;
-    private Vector mUserContainers;
-    private int mContainerWidth;
-    private boolean mOverrideAlpha;
-    private CarQSFragment.UserSwitchCallback mUserSwitchCallback;
-
-    public UserGridView(Context context, AttributeSet attrs) {
-        super(context, attrs);
-    }
-
-    public void init(StatusBar statusBar, UserSwitcherController userSwitcherController,
-            boolean overrideAlpha) {
-        mStatusBar = statusBar;
-        mUserSwitcherController = userSwitcherController;
-        mAdapter = new Adapter(mUserSwitcherController);
-        mUserInfoController = Dependency.get(UserInfoController.class);
-        mOverrideAlpha = overrideAlpha;
-        // Whenever the container width changes, the containers must be refreshed. Instead of
-        // doing an initial refreshContainers() to populate the containers, this listener will
-        // refresh them on layout change because that affects how the users are split into
-        // containers. Furthermore, at this point, the container width is unknown, so
-        // refreshContainers() cannot populate any containers.
-        addOnLayoutChangeListener(
-                (v, left, top, right, bottom, oldLeft, oldTop, oldRight, oldBottom) -> {
-                    int newWidth = Math.max(left - right, right - left);
-                    if (mContainerWidth != newWidth) {
-                        mContainerWidth = newWidth;
-                        refreshContainers();
-                    }
-                });
-    }
-
-    private void refreshContainers() {
-        mUserContainers = new Vector();
-
-        Context context = getContext();
-        LayoutInflater inflater = LayoutInflater.from(context);
-
-        for (int i = 0; i < mAdapter.getCount(); i++) {
-            ViewGroup pods = (ViewGroup) inflater.inflate(
-                    R.layout.car_fullscreen_user_pod_container, null);
-
-            int iconsPerPage = mAdapter.getIconsPerPage();
-            int limit = Math.min(mUserSwitcherController.getUsers().size(), (i + 1) * iconsPerPage);
-            for (int j = i * iconsPerPage; j < limit; j++) {
-                View v = mAdapter.makeUserPod(inflater, context, j, pods);
-                if (mOverrideAlpha) {
-                    v.setAlpha(1f);
-                }
-                pods.addView(v);
-                // This is hacky, but the dividers on the pod container LinearLayout don't seem
-                // to work for whatever reason.  Instead, set a right margin on the pod if it's not
-                // the right-most pod and there is more than one pod in the container.
-                if (i < limit - 1 && limit > 1) {
-                    ViewGroup.MarginLayoutParams params =
-                            (ViewGroup.MarginLayoutParams) v.getLayoutParams();
-                    params.setMargins(0, 0, getResources().getDimensionPixelSize(
-                            R.dimen.car_fullscreen_user_pod_margin_between), 0);
-                    v.setLayoutParams(params);
-                }
-            }
-            mUserContainers.add(pods);
-        }
-
-        mAdapter = new Adapter(mUserSwitcherController);
-        setAdapter(mAdapter);
-    }
-
-    @Override
-    public void onUserInfoChanged(String name, Drawable picture, String userAccount) {
-        refreshContainers();
-    }
-
-    public void setUserSwitchCallback(CarQSFragment.UserSwitchCallback callback) {
-        mUserSwitchCallback = callback;
-    }
-
-    public void onUserSwitched(int newUserId) {
-        // Bring up security view after user switch is completed.
-        post(this::showOfflineAuthUi);
-    }
-
-    public void setUserSelectionListener(UserSelectionListener userSelectionListener) {
-        mUserSelectionListener = userSelectionListener;
-    }
-
-    public void setListening(boolean listening) {
-        if (listening) {
-            mUserInfoController.addCallback(this);
-        } else {
-            mUserInfoController.removeCallback(this);
-        }
-    }
-
-    void showOfflineAuthUi() {
-        // TODO: Show keyguard UI in-place.
-        mStatusBar.executeRunnableDismissingKeyguard(null, null, true, true, true);
-    }
-
-    @Override
-    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
-        // Wrap content doesn't work in ViewPagers, so simulate the behavior in code.
-        int height = 0;
-        if (MeasureSpec.getMode(heightMeasureSpec) == MeasureSpec.EXACTLY) {
-            height = MeasureSpec.getSize(heightMeasureSpec);
-        } else {
-            for (int i = 0; i < getChildCount(); i++) {
-                View child = getChildAt(i);
-                child.measure(widthMeasureSpec,
-                        MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED));
-                height = Math.max(child.getMeasuredHeight(), height);
-            }
-
-            // Respect the AT_MOST request from parent.
-            if (MeasureSpec.getMode(heightMeasureSpec) == MeasureSpec.AT_MOST) {
-                height = Math.min(MeasureSpec.getSize(heightMeasureSpec), height);
-            }
-        }
-        heightMeasureSpec = MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY);
-
-        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
-    }
-
-    /**
-     * This is a ViewPager.PagerAdapter which deletegates the work to a
-     * UserSwitcherController.BaseUserAdapter. Java doesn't support multiple inheritance so we have
-     * to use composition instead to achieve the same goal since both the base classes are abstract
-     * classes and not interfaces.
-     */
-    private final class Adapter extends PagerAdapter {
-        private final int mPodWidth;
-        private final int mPodMarginBetween;
-        private final int mPodImageAvatarWidth;
-        private final int mPodImageAvatarHeight;
-
-        private final WrappedBaseUserAdapter mUserAdapter;
-
-        public Adapter(UserSwitcherController controller) {
-            super();
-            mUserAdapter = new WrappedBaseUserAdapter(controller, this);
-
-            Resources res = getResources();
-            mPodWidth = res.getDimensionPixelSize(R.dimen.car_fullscreen_user_pod_width);
-            mPodMarginBetween = res.getDimensionPixelSize(
-                    R.dimen.car_fullscreen_user_pod_margin_between);
-            mPodImageAvatarWidth = res.getDimensionPixelSize(
-                    R.dimen.car_fullscreen_user_pod_image_avatar_width);
-            mPodImageAvatarHeight = res.getDimensionPixelSize(
-                    R.dimen.car_fullscreen_user_pod_image_avatar_height);
-        }
-
-        @Override
-        public void destroyItem(ViewGroup container, int position, Object object) {
-            container.removeView((View) object);
-        }
-
-        private int getIconsPerPage() {
-            // We need to know how many pods we need in this page. Each pod has its own width and
-            // a margin between them. We can then divide the measured width of the parent by the
-            // sum of pod width and margin to get the number of pods that will completely fit.
-            // There is one less margin than the number of pods (eg. for 5 pods, there are 4
-            // margins), so need to add the margin to the measured width to account for that.
-            return (mContainerWidth + mPodMarginBetween) /
-                    (mPodWidth + mPodMarginBetween);
-        }
-
-        @Override
-        public void finishUpdate(ViewGroup container) {
-            if (mUserSwitchCallback != null) {
-                mUserSwitchCallback.resetShowing();
-            }
-        }
-
-        @Override
-        public Object instantiateItem(ViewGroup container, int position) {
-            if (position < mUserContainers.size()) {
-                container.addView((View) mUserContainers.get(position));
-                return mUserContainers.get(position);
-            } else {
-                return null;
-            }
-        }
-
-        /**
-         * Returns the default user icon.  This icon is a circle with a letter in it.  The letter is
-         * the first character in the username.
-         *
-         * @param userName the username of the user for which the icon is to be created
-         */
-        private Bitmap getDefaultUserIcon(CharSequence userName) {
-            CharSequence displayText = userName.subSequence(0, 1);
-            Bitmap out = Bitmap.createBitmap(mPodImageAvatarWidth, mPodImageAvatarHeight,
-                    Bitmap.Config.ARGB_8888);
-            Canvas canvas = new Canvas(out);
-
-            // Draw the circle background.
-            GradientDrawable shape = new GradientDrawable();
-            shape.setShape(GradientDrawable.RADIAL_GRADIENT);
-            shape.setGradientRadius(1.0f);
-            shape.setColor(getContext().getColor(R.color.car_user_switcher_no_user_image_bgcolor));
-            shape.setBounds(0, 0, mPodImageAvatarWidth, mPodImageAvatarHeight);
-            shape.draw(canvas);
-
-            // Draw the letter in the center.
-            Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
-            paint.setColor(getContext().getColor(R.color.car_user_switcher_no_user_image_fgcolor));
-            paint.setTextAlign(Align.CENTER);
-            paint.setTextSize(getResources().getDimensionPixelSize(
-                    R.dimen.car_fullscreen_user_pod_icon_text_size));
-            Paint.FontMetricsInt metrics = paint.getFontMetricsInt();
-            // The Y coordinate is measured by taking half the height of the pod, but that would
-            // draw the character putting the bottom of the font in the middle of the pod.  To
-            // correct this, half the difference between the top and bottom distance metrics of the
-            // font gives the offset of the font.  Bottom is a positive value, top is negative, so
-            // the different is actually a sum.  The "half" operation is then factored out.
-            canvas.drawText(displayText.toString(), mPodImageAvatarWidth / 2,
-                    (mPodImageAvatarHeight - (metrics.bottom + metrics.top)) / 2, paint);
-
-            return out;
-        }
-
-        private View makeUserPod(LayoutInflater inflater, Context context,
-                int position, ViewGroup parent) {
-            final UserSwitcherController.UserRecord record = mUserAdapter.getItem(position);
-            View view = inflater.inflate(R.layout.car_fullscreen_user_pod, parent, false);
-
-            TextView nameView = view.findViewById(R.id.user_name);
-            if (record != null) {
-                nameView.setText(mUserAdapter.getName(context, record));
-                view.setActivated(record.isCurrent);
-            } else {
-                nameView.setText(context.getString(R.string.unknown_user_label));
-            }
-
-            ImageView iconView = (ImageView) view.findViewById(R.id.user_avatar);
-            if (record == null || (record.picture == null && !record.isAddUser)) {
-                iconView.setImageBitmap(getDefaultUserIcon(nameView.getText()));
-            } else if (record.isAddUser) {
-                Drawable icon = context.getDrawable(R.drawable.ic_add_circle_qs);
-                icon.setTint(context.getColor(R.color.car_user_switcher_no_user_image_bgcolor));
-                iconView.setImageDrawable(icon);
-            } else {
-                iconView.setImageBitmap(record.picture);
-            }
-
-            iconView.setOnClickListener(v -> {
-                if (record == null) {
-                    return;
-                }
-
-                if (mUserSelectionListener != null) {
-                    mUserSelectionListener.onUserSelected(record);
-                }
-
-                if (record.isCurrent) {
-                    showOfflineAuthUi();
-                } else {
-                    mUserSwitcherController.switchTo(record);
-                }
-            });
-
-            return view;
-        }
-
-        @Override
-        public int getCount() {
-            int iconsPerPage = getIconsPerPage();
-            if (iconsPerPage == 0) {
-                return 0;
-            }
-            return (int) Math.ceil((double) mUserAdapter.getCount() / getIconsPerPage());
-        }
-
-        public void refresh() {
-            mUserAdapter.refresh();
-        }
-
-        @Override
-        public boolean isViewFromObject(View view, Object object) {
-            return view == object;
-        }
-    }
-
-    private final class WrappedBaseUserAdapter extends UserSwitcherController.BaseUserAdapter {
-        private final Adapter mContainer;
-
-        public WrappedBaseUserAdapter(UserSwitcherController controller, Adapter container) {
-            super(controller);
-            mContainer = container;
-        }
-
-        @Override
-        public View getView(int position, View convertView, ViewGroup parent) {
-            throw new UnsupportedOperationException("unused");
-        }
-
-        @Override
-        public void notifyDataSetChanged() {
-            super.notifyDataSetChanged();
-            mContainer.notifyDataSetChanged();
-        }
-    }
-
-    interface UserSelectionListener {
-        void onUserSelected(UserSwitcherController.UserRecord record);
-    };
-}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/ActivityLaunchAnimator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/ActivityLaunchAnimator.java
index 3bbfe3c..b8bce95 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/ActivityLaunchAnimator.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/ActivityLaunchAnimator.java
@@ -238,6 +238,7 @@
                 t.deferTransactionUntilSurface(app.leash, systemUiSurface,
                         systemUiSurface.getNextFrameNumber());
             }
+            t.setEarlyWakeup();
             t.apply();
         }
 
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 3dbac51..2c335a3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
@@ -196,6 +196,9 @@
         }
         mState = ScrimState.UNINITIALIZED;
 
+        mScrimBehind.setDefaultFocusHighlightEnabled(false);
+        mScrimInFront.setDefaultFocusHighlightEnabled(false);
+
         updateScrims();
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java
index d38c083..bbdaa99 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java
@@ -112,9 +112,9 @@
             mCurrentInFrontTint = Color.BLACK;
             mCurrentBehindTint = Color.BLACK;
             mAnimationDuration = ScrimController.ANIMATION_DURATION_LONG;
-            // DisplayPowerManager will blank the screen for us, we just need
-            // to set our state.
-            mAnimateChange = !mDisplayRequiresBlanking;
+            // DisplayPowerManager may blank the screen for us,
+            // in this case we just need to set our state.
+            mAnimateChange = mDozeParameters.shouldControlScreenOff();
         }
 
         @Override
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 7987bfd..aa9bbe08 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -3899,7 +3899,11 @@
     }
 
     public boolean onBackPressed() {
-        if (mStatusBarKeyguardViewManager.onBackPressed()) {
+        boolean isScrimmedBouncer = mScrimController.getState() == ScrimState.BOUNCER_SCRIMMED;
+        if (mStatusBarKeyguardViewManager.onBackPressed(isScrimmedBouncer /* hideImmediately */)) {
+            if (!isScrimmedBouncer) {
+                mNotificationPanel.expandWithoutQs();
+            }
             return true;
         }
         if (mNotificationPanel.isQsExpanded()) {
@@ -4994,6 +4998,14 @@
 
     @Override
     public void onNotificationClicked(StatusBarNotification sbn, ExpandableNotificationRow row) {
+        RemoteInputController controller = mRemoteInputManager.getController();
+        if (controller.isRemoteInputActive(row.getEntry())
+                && !TextUtils.isEmpty(row.getActiveRemoteInputText())) {
+            // We have an active remote input typed and the user clicked on the notification.
+            // this was probably unintentional, so we're closing the edit text instead.
+            controller.closeRemoteInputs();
+            return;
+        }
         Notification notification = sbn.getNotification();
         final PendingIntent intent = notification.contentIntent != null
                 ? notification.contentIntent
@@ -5057,12 +5069,7 @@
                     Intent fillInIntent = null;
                     Entry entry = row.getEntry();
                     CharSequence remoteInputText = null;
-                    RemoteInputController controller = mRemoteInputManager.getController();
-                    if (controller.isRemoteInputActive(entry)) {
-                        remoteInputText = row.getActiveRemoteInputText();
-                    }
-                    if (TextUtils.isEmpty(remoteInputText)
-                            && !TextUtils.isEmpty(entry.remoteInputText)) {
+                    if (!TextUtils.isEmpty(entry.remoteInputText)) {
                         remoteInputText = entry.remoteInputText;
                     }
                     if (!TextUtils.isEmpty(remoteInputText)
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 2727b30..670c68f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
@@ -512,12 +512,15 @@
     /**
      * Notifies this manager that the back button has been pressed.
      *
+     * @param hideImmediately Hide bouncer when {@code true}, keep it around otherwise.
+     *                        Non-scrimmed bouncers have a special animation tied to the expansion
+     *                        of the notification panel.
      * @return whether the back press has been handled
      */
-    public boolean onBackPressed() {
+    public boolean onBackPressed(boolean hideImmediately) {
         if (mBouncer.isShowing()) {
             mStatusBar.endAffordanceLaunch();
-            reset(true /* hideBouncerWhenShowing */);
+            reset(hideImmediately);
             return true;
         }
         return false;
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
index 13157fe..a2d2615 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
@@ -212,7 +212,6 @@
                     .setDuration(300)
                     .setInterpolator(new SystemUIInterpolators.LogDecelerateInterpolator())
                     .withEndAction(() -> {
-                        mWindow.getDecorView().requestAccessibilityFocus();
                         if (!Prefs.getBoolean(mContext, Prefs.Key.TOUCHED_RINGER_TOGGLE, false)) {
                             mRingerIcon.postOnAnimationDelayed(mSinglePress, 1500);
                         }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java
index 1e8e14d..82129a1 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java
@@ -246,7 +246,7 @@
     }
 
     @Test
-    public void scrimBlanksBeforeLeavingAoD() {
+    public void scrimBlanksBeforeLeavingAod() {
         // Simulate unlock with fingerprint
         mScrimController.transitionTo(ScrimState.AOD);
         mScrimController.finishAnimationsImmediately();
@@ -455,6 +455,28 @@
         }
     }
 
+    @Test
+    public void testAnimatesTransitionToAod() {
+        when(mDozeParamenters.shouldControlScreenOff()).thenReturn(false);
+        ScrimState.AOD.prepare(ScrimState.KEYGUARD);
+        Assert.assertFalse("No animation when ColorFade kicks in",
+                ScrimState.AOD.getAnimateChange());
+
+        reset(mDozeParamenters);
+        when(mDozeParamenters.shouldControlScreenOff()).thenReturn(true);
+        ScrimState.AOD.prepare(ScrimState.KEYGUARD);
+        Assert.assertTrue("Animate scrims when ColorFade won't be triggered",
+                ScrimState.AOD.getAnimateChange());
+    }
+
+    @Test
+    public void testViewsDontHaveFocusHighlight() {
+        Assert.assertFalse("Scrim shouldn't have focus highlight",
+                mScrimInFront.getDefaultFocusHighlightEnabled());
+        Assert.assertFalse("Scrim shouldn't have focus highlight",
+                mScrimBehind.getDefaultFocusHighlightEnabled());
+    }
+
     /**
      * Conserves old notification density after leaving state and coming back.
      *
diff --git a/proto/src/metrics_constants.proto b/proto/src/metrics_constants.proto
index 0ee026e..739b716 100644
--- a/proto/src/metrics_constants.proto
+++ b/proto/src/metrics_constants.proto
@@ -5578,6 +5578,11 @@
     // OS: P
     WIFI_SCANNING_NEEDED_DIALOG = 1373;
 
+    // OPEN: Settings > System > Gestures > Swipe up gesture
+    // CATEGORY: SETTINGS
+    // OS: P
+    SETTINGS_GESTURE_SWIPE_UP = 1374;
+
     // ---- End P Constants, all P constants go above this line ----
 
     // First Q constant in master goes here:
diff --git a/proto/src/wifi.proto b/proto/src/wifi.proto
index e05416e..595a1d9 100644
--- a/proto/src/wifi.proto
+++ b/proto/src/wifi.proto
@@ -422,6 +422,16 @@
 
   // Indicates the number of times a scan request from an external background app was throttled.
   optional int32 num_external_background_app_oneshot_scan_requests_throttled = 106;
+
+  // WifiLastResortWatchdog time milliseconds delta between trigger and first connection success
+  optional int64 watchdog_trigger_to_connection_success_duration_ms = 107 [default = -1];
+
+  // The number of times wifi experienced failures after watchdog has already been triggered and is
+  // waiting for a connection success
+  optional int64 watchdog_total_connection_failure_count_after_trigger = 108;
+
+  // Number of times DFS channel scans are requested in single scan requests.
+  optional int32 num_oneshot_has_dfs_channel_scans = 109;
 }
 
 // Information that gets logged for every WiFi connection.
diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java
index 3dc2323..655dbef 100644
--- a/services/autofill/java/com/android/server/autofill/Session.java
+++ b/services/autofill/java/com/android/server/autofill/Session.java
@@ -99,6 +99,7 @@
 import java.util.Collections;
 import java.util.List;
 import java.util.Map;
+import java.util.Objects;
 import java.util.concurrent.atomic.AtomicInteger;
 
 /**
@@ -1971,8 +1972,8 @@
                     return;
                 }
 
-                if (value != null && !value.equals(viewState.getCurrentValue())) {
-                    if (value.isEmpty()
+                if (!Objects.equals(value, viewState.getCurrentValue())) {
+                    if ((value == null || value.isEmpty())
                             && viewState.getCurrentValue() != null
                             && viewState.getCurrentValue().isText()
                             && viewState.getCurrentValue().getTextValue() != null
@@ -1993,18 +1994,26 @@
                     // Must check if this update was caused by autofilling the view, in which
                     // case we just update the value, but not the UI.
                     final AutofillValue filledValue = viewState.getAutofilledValue();
-                    if (value.equals(filledValue)) {
+                    if (filledValue != null && filledValue.equals(value)) {
+                        if (sVerbose) {
+                            Slog.v(TAG, "ignoring autofilled change on id " + id);
+                        }
                         return;
                     }
                     // Update the internal state...
                     viewState.setState(ViewState.STATE_CHANGED);
 
                     //..and the UI
-                    if (value.isText()) {
-                        getUiForShowing().filterFillUi(value.getTextValue().toString(), this);
+                    final String filterText;
+                    if (value == null || !value.isText()) {
+                        filterText = null;
                     } else {
-                        getUiForShowing().filterFillUi(null, this);
+                        final CharSequence text = value.getTextValue();
+                        // Text should never be null, but it doesn't hurt to check to avoid a
+                        // system crash...
+                        filterText = (text == null) ? null : text.toString();
                     }
+                    getUiForShowing().filterFillUi(filterText, this);
                 }
                 break;
             case ACTION_VIEW_ENTERED:
diff --git a/services/core/java/com/android/server/InputMethodManagerService.java b/services/core/java/com/android/server/InputMethodManagerService.java
index f678eed..325dabe 100644
--- a/services/core/java/com/android/server/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/InputMethodManagerService.java
@@ -1404,7 +1404,7 @@
 
     private void resetDefaultImeLocked(Context context) {
         // Do not reset the default (current) IME when it is a 3rd-party IME
-        if (mCurMethodId != null && !InputMethodUtils.isSystemIme(mMethodMap.get(mCurMethodId))) {
+        if (mCurMethodId != null && !mMethodMap.get(mCurMethodId).isSystem()) {
             return;
         }
         final List<InputMethodInfo> suitableImes = InputMethodUtils.getDefaultEnabledImes(
@@ -3068,7 +3068,7 @@
                             : mCurrentSubtype.getLocale();
                     for (int i = 0; i < N; ++i) {
                         final InputMethodInfo imi = enabled.get(i);
-                        if (imi.getSubtypeCount() > 0 && InputMethodUtils.isSystemIme(imi)) {
+                        if (imi.getSubtypeCount() > 0 && imi.isSystem()) {
                             InputMethodSubtype keyboardSubtype =
                                     InputMethodUtils.findLastResortApplicableSubtypeLocked(mRes,
                                             InputMethodUtils.getSubtypes(imi),
diff --git a/services/core/java/com/android/server/am/RecentTasks.java b/services/core/java/com/android/server/am/RecentTasks.java
index 1d305fb..365c436 100644
--- a/services/core/java/com/android/server/am/RecentTasks.java
+++ b/services/core/java/com/android/server/am/RecentTasks.java
@@ -1255,7 +1255,7 @@
         for (int i = 0; i < recentsCount; i++) {
             final TaskRecord tr = mTasks.get(i);
             if (task != tr) {
-                if (!task.hasCompatibleActivityType(tr)) {
+                if (!task.hasCompatibleActivityType(tr) || task.userId != tr.userId) {
                     continue;
                 }
                 final Intent trIntent = tr.intent;
diff --git a/services/core/java/com/android/server/content/ContentService.java b/services/core/java/com/android/server/content/ContentService.java
index b7bbb3b..e3d0bdd 100644
--- a/services/core/java/com/android/server/content/ContentService.java
+++ b/services/core/java/com/android/server/content/ContentService.java
@@ -79,7 +79,7 @@
  */
 public final class ContentService extends IContentService.Stub {
     static final String TAG = "ContentService";
-    static final boolean DEBUG = true;
+    static final boolean DEBUG = false;
 
     public static class Lifecycle extends SystemService {
         private ContentService mService;
diff --git a/services/core/java/com/android/server/job/JobSchedulerService.java b/services/core/java/com/android/server/job/JobSchedulerService.java
index 66817fa..736aa46 100644
--- a/services/core/java/com/android/server/job/JobSchedulerService.java
+++ b/services/core/java/com/android/server/job/JobSchedulerService.java
@@ -127,7 +127,7 @@
  * Any function with the suffix 'Locked' also needs to lock on {@link #mJobs}.
  * @hide
  */
-public final class JobSchedulerService extends com.android.server.SystemService
+public class JobSchedulerService extends com.android.server.SystemService
         implements StateChangedListener, JobCompletedListener {
     public static final String TAG = "JobScheduler";
     public static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
@@ -781,6 +781,10 @@
         }
     };
 
+    public Context getTestableContext() {
+        return getContext();
+    }
+
     public Object getLock() {
         return mLock;
     }
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 8365fd2..0c66c5b 100644
--- a/services/core/java/com/android/server/job/controllers/ConnectivityController.java
+++ b/services/core/java/com/android/server/job/controllers/ConnectivityController.java
@@ -30,12 +30,12 @@
 import android.net.NetworkPolicyManager;
 import android.net.NetworkRequest;
 import android.net.TrafficStats;
-import android.os.Process;
 import android.os.UserHandle;
 import android.text.format.DateUtils;
 import android.util.ArraySet;
 import android.util.Log;
 import android.util.Slog;
+import android.util.SparseArray;
 import android.util.proto.ProtoOutputStream;
 
 import com.android.internal.annotations.GuardedBy;
@@ -46,6 +46,7 @@
 import com.android.server.job.JobServiceContext;
 import com.android.server.job.StateControllerProto;
 
+import java.util.Objects;
 import java.util.function.Predicate;
 
 /**
@@ -63,7 +64,6 @@
 
     private final ConnectivityManager mConnManager;
     private final NetworkPolicyManager mNetPolicyManager;
-    private boolean mConnected;
 
     @GuardedBy("mLock")
     private final ArraySet<JobStatus> mTrackedJobs = new ArraySet<>();
@@ -74,9 +74,11 @@
         mConnManager = mContext.getSystemService(ConnectivityManager.class);
         mNetPolicyManager = mContext.getSystemService(NetworkPolicyManager.class);
 
-        mConnected = false;
+        // We're interested in all network changes; internally we match these
+        // network changes against the active network for each UID with jobs.
+        final NetworkRequest request = new NetworkRequest.Builder().clearCapabilities().build();
+        mConnManager.registerNetworkCallback(request, mNetworkCallback);
 
-        mConnManager.registerDefaultNetworkCallback(mNetworkCallback);
         mNetPolicyManager.registerListener(mNetPolicyListener);
     }
 
@@ -198,14 +200,18 @@
     }
 
     private boolean updateConstraintsSatisfied(JobStatus jobStatus) {
+        final Network network = mConnManager.getActiveNetworkForUid(jobStatus.getSourceUid());
+        final NetworkCapabilities capabilities = mConnManager.getNetworkCapabilities(network);
+        return updateConstraintsSatisfied(jobStatus, network, capabilities);
+    }
+
+    private boolean updateConstraintsSatisfied(JobStatus jobStatus, Network network,
+            NetworkCapabilities capabilities) {
         // TODO: consider matching against non-active networks
 
-        final int jobUid = jobStatus.getSourceUid();
         final boolean ignoreBlocked = (jobStatus.getFlags() & JobInfo.FLAG_WILL_BE_FOREGROUND) != 0;
-
-        final Network network = mConnManager.getActiveNetworkForUid(jobUid, ignoreBlocked);
-        final NetworkInfo info = mConnManager.getNetworkInfoForUid(network, jobUid, ignoreBlocked);
-        final NetworkCapabilities capabilities = mConnManager.getNetworkCapabilities(network);
+        final NetworkInfo info = mConnManager.getNetworkInfoForUid(network,
+                jobStatus.getSourceUid(), ignoreBlocked);
 
         final boolean connected = (info != null) && info.isConnected();
         final boolean satisfied = isSatisfied(jobStatus, network, capabilities, mConstants);
@@ -218,12 +224,6 @@
         // using non-default routes.
         jobStatus.network = network;
 
-        // Track system-uid connected/validated as a general reportable proxy for the
-        // overall state of connectivity constraint satisfiability.
-        if (jobUid == Process.SYSTEM_UID) {
-            mConnected = connected;
-        }
-
         if (DEBUG) {
             Slog.i(TAG, "Connectivity " + (changed ? "CHANGED" : "unchanged")
                     + " for " + jobStatus + ": connected=" + connected
@@ -233,18 +233,48 @@
     }
 
     /**
-     * Update all jobs tracked by this controller.
+     * Update any jobs tracked by this controller that match given filters.
      *
-     * @param uid only update jobs belonging to this UID, or {@code -1} to
+     * @param filterUid only update jobs belonging to this UID, or {@code -1} to
      *            update all tracked jobs.
+     * @param filterNetwork only update jobs that would use this
+     *            {@link Network}, or {@code null} to update all tracked jobs.
      */
-    private void updateTrackedJobs(int uid) {
+    private void updateTrackedJobs(int filterUid, Network filterNetwork) {
         synchronized (mLock) {
+            // Since this is a really hot codepath, temporarily cache any
+            // answers that we get from ConnectivityManager.
+            final SparseArray<Network> uidToNetwork = new SparseArray<>();
+            final SparseArray<NetworkCapabilities> networkToCapabilities = new SparseArray<>();
+
             boolean changed = false;
-            for (int i = mTrackedJobs.size()-1; i >= 0; i--) {
+            for (int i = mTrackedJobs.size() - 1; i >= 0; i--) {
                 final JobStatus js = mTrackedJobs.valueAt(i);
-                if (uid == -1 || uid == js.getSourceUid()) {
-                    changed |= updateConstraintsSatisfied(js);
+                final int uid = js.getSourceUid();
+
+                final boolean uidMatch = (filterUid == -1 || filterUid == uid);
+                if (uidMatch) {
+                    Network network = uidToNetwork.get(uid);
+                    if (network == null) {
+                        network = mConnManager.getActiveNetworkForUid(uid);
+                        uidToNetwork.put(uid, network);
+                    }
+
+                    // Update either when we have a network match, or when the
+                    // job hasn't yet been evaluated against the currently
+                    // active network; typically when we just lost a network.
+                    final boolean networkMatch = (filterNetwork == null
+                            || Objects.equals(filterNetwork, network));
+                    final boolean forceUpdate = !Objects.equals(js.network, network);
+                    if (networkMatch || forceUpdate) {
+                        final int netId = network != null ? network.netId : -1;
+                        NetworkCapabilities capabilities = networkToCapabilities.get(netId);
+                        if (capabilities == null) {
+                            capabilities = mConnManager.getNetworkCapabilities(network);
+                            networkToCapabilities.put(netId, capabilities);
+                        }
+                        changed |= updateConstraintsSatisfied(js, network, capabilities);
+                    }
                 }
             }
             if (changed) {
@@ -273,19 +303,19 @@
 
     private final NetworkCallback mNetworkCallback = new NetworkCallback() {
         @Override
-        public void onCapabilitiesChanged(Network network, NetworkCapabilities networkCapabilities) {
+        public void onCapabilitiesChanged(Network network, NetworkCapabilities capabilities) {
             if (DEBUG) {
-                Slog.v(TAG, "onCapabilitiesChanged() : " + networkCapabilities);
+                Slog.v(TAG, "onCapabilitiesChanged: " + network);
             }
-            updateTrackedJobs(-1);
+            updateTrackedJobs(-1, network);
         }
 
         @Override
         public void onLost(Network network) {
             if (DEBUG) {
-                Slog.v(TAG, "Network lost");
+                Slog.v(TAG, "onLost: " + network);
             }
-            updateTrackedJobs(-1);
+            updateTrackedJobs(-1, network);
         }
     };
 
@@ -293,25 +323,9 @@
         @Override
         public void onUidRulesChanged(int uid, int uidRules) {
             if (DEBUG) {
-                Slog.v(TAG, "Uid rules changed for " + uid);
+                Slog.v(TAG, "onUidRulesChanged: " + uid);
             }
-            updateTrackedJobs(uid);
-        }
-
-        @Override
-        public void onRestrictBackgroundChanged(boolean restrictBackground) {
-            if (DEBUG) {
-                Slog.v(TAG, "Background restriction change to " + restrictBackground);
-            }
-            updateTrackedJobs(-1);
-        }
-
-        @Override
-        public void onUidPoliciesChanged(int uid, int uidPolicies) {
-            if (DEBUG) {
-                Slog.v(TAG, "Uid policy changed for " + uid);
-            }
-            updateTrackedJobs(uid);
+            updateTrackedJobs(uid, null);
         }
     };
 
@@ -319,9 +333,6 @@
     @Override
     public void dumpControllerStateLocked(IndentingPrintWriter pw,
             Predicate<JobStatus> predicate) {
-        pw.println("System connected: " + mConnected);
-        pw.println();
-
         for (int i = 0; i < mTrackedJobs.size(); i++) {
             final JobStatus js = mTrackedJobs.valueAt(i);
             if (predicate.test(js)) {
@@ -343,8 +354,6 @@
         final long token = proto.start(fieldId);
         final long mToken = proto.start(StateControllerProto.CONNECTIVITY);
 
-        proto.write(StateControllerProto.ConnectivityController.IS_CONNECTED, mConnected);
-
         for (int i = 0; i < mTrackedJobs.size(); i++) {
             final JobStatus js = mTrackedJobs.valueAt(i);
             if (!predicate.test(js)) {
diff --git a/services/core/java/com/android/server/job/controllers/StateController.java b/services/core/java/com/android/server/job/controllers/StateController.java
index 495109d..c2be283 100644
--- a/services/core/java/com/android/server/job/controllers/StateController.java
+++ b/services/core/java/com/android/server/job/controllers/StateController.java
@@ -41,7 +41,7 @@
     StateController(JobSchedulerService service) {
         mService = service;
         mStateChangedListener = service;
-        mContext = service.getContext();
+        mContext = service.getTestableContext();
         mLock = service.getLock();
         mConstants = service.getConstants();
     }
diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
index 224d085..1782ae5 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
@@ -70,6 +70,12 @@
 import static android.net.NetworkTemplate.MATCH_WIFI;
 import static android.net.NetworkTemplate.buildTemplateMobileAll;
 import static android.net.TrafficStats.MB_IN_BYTES;
+import static android.provider.Settings.Global.NETPOLICY_OVERRIDE_ENABLED;
+import static android.provider.Settings.Global.NETPOLICY_QUOTA_ENABLED;
+import static android.provider.Settings.Global.NETPOLICY_QUOTA_FRAC_JOBS;
+import static android.provider.Settings.Global.NETPOLICY_QUOTA_FRAC_MULTIPATH;
+import static android.provider.Settings.Global.NETPOLICY_QUOTA_LIMITED;
+import static android.provider.Settings.Global.NETPOLICY_QUOTA_UNLIMITED;
 import static android.telephony.CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED;
 import static android.telephony.CarrierConfigManager.DATA_CYCLE_THRESHOLD_DISABLED;
 import static android.telephony.CarrierConfigManager.DATA_CYCLE_USE_PLATFORM_DEFAULT;
@@ -115,6 +121,7 @@
 import android.app.usage.UsageStatsManagerInternal;
 import android.content.BroadcastReceiver;
 import android.content.ComponentName;
+import android.content.ContentResolver;
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
@@ -351,6 +358,11 @@
      */
     private static final long WAIT_FOR_ADMIN_DATA_TIMEOUT_MS = 10_000;
 
+    private static final long QUOTA_UNLIMITED_DEFAULT = DataUnit.MEBIBYTES.toBytes(20);
+    private static final float QUOTA_LIMITED_DEFAULT = 0.1f;
+    private static final float QUOTA_FRAC_JOBS_DEFAULT = 0.5f;
+    private static final float QUOTA_FRAC_MULTIPATH_DEFAULT = 0.5f;
+
     private static final int MSG_RULES_CHANGED = 1;
     private static final int MSG_METERED_IFACES_CHANGED = 2;
     private static final int MSG_LIMIT_REACHED = 5;
@@ -1739,10 +1751,18 @@
         }
         mMeteredIfaces = newMeteredIfaces;
 
+        final ContentResolver cr = mContext.getContentResolver();
+        final boolean quotaEnabled = Settings.Global.getInt(cr,
+                NETPOLICY_QUOTA_ENABLED, 1) != 0;
+        final long quotaUnlimited = Settings.Global.getLong(cr,
+                NETPOLICY_QUOTA_UNLIMITED, QUOTA_UNLIMITED_DEFAULT);
+        final float quotaLimited = Settings.Global.getFloat(cr,
+                NETPOLICY_QUOTA_LIMITED, QUOTA_LIMITED_DEFAULT);
+
         // Finally, calculate our opportunistic quotas
-        // TODO: add experiments support to disable or tweak ratios
         mSubscriptionOpportunisticQuota.clear();
         for (NetworkState state : states) {
+            if (!quotaEnabled) continue;
             if (state.network == null) continue;
             final int subId = getSubIdLocked(state.network);
             final SubscriptionPlan plan = getPrimarySubscriptionPlanLocked(subId);
@@ -1754,7 +1774,7 @@
                 quotaBytes = OPPORTUNISTIC_QUOTA_UNKNOWN;
             } else if (limitBytes == SubscriptionPlan.BYTES_UNLIMITED) {
                 // Unlimited data; let's use 20MiB/day (600MiB/month)
-                quotaBytes = DataUnit.MEBIBYTES.toBytes(20);
+                quotaBytes = quotaUnlimited;
             } else {
                 // Limited data; let's only use 10% of remaining budget
                 final Pair<ZonedDateTime, ZonedDateTime> cycle = plan.cycleIterator().next();
@@ -1772,7 +1792,7 @@
                 final long remainingDays =
                         1 + ((end - now.toEpochMilli() - 1) / TimeUnit.DAYS.toMillis(1));
 
-                quotaBytes = Math.max(0, (remainingBytes / remainingDays) / 10);
+                quotaBytes = Math.max(0, (long) ((remainingBytes / remainingDays) * quotaLimited));
             }
 
             mSubscriptionOpportunisticQuota.put(subId, quotaBytes);
@@ -3048,11 +3068,17 @@
             }
         }
 
-        mHandler.sendMessage(mHandler.obtainMessage(MSG_SUBSCRIPTION_OVERRIDE,
-                overrideMask, overrideValue, subId));
-        if (timeoutMillis > 0) {
-            mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_SUBSCRIPTION_OVERRIDE,
-                    overrideMask, 0, subId), timeoutMillis);
+        // Only allow overrides when feature is enabled. However, we always
+        // allow disabling of overrides for safety reasons.
+        final boolean overrideEnabled = Settings.Global.getInt(mContext.getContentResolver(),
+                NETPOLICY_OVERRIDE_ENABLED, 1) != 0;
+        if (overrideEnabled || overrideValue == 0) {
+            mHandler.sendMessage(mHandler.obtainMessage(MSG_SUBSCRIPTION_OVERRIDE,
+                    overrideMask, overrideValue, subId));
+            if (timeoutMillis > 0) {
+                mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_SUBSCRIPTION_OVERRIDE,
+                        overrideMask, 0, subId), timeoutMillis);
+            }
         }
     }
 
@@ -4695,11 +4721,24 @@
 
         @Override
         public long getSubscriptionOpportunisticQuota(Network network, int quotaType) {
+            final long quotaBytes;
             synchronized (mNetworkPoliciesSecondLock) {
-                // TODO: handle splitting quota between use-cases
-                return mSubscriptionOpportunisticQuota.get(getSubIdLocked(network),
+                quotaBytes = mSubscriptionOpportunisticQuota.get(getSubIdLocked(network),
                         OPPORTUNISTIC_QUOTA_UNKNOWN);
             }
+            if (quotaBytes == OPPORTUNISTIC_QUOTA_UNKNOWN) {
+                return OPPORTUNISTIC_QUOTA_UNKNOWN;
+            }
+
+            if (quotaType == QUOTA_TYPE_JOBS) {
+                return (long) (quotaBytes * Settings.Global.getFloat(mContext.getContentResolver(),
+                        NETPOLICY_QUOTA_FRAC_JOBS, QUOTA_FRAC_JOBS_DEFAULT));
+            } else if (quotaType == QUOTA_TYPE_MULTIPATH) {
+                return (long) (quotaBytes * Settings.Global.getFloat(mContext.getContentResolver(),
+                        NETPOLICY_QUOTA_FRAC_MULTIPATH, QUOTA_FRAC_MULTIPATH_DEFAULT));
+            } else {
+                return OPPORTUNISTIC_QUOTA_UNKNOWN;
+            }
         }
 
         @Override
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index cf7dfd0..79f80e6 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -618,6 +618,7 @@
     boolean mTranslucentDecorEnabled = true;
     boolean mUseTvRouting;
     int mVeryLongPressTimeout;
+    boolean mAllowStartActivityForLongPressOnPowerDuringSetup;
 
     private boolean mHandleVolumeKeysInWM;
 
@@ -1622,7 +1623,11 @@
                     : mKeyguardDelegate.isShowing();
             if (!keyguardActive) {
                 Intent intent = new Intent(Intent.ACTION_VOICE_ASSIST);
-                startActivityAsUser(intent, UserHandle.CURRENT_OR_SELF);
+                if (mAllowStartActivityForLongPressOnPowerDuringSetup) {
+                    mContext.startActivityAsUser(intent, UserHandle.CURRENT_OR_SELF);
+                } else {
+                    startActivityAsUser(intent, UserHandle.CURRENT_OR_SELF);
+                }
             }
             break;
         }
@@ -2134,6 +2139,8 @@
                 com.android.internal.R.integer.config_shortPressOnSleepBehavior);
         mVeryLongPressTimeout = mContext.getResources().getInteger(
                 com.android.internal.R.integer.config_veryLongPressTimeout);
+        mAllowStartActivityForLongPressOnPowerDuringSetup = mContext.getResources().getBoolean(
+                com.android.internal.R.bool.config_allowStartActivityForLongPressOnPowerInSetup);
 
         mUseTvRouting = AudioSystem.getPlatformType(mContext) == AudioSystem.PLATFORM_TELEVISION;
 
@@ -8661,6 +8668,9 @@
                 pw.print("mShortPressOnWindowBehavior=");
                 pw.println(shortPressOnWindowBehaviorToString(mShortPressOnWindowBehavior));
         pw.print(prefix);
+                pw.print("mAllowStartActivityForLongPressOnPowerDuringSetup=");
+                pw.println(mAllowStartActivityForLongPressOnPowerDuringSetup);
+        pw.print(prefix);
                 pw.print("mHasSoftInput="); pw.print(mHasSoftInput);
                 pw.print(" mDismissImeOnBackKeyPressed="); pw.println(mDismissImeOnBackKeyPressed);
         pw.print(prefix);
diff --git a/services/core/java/com/android/server/wm/AnimatingAppWindowTokenRegistry.java b/services/core/java/com/android/server/wm/AnimatingAppWindowTokenRegistry.java
index 416469b..9c00d1a 100644
--- a/services/core/java/com/android/server/wm/AnimatingAppWindowTokenRegistry.java
+++ b/services/core/java/com/android/server/wm/AnimatingAppWindowTokenRegistry.java
@@ -37,6 +37,8 @@
 
     private ArrayList<Runnable> mTmpRunnableList = new ArrayList<>();
 
+    private boolean mEndingDeferredFinish;
+
     /**
      * Notifies that an {@link AppWindowToken} has started animating.
      */
@@ -50,6 +52,11 @@
     void notifyFinished(AppWindowToken token) {
         mAnimatingTokens.remove(token);
         mFinishedTokens.remove(token);
+
+        // If we were the last token, make sure the end all deferred finishes.
+        if (mAnimatingTokens.isEmpty()) {
+            endDeferringFinished();
+        }
     }
 
     /**
@@ -78,16 +85,28 @@
     }
 
     private void endDeferringFinished() {
-        // Copy it into a separate temp list to avoid modifying the collection while iterating as
-        // calling the callback may call back into notifyFinished.
-        for (int i = mFinishedTokens.size() - 1; i >= 0; i--) {
-            mTmpRunnableList.add(mFinishedTokens.valueAt(i));
+
+        // Don't start recursing. Running the finished listener invokes notifyFinished, which may
+        // invoked us again.
+        if (mEndingDeferredFinish) {
+            return;
         }
-        mFinishedTokens.clear();
-        for (int i = mTmpRunnableList.size() - 1; i >= 0; i--) {
-            mTmpRunnableList.get(i).run();
+        try {
+            mEndingDeferredFinish = true;
+
+            // Copy it into a separate temp list to avoid modifying the collection while iterating
+            // as calling the callback may call back into notifyFinished.
+            for (int i = mFinishedTokens.size() - 1; i >= 0; i--) {
+                mTmpRunnableList.add(mFinishedTokens.valueAt(i));
+            }
+            mFinishedTokens.clear();
+            for (int i = mTmpRunnableList.size() - 1; i >= 0; i--) {
+                mTmpRunnableList.get(i).run();
+            }
+            mTmpRunnableList.clear();
+        } finally {
+            mEndingDeferredFinish = false;
         }
-        mTmpRunnableList.clear();
     }
 
     void dump(PrintWriter pw, String header, String prefix) {
diff --git a/services/core/java/com/android/server/wm/AppWindowToken.java b/services/core/java/com/android/server/wm/AppWindowToken.java
index 8fa94fb..9a4db65 100644
--- a/services/core/java/com/android/server/wm/AppWindowToken.java
+++ b/services/core/java/com/android/server/wm/AppWindowToken.java
@@ -1714,7 +1714,8 @@
                     adapter = new LocalAnimationAdapter(
                             new WindowAnimationSpec(a, mTmpPoint, mTmpRect,
                                     mService.mAppTransition.canSkipFirstFrame(),
-                                    mService.mAppTransition.getAppStackClipMode()),
+                                    mService.mAppTransition.getAppStackClipMode(),
+                                    true /* isAppAnimation */),
                             mService.mSurfaceAnimationRunner);
                     if (a.getZAdjustment() == Animation.ZORDER_TOP) {
                         mNeedsZBoost = true;
diff --git a/services/core/java/com/android/server/wm/LocalAnimationAdapter.java b/services/core/java/com/android/server/wm/LocalAnimationAdapter.java
index 529aacc..d89d6f0 100644
--- a/services/core/java/com/android/server/wm/LocalAnimationAdapter.java
+++ b/services/core/java/com/android/server/wm/LocalAnimationAdapter.java
@@ -146,6 +146,13 @@
             return false;
         }
 
+        /**
+         * @return {@code true} if we need to wake-up SurfaceFlinger earlier during this animation.
+         *
+         * @see Transaction#setEarlyWakeup
+         */
+        default boolean needsEarlyWakeup() { return false; }
+
         void dump(PrintWriter pw, String prefix);
 
         default void writeToProto(ProtoOutputStream proto, long fieldId) {
diff --git a/services/core/java/com/android/server/wm/RecentsAnimationController.java b/services/core/java/com/android/server/wm/RecentsAnimationController.java
index e8f4545..318b3d2 100644
--- a/services/core/java/com/android/server/wm/RecentsAnimationController.java
+++ b/services/core/java/com/android/server/wm/RecentsAnimationController.java
@@ -51,6 +51,7 @@
 import android.view.SurfaceControl;
 import android.view.SurfaceControl.Transaction;
 
+import com.android.internal.annotations.VisibleForTesting;
 import com.google.android.collect.Sets;
 
 import com.android.server.wm.SurfaceAnimator.OnAnimationFinishedCallback;
@@ -290,8 +291,10 @@
         mService.mWindowPlacerLocked.performSurfacePlacement();
     }
 
-    private void addAnimation(Task task, boolean isRecentTaskInvisible) {
+    @VisibleForTesting
+    AnimationAdapter addAnimation(Task task, boolean isRecentTaskInvisible) {
         if (DEBUG) Log.d(TAG, "addAnimation(" + task.getName() + ")");
+        // TODO: Refactor this to use the task's animator
         final SurfaceAnimator anim = new SurfaceAnimator(task, null /* animationFinishedCallback */,
                 mService);
         final TaskAnimationAdapter taskAdapter = new TaskAnimationAdapter(task,
@@ -299,6 +302,15 @@
         anim.startAnimation(task.getPendingTransaction(), taskAdapter, false /* hidden */);
         task.commitPendingTransaction();
         mPendingAnimations.add(taskAdapter);
+        return taskAdapter;
+    }
+
+    @VisibleForTesting
+    void removeAnimation(TaskAnimationAdapter taskAdapter) {
+        if (DEBUG) Log.d(TAG, "removeAnimation(" + taskAdapter.mTask.getName() + ")");
+        taskAdapter.mTask.setCanAffectSystemUiFlags(true);
+        taskAdapter.mCapturedFinishCallback.onAnimationFinished(taskAdapter);
+        mPendingAnimations.remove(taskAdapter);
     }
 
     void startAnimation() {
@@ -311,12 +323,21 @@
         try {
             final ArrayList<RemoteAnimationTarget> appAnimations = new ArrayList<>();
             for (int i = mPendingAnimations.size() - 1; i >= 0; i--) {
-                final RemoteAnimationTarget target =
-                        mPendingAnimations.get(i).createRemoteAnimationApp();
+                final TaskAnimationAdapter taskAdapter = mPendingAnimations.get(i);
+                final RemoteAnimationTarget target = taskAdapter.createRemoteAnimationApp();
                 if (target != null) {
                     appAnimations.add(target);
+                } else {
+                    removeAnimation(taskAdapter);
                 }
             }
+
+            // Skip the animation if there is nothing to animate
+            if (appAnimations.isEmpty()) {
+                cancelAnimation(REORDER_MOVE_TO_ORIGINAL_POSITION);
+                return;
+            }
+
             final RemoteAnimationTarget[] appTargets = appAnimations.toArray(
                     new RemoteAnimationTarget[appAnimations.size()]);
             mPendingStart = false;
@@ -365,14 +386,12 @@
         if (DEBUG) Log.d(TAG, "cleanupAnimation(): mPendingAnimations="
                 + mPendingAnimations.size());
         for (int i = mPendingAnimations.size() - 1; i >= 0; i--) {
-            final TaskAnimationAdapter adapter = mPendingAnimations.get(i);
-            adapter.mTask.setCanAffectSystemUiFlags(true);
+            final TaskAnimationAdapter taskAdapter = mPendingAnimations.get(i);
             if (reorderMode == REORDER_MOVE_TO_TOP || reorderMode == REORDER_KEEP_IN_PLACE) {
-                adapter.mTask.dontAnimateDimExit();
+                taskAdapter.mTask.dontAnimateDimExit();
             }
-            adapter.mCapturedFinishCallback.onAnimationFinished(adapter);
+            removeAnimation(taskAdapter);
         }
-        mPendingAnimations.clear();
 
         mRunner.asBinder().unlinkToDeath(this, 0);
         // Clear associated input consumers
@@ -457,7 +476,8 @@
         return false;
     }
 
-    private class TaskAnimationAdapter implements AnimationAdapter {
+    @VisibleForTesting
+    class TaskAnimationAdapter implements AnimationAdapter {
 
         private final Task mTask;
         private SurfaceControl mCapturedLeash;
diff --git a/services/core/java/com/android/server/wm/SurfaceAnimationRunner.java b/services/core/java/com/android/server/wm/SurfaceAnimationRunner.java
index 98fcb0b..7211533 100644
--- a/services/core/java/com/android/server/wm/SurfaceAnimationRunner.java
+++ b/services/core/java/com/android/server/wm/SurfaceAnimationRunner.java
@@ -220,6 +220,9 @@
     }
 
     private void applyTransformation(RunningAnimation a, Transaction t, long currentPlayTime) {
+        if (a.mAnimSpec.needsEarlyWakeup()) {
+            t.setEarlyWakeup();
+        }
         a.mAnimSpec.apply(t, a.mLeash, currentPlayTime);
     }
 
diff --git a/services/core/java/com/android/server/wm/WindowAnimationSpec.java b/services/core/java/com/android/server/wm/WindowAnimationSpec.java
index 7b7cb30..548e23a 100644
--- a/services/core/java/com/android/server/wm/WindowAnimationSpec.java
+++ b/services/core/java/com/android/server/wm/WindowAnimationSpec.java
@@ -47,21 +47,24 @@
     private final Point mPosition = new Point();
     private final ThreadLocal<TmpValues> mThreadLocalTmps = ThreadLocal.withInitial(TmpValues::new);
     private final boolean mCanSkipFirstFrame;
+    private final boolean mIsAppAnimation;
     private final Rect mStackBounds = new Rect();
     private int mStackClipMode;
     private final Rect mTmpRect = new Rect();
 
     public WindowAnimationSpec(Animation animation, Point position, boolean canSkipFirstFrame)  {
-        this(animation, position, null /* stackBounds */, canSkipFirstFrame, STACK_CLIP_NONE);
+        this(animation, position, null /* stackBounds */, canSkipFirstFrame, STACK_CLIP_NONE,
+                false /* isAppAnimation */);
     }
 
     public WindowAnimationSpec(Animation animation, Point position, Rect stackBounds,
-            boolean canSkipFirstFrame, int stackClipMode) {
+            boolean canSkipFirstFrame, int stackClipMode, boolean isAppAnimation) {
         mAnimation = animation;
         if (position != null) {
             mPosition.set(position.x, position.y);
         }
         mCanSkipFirstFrame = canSkipFirstFrame;
+        mIsAppAnimation = isAppAnimation;
         mStackClipMode = stackClipMode;
         if (stackBounds != null) {
             mStackBounds.set(stackBounds);
@@ -135,6 +138,11 @@
     }
 
     @Override
+    public boolean needsEarlyWakeup() {
+        return mIsAppAnimation;
+    }
+
+    @Override
     public void dump(PrintWriter pw, String prefix) {
         pw.print(prefix); pw.println(mAnimation);
     }
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 756dc3ab..d10f005 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -2001,6 +2001,11 @@
                     // Try starting an animation.
                     if (mWinAnimator.applyAnimationLocked(transit, false)) {
                         mAnimatingExit = true;
+
+                        // mAnimatingExit affects canAffectSystemUiFlags(). Run layout such that
+                        // any change from that is performed immediately.
+                        setDisplayLayoutNeeded();
+                        mService.requestTraversal();
                     }
                     //TODO (multidisplay): Magnification is supported only for the default display.
                     if (mService.mAccessibilityController != null && displayId == DEFAULT_DISPLAY) {
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index ec079da..5cebbd8 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -8482,6 +8482,7 @@
         if (!mHasFeature) {
             return null;
         }
+        enforceManageUsers();
         synchronized (getLockObject()) {
             List<String> result = null;
             // If we have multiple profiles we return the intersection of the
diff --git a/services/net/java/android/net/ip/IpClient.java b/services/net/java/android/net/ip/IpClient.java
index a184b22..87249df 100644
--- a/services/net/java/android/net/ip/IpClient.java
+++ b/services/net/java/android/net/ip/IpClient.java
@@ -40,6 +40,7 @@
 import android.net.util.NetdService;
 import android.net.util.NetworkConstants;
 import android.net.util.SharedLog;
+import android.os.ConditionVariable;
 import android.os.INetworkManagementService;
 import android.os.Message;
 import android.os.RemoteException;
@@ -150,6 +151,28 @@
         public void setNeighborDiscoveryOffload(boolean enable) {}
     }
 
+    public static class WaitForProvisioningCallback extends Callback {
+        private final ConditionVariable mCV = new ConditionVariable();
+        private LinkProperties mCallbackLinkProperties;
+
+        public LinkProperties waitForProvisioning() {
+            mCV.block();
+            return mCallbackLinkProperties;
+        }
+
+        @Override
+        public void onProvisioningSuccess(LinkProperties newLp) {
+            mCallbackLinkProperties = newLp;
+            mCV.open();
+        }
+
+        @Override
+        public void onProvisioningFailure(LinkProperties newLp) {
+            mCallbackLinkProperties = null;
+            mCV.open();
+        }
+    }
+
     // Use a wrapper class to log in order to ensure complete and detailed
     // logging. This method is lighter weight than annotations/reflection
     // and has the following benefits:
@@ -281,6 +304,11 @@
                 return this;
             }
 
+            public Builder withoutMultinetworkPolicyTracker() {
+                mConfig.mUsingMultinetworkPolicyTracker = false;
+                return this;
+            }
+
             public Builder withoutIpReachabilityMonitor() {
                 mConfig.mUsingIpReachabilityMonitor = false;
                 return this;
@@ -343,6 +371,7 @@
 
         /* package */ boolean mEnableIPv4 = true;
         /* package */ boolean mEnableIPv6 = true;
+        /* package */ boolean mUsingMultinetworkPolicyTracker = true;
         /* package */ boolean mUsingIpReachabilityMonitor = true;
         /* package */ int mRequestedPreDhcpActionMs;
         /* package */ InitialConfiguration mInitialConfig;
@@ -374,6 +403,7 @@
             return new StringJoiner(", ", getClass().getSimpleName() + "{", "}")
                     .add("mEnableIPv4: " + mEnableIPv4)
                     .add("mEnableIPv6: " + mEnableIPv6)
+                    .add("mUsingMultinetworkPolicyTracker: " + mUsingMultinetworkPolicyTracker)
                     .add("mUsingIpReachabilityMonitor: " + mUsingIpReachabilityMonitor)
                     .add("mRequestedPreDhcpActionMs: " + mRequestedPreDhcpActionMs)
                     .add("mInitialConfig: " + mInitialConfig)
@@ -559,7 +589,6 @@
     private final NetlinkTracker mNetlinkTracker;
     private final WakeupMessage mProvisioningTimeoutAlarm;
     private final WakeupMessage mDhcpActionTimeoutAlarm;
-    private final MultinetworkPolicyTracker mMultinetworkPolicyTracker;
     private final SharedLog mLog;
     private final LocalLog mConnectivityPacketLog;
     private final MessageHandlingLogger mMsgStateLogger;
@@ -573,6 +602,7 @@
      */
     private LinkProperties mLinkProperties;
     private ProvisioningConfiguration mConfiguration;
+    private MultinetworkPolicyTracker mMultinetworkPolicyTracker;
     private IpReachabilityMonitor mIpReachabilityMonitor;
     private DhcpClient mDhcpClient;
     private DhcpResults mDhcpResults;
@@ -685,9 +715,6 @@
         mLinkProperties = new LinkProperties();
         mLinkProperties.setInterfaceName(mInterfaceName);
 
-        mMultinetworkPolicyTracker = new MultinetworkPolicyTracker(mContext, getHandler(),
-                () -> { mLog.log("OBSERVED AvoidBadWifi changed"); });
-
         mProvisioningTimeoutAlarm = new WakeupMessage(mContext, getHandler(),
                 mTag + ".EVENT_PROVISIONING_TIMEOUT", EVENT_PROVISIONING_TIMEOUT);
         mDhcpActionTimeoutAlarm = new WakeupMessage(mContext, getHandler(),
@@ -719,8 +746,6 @@
         } catch (RemoteException e) {
             logError("Couldn't register NetlinkTracker: %s", e);
         }
-
-        mMultinetworkPolicyTracker.start();
     }
 
     private void stopStateMachineUpdaters() {
@@ -729,8 +754,6 @@
         } catch (RemoteException e) {
             logError("Couldn't unregister NetlinkTracker: %s", e);
         }
-
-        mMultinetworkPolicyTracker.shutdown();
     }
 
     @Override
@@ -1028,7 +1051,8 @@
         // Note that we can still be disconnected by IpReachabilityMonitor
         // if the IPv6 default gateway (but not the IPv6 DNS servers; see
         // accompanying code in IpReachabilityMonitor) is unreachable.
-        final boolean ignoreIPv6ProvisioningLoss = !mMultinetworkPolicyTracker.getAvoidBadWifi();
+        final boolean ignoreIPv6ProvisioningLoss = (mMultinetworkPolicyTracker != null)
+                && !mMultinetworkPolicyTracker.getAvoidBadWifi();
 
         // Additionally:
         //
@@ -1520,6 +1544,13 @@
                 return;
             }
 
+            if (mConfiguration.mUsingMultinetworkPolicyTracker) {
+                mMultinetworkPolicyTracker = new MultinetworkPolicyTracker(
+                        mContext, getHandler(),
+                        () -> { mLog.log("OBSERVED AvoidBadWifi changed"); });
+                mMultinetworkPolicyTracker.start();
+            }
+
             if (mConfiguration.mUsingIpReachabilityMonitor && !startIpReachabilityMonitor()) {
                 doImmediateProvisioningFailure(
                         IpManagerEvent.ERROR_STARTING_IPREACHABILITYMONITOR);
@@ -1537,6 +1568,11 @@
                 mIpReachabilityMonitor = null;
             }
 
+            if (mMultinetworkPolicyTracker != null) {
+                mMultinetworkPolicyTracker.shutdown();
+                mMultinetworkPolicyTracker = null;
+            }
+
             if (mDhcpClient != null) {
                 mDhcpClient.sendMessage(DhcpClient.CMD_STOP_DHCP);
                 mDhcpClient.doQuit();
diff --git a/services/net/java/android/net/ip/IpManager.java b/services/net/java/android/net/ip/IpManager.java
index 508a43d..2eb36a2 100644
--- a/services/net/java/android/net/ip/IpManager.java
+++ b/services/net/java/android/net/ip/IpManager.java
@@ -114,35 +114,6 @@
     public static class Callback extends IpClient.Callback {
     }
 
-    public static class WaitForProvisioningCallback extends Callback {
-        private LinkProperties mCallbackLinkProperties;
-
-        public LinkProperties waitForProvisioning() {
-            synchronized (this) {
-                try {
-                    wait();
-                } catch (InterruptedException e) {}
-                return mCallbackLinkProperties;
-            }
-        }
-
-        @Override
-        public void onProvisioningSuccess(LinkProperties newLp) {
-            synchronized (this) {
-                mCallbackLinkProperties = newLp;
-                notify();
-            }
-        }
-
-        @Override
-        public void onProvisioningFailure(LinkProperties newLp) {
-            synchronized (this) {
-                mCallbackLinkProperties = null;
-                notify();
-            }
-        }
-    }
-
     public IpManager(Context context, String ifName, Callback callback) {
         super(context, ifName, callback);
     }
diff --git a/services/tests/servicestests/src/com/android/server/NetworkPolicyManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/NetworkPolicyManagerServiceTest.java
index d31d550..d908b8e 100644
--- a/services/tests/servicestests/src/com/android/server/NetworkPolicyManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/NetworkPolicyManagerServiceTest.java
@@ -37,9 +37,12 @@
 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;
+import static android.telephony.SubscriptionPlan.BYTES_UNLIMITED;
 import static android.telephony.SubscriptionPlan.LIMIT_BEHAVIOR_DISABLED;
 import static android.text.format.Time.TIMEZONE_UTC;
 
+import static com.android.server.net.NetworkPolicyManagerInternal.QUOTA_TYPE_JOBS;
+import static com.android.server.net.NetworkPolicyManagerInternal.QUOTA_TYPE_MULTIPATH;
 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_RAPID;
@@ -1484,6 +1487,102 @@
                 true);
     }
 
+    @Test
+    public void testOpportunisticQuota() throws Exception {
+        final Network net = new Network(TEST_NET_ID);
+        final NetworkPolicyManagerInternal internal = LocalServices
+                .getService(NetworkPolicyManagerInternal.class);
+
+        // Create a place to store fake usage
+        final NetworkStatsHistory history = new NetworkStatsHistory(TimeUnit.HOURS.toMillis(1));
+        final NetworkStats stats = new NetworkStats(SystemClock.elapsedRealtime(), 0);
+        when(mStatsService.getNetworkTotalBytes(any(), anyLong(), anyLong()))
+                .thenAnswer(new Answer<Long>() {
+                    @Override
+                    public Long answer(InvocationOnMock invocation) throws Throwable {
+                        final NetworkStatsHistory.Entry entry = history.getValues(
+                                invocation.getArgument(1), invocation.getArgument(2), null);
+                        return entry.rxBytes + entry.txBytes;
+                    }
+                });
+        when(mStatsService.getNetworkUidBytes(any(), anyLong(), anyLong()))
+                .thenAnswer(new Answer<NetworkStats>() {
+                    @Override
+                    public NetworkStats answer(InvocationOnMock invocation) throws Throwable {
+                        return stats;
+                    }
+                });
+
+        // Get active mobile network in place
+        expectMobileDefaults();
+        mService.updateNetworks();
+
+        // We're 20% through the month (6 days)
+        final long start = parseTime("2015-11-01T00:00Z");
+        final long end = parseTime("2015-11-07T00:00Z");
+        setCurrentTimeMillis(end);
+
+        // Get some data usage in place
+        history.clear();
+        history.recordData(start, end,
+                new NetworkStats.Entry(DataUnit.MEGABYTES.toBytes(360), 0L, 0L, 0L, 0));
+
+        // No data plan
+        {
+            reset(mTelephonyManager, mNetworkManager, mNotifManager);
+            expectMobileDefaults();
+
+            mService.updateNetworks();
+
+            // No quotas
+            assertEquals(-1, internal.getSubscriptionOpportunisticQuota(net, QUOTA_TYPE_JOBS));
+            assertEquals(-1, internal.getSubscriptionOpportunisticQuota(net, QUOTA_TYPE_MULTIPATH));
+        }
+
+        // Limited data plan
+        {
+            final SubscriptionPlan plan = SubscriptionPlan.Builder
+                    .createRecurringMonthly(ZonedDateTime.parse("2015-11-01T00:00:00.00Z"))
+                    .setDataLimit(DataUnit.MEGABYTES.toBytes(1800), LIMIT_BEHAVIOR_DISABLED)
+                    .build();
+            mService.setSubscriptionPlans(TEST_SUB_ID, new SubscriptionPlan[] { plan },
+                    mServiceContext.getOpPackageName());
+
+            reset(mTelephonyManager, mNetworkManager, mNotifManager);
+            expectMobileDefaults();
+
+            mService.updateNetworks();
+
+            // We have 1440MB and 24 days left, which is 60MB/day; assuming 10%
+            // for quota split equally between two types gives 3MB.
+            assertEquals(DataUnit.MEGABYTES.toBytes(3),
+                    internal.getSubscriptionOpportunisticQuota(net, QUOTA_TYPE_JOBS));
+            assertEquals(DataUnit.MEGABYTES.toBytes(3),
+                    internal.getSubscriptionOpportunisticQuota(net, QUOTA_TYPE_MULTIPATH));
+        }
+
+        // Unlimited data plan
+        {
+            final SubscriptionPlan plan = SubscriptionPlan.Builder
+                    .createRecurringMonthly(ZonedDateTime.parse("2015-11-01T00:00:00.00Z"))
+                    .setDataLimit(BYTES_UNLIMITED, LIMIT_BEHAVIOR_DISABLED)
+                    .build();
+            mService.setSubscriptionPlans(TEST_SUB_ID, new SubscriptionPlan[] { plan },
+                    mServiceContext.getOpPackageName());
+
+            reset(mTelephonyManager, mNetworkManager, mNotifManager);
+            expectMobileDefaults();
+
+            mService.updateNetworks();
+
+            // 20MB/day, split equally between two types gives 10MB.
+            assertEquals(DataUnit.MEBIBYTES.toBytes(10),
+                    internal.getSubscriptionOpportunisticQuota(net, QUOTA_TYPE_JOBS));
+            assertEquals(DataUnit.MEBIBYTES.toBytes(10),
+                    internal.getSubscriptionOpportunisticQuota(net, QUOTA_TYPE_MULTIPATH));
+        }
+    }
+
     private ApplicationInfo buildApplicationInfo(String label) {
         final ApplicationInfo ai = new ApplicationInfo();
         ai.nonLocalizedLabel = label;
diff --git a/services/tests/servicestests/src/com/android/server/am/RecentTasksTest.java b/services/tests/servicestests/src/com/android/server/am/RecentTasksTest.java
index edc6509..1192114 100644
--- a/services/tests/servicestests/src/com/android/server/am/RecentTasksTest.java
+++ b/services/tests/servicestests/src/com/android/server/am/RecentTasksTest.java
@@ -327,6 +327,33 @@
     }
 
     @Test
+    public void testAddTaskCompatibleActivityTypeDifferentUser_expectNoRemove() throws Exception {
+        Configuration config1 = new Configuration();
+        config1.windowConfiguration.setActivityType(ACTIVITY_TYPE_UNDEFINED);
+        TaskRecord task1 = createTaskBuilder(".Task1")
+                .setFlags(FLAG_ACTIVITY_NEW_TASK)
+                .setStack(mStack)
+                .setUserId(TEST_USER_0_ID)
+                .build();
+        task1.onConfigurationChanged(config1);
+        assertTrue(task1.getActivityType() == ACTIVITY_TYPE_UNDEFINED);
+        mRecentTasks.add(task1);
+        mCallbacksRecorder.clear();
+
+        TaskRecord task2 = createTaskBuilder(".Task1")
+                .setFlags(FLAG_ACTIVITY_NEW_TASK)
+                .setStack(mStack)
+                .setUserId(TEST_USER_1_ID)
+                .build();
+        assertTrue(task2.getActivityType() == ACTIVITY_TYPE_STANDARD);
+        mRecentTasks.add(task2);
+        assertTrue(mCallbacksRecorder.added.size() == 1);
+        assertTrue(mCallbacksRecorder.added.contains(task2));
+        assertTrue(mCallbacksRecorder.trimmed.isEmpty());
+        assertTrue(mCallbacksRecorder.removed.isEmpty());
+    }
+
+    @Test
     public void testUsersTasks() throws Exception {
         mRecentTasks.setOnlyTestVisibleRange();
 
diff --git a/services/tests/servicestests/src/com/android/server/job/controllers/ConnectivityControllerTest.java b/services/tests/servicestests/src/com/android/server/job/controllers/ConnectivityControllerTest.java
index 8874894..5b59e60 100644
--- a/services/tests/servicestests/src/com/android/server/job/controllers/ConnectivityControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/job/controllers/ConnectivityControllerTest.java
@@ -23,19 +23,28 @@
 
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
 import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.doNothing;
 import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.reset;
 import static org.mockito.Mockito.when;
 
 import android.app.job.JobInfo;
 import android.content.ComponentName;
+import android.content.Context;
 import android.content.pm.PackageManagerInternal;
+import android.net.ConnectivityManager;
+import android.net.ConnectivityManager.NetworkCallback;
 import android.net.Network;
 import android.net.NetworkCapabilities;
+import android.net.NetworkInfo;
+import android.net.NetworkInfo.DetailedState;
+import android.net.NetworkPolicyManager;
 import android.os.Build;
-import android.os.Handler;
 import android.os.SystemClock;
-import android.support.test.runner.AndroidJUnit4;
 import android.util.DataUnit;
 
 import com.android.server.LocalServices;
@@ -45,14 +54,26 @@
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnitRunner;
 
 import java.time.Clock;
 import java.time.ZoneOffset;
 
-@RunWith(AndroidJUnit4.class)
+@RunWith(MockitoJUnitRunner.class)
 public class ConnectivityControllerTest {
+
+    @Mock private Context mContext;
+    @Mock private ConnectivityManager mConnManager;
+    @Mock private NetworkPolicyManager mNetPolicyManager;
+    @Mock private JobSchedulerService mService;
+
     private Constants mConstants;
 
+    private static final int UID_RED = 10001;
+    private static final int UID_BLUE = 10002;
+
     @Before
     public void setUp() throws Exception {
         // Assume all packages are current SDK
@@ -72,6 +93,19 @@
 
         // Assume default constants for now
         mConstants = new Constants();
+
+        // Get our mocks ready
+        when(mContext.getSystemServiceName(ConnectivityManager.class))
+                .thenReturn(Context.CONNECTIVITY_SERVICE);
+        when(mContext.getSystemService(Context.CONNECTIVITY_SERVICE))
+                .thenReturn(mConnManager);
+        when(mContext.getSystemServiceName(NetworkPolicyManager.class))
+                .thenReturn(Context.NETWORK_POLICY_SERVICE);
+        when(mContext.getSystemService(Context.NETWORK_POLICY_SERVICE))
+                .thenReturn(mNetPolicyManager);
+        when(mService.getTestableContext()).thenReturn(mContext);
+        when(mService.getLock()).thenReturn(mService);
+        when(mService.getConstants()).thenReturn(mConstants);
     }
 
     @Test
@@ -155,6 +189,99 @@
         }
     }
 
+    @Test
+    public void testUpdates() throws Exception {
+        final ArgumentCaptor<NetworkCallback> callback = ArgumentCaptor
+                .forClass(NetworkCallback.class);
+        doNothing().when(mConnManager).registerNetworkCallback(any(), callback.capture());
+
+        final ConnectivityController controller = new ConnectivityController(mService);
+
+        final Network meteredNet = new Network(101);
+        final NetworkCapabilities meteredCaps = createCapabilities();
+        final Network unmeteredNet = new Network(202);
+        final NetworkCapabilities unmeteredCaps = createCapabilities()
+                .addCapability(NET_CAPABILITY_NOT_METERED);
+
+        final JobStatus red = createJobStatus(createJob()
+                .setEstimatedNetworkBytes(DataUnit.MEBIBYTES.toBytes(1), 0)
+                .setRequiredNetworkType(JobInfo.NETWORK_TYPE_UNMETERED), UID_RED);
+        final JobStatus blue = createJobStatus(createJob()
+                .setEstimatedNetworkBytes(DataUnit.MEBIBYTES.toBytes(1), 0)
+                .setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY), UID_BLUE);
+
+        // Pretend we're offline when job is added
+        {
+            reset(mConnManager);
+            answerNetwork(UID_RED, null, null);
+            answerNetwork(UID_BLUE, null, null);
+
+            controller.maybeStartTrackingJobLocked(red, null);
+            controller.maybeStartTrackingJobLocked(blue, null);
+
+            assertFalse(red.isConstraintSatisfied(JobStatus.CONSTRAINT_CONNECTIVITY));
+            assertFalse(blue.isConstraintSatisfied(JobStatus.CONSTRAINT_CONNECTIVITY));
+        }
+
+        // Metered network
+        {
+            reset(mConnManager);
+            answerNetwork(UID_RED, meteredNet, meteredCaps);
+            answerNetwork(UID_BLUE, meteredNet, meteredCaps);
+
+            callback.getValue().onCapabilitiesChanged(meteredNet, meteredCaps);
+
+            assertFalse(red.isConstraintSatisfied(JobStatus.CONSTRAINT_CONNECTIVITY));
+            assertTrue(blue.isConstraintSatisfied(JobStatus.CONSTRAINT_CONNECTIVITY));
+        }
+
+        // Unmetered network background
+        {
+            reset(mConnManager);
+            answerNetwork(UID_RED, meteredNet, meteredCaps);
+            answerNetwork(UID_BLUE, meteredNet, meteredCaps);
+
+            callback.getValue().onCapabilitiesChanged(unmeteredNet, unmeteredCaps);
+
+            assertFalse(red.isConstraintSatisfied(JobStatus.CONSTRAINT_CONNECTIVITY));
+            assertTrue(blue.isConstraintSatisfied(JobStatus.CONSTRAINT_CONNECTIVITY));
+        }
+
+        // Lost metered network
+        {
+            reset(mConnManager);
+            answerNetwork(UID_RED, unmeteredNet, unmeteredCaps);
+            answerNetwork(UID_BLUE, unmeteredNet, unmeteredCaps);
+
+            callback.getValue().onLost(meteredNet);
+
+            assertTrue(red.isConstraintSatisfied(JobStatus.CONSTRAINT_CONNECTIVITY));
+            assertTrue(blue.isConstraintSatisfied(JobStatus.CONSTRAINT_CONNECTIVITY));
+        }
+
+        // Specific UID was blocked
+        {
+            reset(mConnManager);
+            answerNetwork(UID_RED, null, null);
+            answerNetwork(UID_BLUE, unmeteredNet, unmeteredCaps);
+
+            callback.getValue().onCapabilitiesChanged(unmeteredNet, unmeteredCaps);
+
+            assertFalse(red.isConstraintSatisfied(JobStatus.CONSTRAINT_CONNECTIVITY));
+            assertTrue(blue.isConstraintSatisfied(JobStatus.CONSTRAINT_CONNECTIVITY));
+        }
+    }
+
+    private void answerNetwork(int uid, Network net, NetworkCapabilities caps) {
+        when(mConnManager.getActiveNetworkForUid(eq(uid))).thenReturn(net);
+        when(mConnManager.getNetworkCapabilities(eq(net))).thenReturn(caps);
+        if (net != null) {
+            final NetworkInfo ni = new NetworkInfo(ConnectivityManager.TYPE_WIFI, 0, null, null);
+            ni.setDetailedState(DetailedState.CONNECTED, null, null);
+            when(mConnManager.getNetworkInfoForUid(eq(net), eq(uid), anyBoolean())).thenReturn(ni);
+        }
+    }
+
     private static NetworkCapabilities createCapabilities() {
         return new NetworkCapabilities().addCapability(NET_CAPABILITY_INTERNET)
                 .addCapability(NET_CAPABILITY_VALIDATED);
@@ -165,12 +292,22 @@
     }
 
     private static JobStatus createJobStatus(JobInfo.Builder job) {
-        return createJobStatus(job, 0, Long.MAX_VALUE);
+        return createJobStatus(job, android.os.Process.NOBODY_UID, 0, Long.MAX_VALUE);
     }
 
-    private static JobStatus createJobStatus(JobInfo.Builder job, long earliestRunTimeElapsedMillis,
-            long latestRunTimeElapsedMillis) {
-        return new JobStatus(job.build(), 0, null, -1, 0, 0, null, earliestRunTimeElapsedMillis,
-                latestRunTimeElapsedMillis, 0, 0, null, 0);
+    private static JobStatus createJobStatus(JobInfo.Builder job, int uid) {
+        return createJobStatus(job, uid, 0, Long.MAX_VALUE);
+    }
+
+    private static JobStatus createJobStatus(JobInfo.Builder job,
+            long earliestRunTimeElapsedMillis, long latestRunTimeElapsedMillis) {
+        return createJobStatus(job, android.os.Process.NOBODY_UID,
+                earliestRunTimeElapsedMillis, latestRunTimeElapsedMillis);
+    }
+
+    private static JobStatus createJobStatus(JobInfo.Builder job, int uid,
+            long earliestRunTimeElapsedMillis, long latestRunTimeElapsedMillis) {
+        return new JobStatus(job.build(), uid, null, -1, 0, 0, null,
+                earliestRunTimeElapsedMillis, latestRunTimeElapsedMillis, 0, 0, null, 0);
     }
 }
diff --git a/services/tests/servicestests/src/com/android/server/wm/AnimatingAppWindowTokenRegistryTest.java b/services/tests/servicestests/src/com/android/server/wm/AnimatingAppWindowTokenRegistryTest.java
index 8b78f10..164c80b 100644
--- a/services/tests/servicestests/src/com/android/server/wm/AnimatingAppWindowTokenRegistryTest.java
+++ b/services/tests/servicestests/src/com/android/server/wm/AnimatingAppWindowTokenRegistryTest.java
@@ -88,4 +88,25 @@
         verify(mMockEndDeferFinishCallback1).run();
         verifyZeroInteractions(mMockEndDeferFinishCallback2);
     }
+
+    @Test
+    public void testContainerRemoved() throws Exception {
+        final AppWindowToken window1 = createAppWindowToken(mDisplayContent,
+                WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD);
+        final AppWindowToken window2 = createAppWindow(window1.getTask(), ACTIVITY_TYPE_STANDARD,
+                "window2").mAppToken;
+        final AnimatingAppWindowTokenRegistry registry =
+                window1.getStack().getAnimatingAppWindowTokenRegistry();
+
+        window1.startAnimation(window1.getPendingTransaction(), mAdapter, false /* hidden */);
+        window2.startAnimation(window1.getPendingTransaction(), mAdapter, false /* hidden */);
+        assertTrue(window1.isSelfAnimating());
+        assertTrue(window2.isSelfAnimating());
+
+        // Make sure that first animation finish is deferred, and removing the second window stops
+        // finishes all pending deferred finishings.
+        registry.notifyAboutToFinish(window1, mMockEndDeferFinishCallback1);
+        window2.setParent(null);
+        verify(mMockEndDeferFinishCallback1).run();
+    }
 }
diff --git a/services/tests/servicestests/src/com/android/server/wm/DisplayContentTests.java b/services/tests/servicestests/src/com/android/server/wm/DisplayContentTests.java
index 7f1bcac..845095a 100644
--- a/services/tests/servicestests/src/com/android/server/wm/DisplayContentTests.java
+++ b/services/tests/servicestests/src/com/android/server/wm/DisplayContentTests.java
@@ -46,6 +46,7 @@
 import android.graphics.Rect;
 import android.os.SystemClock;
 import android.platform.test.annotations.Presubmit;
+import android.support.test.filters.FlakyTest;
 import android.support.test.filters.SmallTest;
 import android.support.test.runner.AndroidJUnit4;
 import android.util.DisplayMetrics;
@@ -56,7 +57,9 @@
 
 import com.android.server.wm.utils.WmDisplayCutout;
 
+import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.Collections;
 import java.util.LinkedList;
 import java.util.List;
 
@@ -72,6 +75,7 @@
 public class DisplayContentTests extends WindowTestsBase {
 
     @Test
+    @FlakyTest(bugId = 77772044)
     public void testForAllWindows() throws Exception {
         final WindowState exitingAppWindow = createWindow(null, TYPE_BASE_APPLICATION,
                 mDisplayContent, "exiting app");
@@ -495,24 +499,24 @@
         assertEquals(displayContent.mBaseDisplayDensity, expectedBaseDensity);
     }
 
-    private void assertForAllWindowsOrder(List<WindowState> expectedWindows) {
-        final LinkedList<WindowState> actualWindows = new LinkedList();
+    private void assertForAllWindowsOrder(List<WindowState> expectedWindowsBottomToTop) {
+        final LinkedList<WindowState> actualWindows = new LinkedList<>();
 
         // Test forward traversal.
         mDisplayContent.forAllWindows(actualWindows::addLast, false /* traverseTopToBottom */);
-        assertEquals(expectedWindows.size(), actualWindows.size());
-        for (WindowState w : expectedWindows) {
-            assertEquals(w, actualWindows.pollFirst());
-        }
-        assertTrue(actualWindows.isEmpty());
+        assertThat("bottomToTop", actualWindows, is(expectedWindowsBottomToTop));
+
+        actualWindows.clear();
 
         // Test backward traversal.
         mDisplayContent.forAllWindows(actualWindows::addLast, true /* traverseTopToBottom */);
-        assertEquals(expectedWindows.size(), actualWindows.size());
-        for (WindowState w : expectedWindows) {
-            assertEquals(w, actualWindows.pollLast());
-        }
-        assertTrue(actualWindows.isEmpty());
+        assertThat("topToBottom", actualWindows, is(reverseList(expectedWindowsBottomToTop)));
+    }
+
+    private static List<WindowState> reverseList(List<WindowState> list) {
+        final ArrayList<WindowState> result = new ArrayList<>(list);
+        Collections.reverse(result);
+        return result;
     }
 
     private MotionEvent createTapEvent(float x, float y, boolean isDownEvent) {
diff --git a/services/tests/servicestests/src/com/android/server/wm/RecentsAnimationControllerTest.java b/services/tests/servicestests/src/com/android/server/wm/RecentsAnimationControllerTest.java
new file mode 100644
index 0000000..fbf6691
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/wm/RecentsAnimationControllerTest.java
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.server.wm;
+
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
+import static android.view.Display.DEFAULT_DISPLAY;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.atLeast;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
+import static org.mockito.Mockito.when;
+
+import android.os.Binder;
+import android.os.IInterface;
+import android.platform.test.annotations.Presubmit;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+import android.view.IRecentsAnimationRunner;
+import android.view.SurfaceControl;
+import com.android.server.wm.SurfaceAnimator.OnAnimationFinishedCallback;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+/**
+ * atest FrameworksServicesTests:com.android.server.wm.RecentsAnimationControllerTest
+ */
+@SmallTest
+@Presubmit
+@RunWith(AndroidJUnit4.class)
+public class RecentsAnimationControllerTest extends WindowTestsBase {
+
+    @Mock SurfaceControl mMockLeash;
+    @Mock SurfaceControl.Transaction mMockTransaction;
+    @Mock OnAnimationFinishedCallback mFinishedCallback;
+    @Mock IRecentsAnimationRunner mMockRunner;
+    @Mock RecentsAnimationController.RecentsAnimationCallbacks mAnimationCallbacks;
+    private RecentsAnimationController mController;
+
+    @Before
+    public void setUp() throws Exception {
+        super.setUp();
+        MockitoAnnotations.initMocks(this);
+        when(mMockRunner.asBinder()).thenReturn(new Binder());
+        mController = new RecentsAnimationController(sWm, mMockRunner, mAnimationCallbacks,
+                DEFAULT_DISPLAY);
+    }
+
+    @Test
+    public void testRemovedBeforeStarted_expectCanceled() throws Exception {
+        final AppWindowToken appWindow = createAppWindowToken(mDisplayContent,
+                WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD);
+        AnimationAdapter adapter = mController.addAnimation(appWindow.getTask(),
+                false /* isRecentTaskInvisible */);
+        adapter.startAnimation(mMockLeash, mMockTransaction, mFinishedCallback);
+
+        // Remove the app window so that the animation target can not be created
+        appWindow.removeImmediately();
+        mController.startAnimation();
+
+        // Verify that the finish callback to reparent the leash is called
+        verify(mFinishedCallback).onAnimationFinished(eq(adapter));
+        // Verify the animation canceled callback to the app was made
+        verify(mMockRunner).onAnimationCanceled();
+        verifyNoMoreInteractionsExceptAsBinder(mMockRunner);
+    }
+
+    private static void verifyNoMoreInteractionsExceptAsBinder(IInterface binder) {
+        verify(binder, atLeast(0)).asBinder();
+        verifyNoMoreInteractions(binder);
+    }
+}
diff --git a/services/tests/servicestests/src/com/android/server/wm/WindowAnimationSpecTest.java b/services/tests/servicestests/src/com/android/server/wm/WindowAnimationSpecTest.java
index 794d033..ca520ed 100644
--- a/services/tests/servicestests/src/com/android/server/wm/WindowAnimationSpecTest.java
+++ b/services/tests/servicestests/src/com/android/server/wm/WindowAnimationSpecTest.java
@@ -56,7 +56,8 @@
         Rect windowCrop = new Rect(0, 0, 20, 20);
         Animation a = createClipRectAnimation(windowCrop, windowCrop);
         WindowAnimationSpec windowAnimationSpec = new WindowAnimationSpec(a, null,
-                mStackBounds, false /* canSkipFirstFrame */, STACK_CLIP_NONE);
+                mStackBounds, false /* canSkipFirstFrame */, STACK_CLIP_NONE,
+                true /* isAppAnimation */);
         windowAnimationSpec.apply(mTransaction, mSurfaceControl, 0);
         verify(mTransaction).setWindowCrop(eq(mSurfaceControl),
                 argThat(rect -> rect.equals(windowCrop)));
@@ -65,7 +66,8 @@
     @Test
     public void testApply_clipAfter() {
         WindowAnimationSpec windowAnimationSpec = new WindowAnimationSpec(mAnimation, null,
-                mStackBounds, false /* canSkipFirstFrame */, STACK_CLIP_AFTER_ANIM);
+                mStackBounds, false /* canSkipFirstFrame */, STACK_CLIP_AFTER_ANIM,
+                true /* isAppAnimation */);
         windowAnimationSpec.apply(mTransaction, mSurfaceControl, 0);
         verify(mTransaction).setWindowCrop(eq(mSurfaceControl), argThat(Rect::isEmpty));
         verify(mTransaction).setFinalCrop(eq(mSurfaceControl),
@@ -77,7 +79,8 @@
         // Stack bounds is (0, 0, 10, 10) position is (20, 40)
         WindowAnimationSpec windowAnimationSpec = new WindowAnimationSpec(mAnimation,
                 new Point(20, 40), mStackBounds, false /* canSkipFirstFrame */,
-                STACK_CLIP_AFTER_ANIM);
+                STACK_CLIP_AFTER_ANIM,
+                true /* isAppAnimation */);
         windowAnimationSpec.apply(mTransaction, mSurfaceControl, 0);
         verify(mTransaction).setWindowCrop(eq(mSurfaceControl), argThat(Rect::isEmpty));
         verify(mTransaction).setFinalCrop(eq(mSurfaceControl),
@@ -89,7 +92,8 @@
     public void testApply_clipBeforeNoAnimationBounds() {
         // Stack bounds is (0, 0, 10, 10) animation clip is (0, 0, 0, 0)
         WindowAnimationSpec windowAnimationSpec = new WindowAnimationSpec(mAnimation, null,
-                mStackBounds, false /* canSkipFirstFrame */, STACK_CLIP_BEFORE_ANIM);
+                mStackBounds, false /* canSkipFirstFrame */, STACK_CLIP_BEFORE_ANIM,
+                true /* isAppAnimation */);
         windowAnimationSpec.apply(mTransaction, mSurfaceControl, 0);
         verify(mTransaction).setWindowCrop(eq(mSurfaceControl),
                 argThat(rect -> rect.equals(mStackBounds)));
@@ -102,7 +106,8 @@
         Animation a = createClipRectAnimation(windowCrop, windowCrop);
         a.initialize(0, 0, 0, 0);
         WindowAnimationSpec windowAnimationSpec = new WindowAnimationSpec(a, null,
-                null, false /* canSkipFirstFrame */, STACK_CLIP_BEFORE_ANIM);
+                null, false /* canSkipFirstFrame */, STACK_CLIP_BEFORE_ANIM,
+                true /* isAppAnimation */);
         windowAnimationSpec.apply(mTransaction, mSurfaceControl, 0);
         verify(mTransaction).setWindowCrop(eq(mSurfaceControl), argThat(Rect::isEmpty));
     }
@@ -113,7 +118,8 @@
         Rect windowCrop = new Rect(0, 0, 5, 5);
         Animation a = createClipRectAnimation(windowCrop, windowCrop);
         WindowAnimationSpec windowAnimationSpec = new WindowAnimationSpec(a, null,
-                mStackBounds, false /* canSkipFirstFrame */, STACK_CLIP_BEFORE_ANIM);
+                mStackBounds, false /* canSkipFirstFrame */, STACK_CLIP_BEFORE_ANIM,
+                true /* isAppAnimation */);
         windowAnimationSpec.apply(mTransaction, mSurfaceControl, 0);
         verify(mTransaction).setWindowCrop(eq(mSurfaceControl),
                 argThat(rect -> rect.equals(windowCrop)));
@@ -125,7 +131,8 @@
         Rect windowCrop = new Rect(0, 0, 20, 20);
         Animation a = createClipRectAnimation(windowCrop, windowCrop);
         WindowAnimationSpec windowAnimationSpec = new WindowAnimationSpec(a, null,
-                mStackBounds, false /* canSkipFirstFrame */, STACK_CLIP_BEFORE_ANIM);
+                mStackBounds, false /* canSkipFirstFrame */, STACK_CLIP_BEFORE_ANIM,
+                true /* isAppAnimation */);
         windowAnimationSpec.apply(mTransaction, mSurfaceControl, 0);
         verify(mTransaction).setWindowCrop(eq(mSurfaceControl),
                 argThat(rect -> rect.equals(mStackBounds)));
diff --git a/tests/net/java/android/net/apf/ApfTest.java b/tests/net/java/android/net/apf/ApfTest.java
index fef702e..9364ec8 100644
--- a/tests/net/java/android/net/apf/ApfTest.java
+++ b/tests/net/java/android/net/apf/ApfTest.java
@@ -34,7 +34,7 @@
 import android.net.apf.ApfFilter.ApfConfiguration;
 import android.net.apf.ApfGenerator.IllegalInstructionException;
 import android.net.apf.ApfGenerator.Register;
-import android.net.ip.IpManager;
+import android.net.ip.IpClient;
 import android.net.metrics.IpConnectivityLog;
 import android.net.metrics.RaEvent;
 import android.net.util.InterfaceParams;
@@ -606,7 +606,7 @@
         }
     }
 
-    private class MockIpManagerCallback extends IpManager.Callback {
+    private class MockIpClientCallback extends IpClient.Callback {
         private final ConditionVariable mGotApfProgram = new ConditionVariable();
         private byte[] mLastApfProgram;
 
@@ -637,8 +637,8 @@
         private final long mFixedTimeMs = SystemClock.elapsedRealtime();
 
         public TestApfFilter(Context context, ApfConfiguration config,
-                IpManager.Callback ipManagerCallback, IpConnectivityLog log) throws Exception {
-            super(context, config, InterfaceParams.getByName("lo"), ipManagerCallback, log);
+                IpClient.Callback ipClientCallback, IpConnectivityLog log) throws Exception {
+            super(context, config, InterfaceParams.getByName("lo"), ipClientCallback, log);
         }
 
         // Pretend an RA packet has been received and show it to ApfFilter.
@@ -761,29 +761,29 @@
     private static final byte[] IPV4_ANY_HOST_ADDR       = {0, 0, 0, 0};
 
     // Helper to initialize a default apfFilter.
-    private ApfFilter setupApfFilter(IpManager.Callback ipManagerCallback, ApfConfiguration config)
+    private ApfFilter setupApfFilter(IpClient.Callback ipClientCallback, ApfConfiguration config)
             throws Exception {
         LinkAddress link = new LinkAddress(InetAddress.getByAddress(MOCK_IPV4_ADDR), 19);
         LinkProperties lp = new LinkProperties();
         lp.addLinkAddress(link);
-        TestApfFilter apfFilter = new TestApfFilter(mContext, config, ipManagerCallback, mLog);
+        TestApfFilter apfFilter = new TestApfFilter(mContext, config, ipClientCallback, mLog);
         apfFilter.setLinkProperties(lp);
         return apfFilter;
     }
 
     @Test
     public void testApfFilterIPv4() throws Exception {
-        MockIpManagerCallback ipManagerCallback = new MockIpManagerCallback();
+        MockIpClientCallback ipClientCallback = new MockIpClientCallback();
         LinkAddress link = new LinkAddress(InetAddress.getByAddress(MOCK_IPV4_ADDR), 19);
         LinkProperties lp = new LinkProperties();
         lp.addLinkAddress(link);
 
         ApfConfiguration config = getDefaultConfig();
         config.multicastFilter = DROP_MULTICAST;
-        TestApfFilter apfFilter = new TestApfFilter(mContext, config, ipManagerCallback, mLog);
+        TestApfFilter apfFilter = new TestApfFilter(mContext, config, ipClientCallback, mLog);
         apfFilter.setLinkProperties(lp);
 
-        byte[] program = ipManagerCallback.getApfProgram();
+        byte[] program = ipClientCallback.getApfProgram();
 
         // Verify empty packet of 100 zero bytes is passed
         ByteBuffer packet = ByteBuffer.wrap(new byte[100]);
@@ -830,10 +830,10 @@
 
     @Test
     public void testApfFilterIPv6() throws Exception {
-        MockIpManagerCallback ipManagerCallback = new MockIpManagerCallback();
+        MockIpClientCallback ipClientCallback = new MockIpClientCallback();
         ApfConfiguration config = getDefaultConfig();
-        TestApfFilter apfFilter = new TestApfFilter(mContext, config, ipManagerCallback, mLog);
-        byte[] program = ipManagerCallback.getApfProgram();
+        TestApfFilter apfFilter = new TestApfFilter(mContext, config, ipClientCallback, mLog);
+        byte[] program = ipClientCallback.getApfProgram();
 
         // Verify empty IPv6 packet is passed
         ByteBuffer packet = ByteBuffer.wrap(new byte[100]);
@@ -868,17 +868,17 @@
         final byte[] multicastIpv4Addr = {(byte)224,0,0,1};
         final byte[] multicastIpv6Addr = {(byte)0xff,2,0,0,0,0,0,0,0,0,0,0,0,0,0,(byte)0xfb};
 
-        MockIpManagerCallback ipManagerCallback = new MockIpManagerCallback();
+        MockIpClientCallback ipClientCallback = new MockIpClientCallback();
         LinkAddress link = new LinkAddress(InetAddress.getByAddress(unicastIpv4Addr), 24);
         LinkProperties lp = new LinkProperties();
         lp.addLinkAddress(link);
 
         ApfConfiguration config = getDefaultConfig();
         config.ieee802_3Filter = DROP_802_3_FRAMES;
-        TestApfFilter apfFilter = new TestApfFilter(mContext, config, ipManagerCallback, mLog);
+        TestApfFilter apfFilter = new TestApfFilter(mContext, config, ipClientCallback, mLog);
         apfFilter.setLinkProperties(lp);
 
-        byte[] program = ipManagerCallback.getApfProgram();
+        byte[] program = ipClientCallback.getApfProgram();
 
         // Construct IPv4 and IPv6 multicast packets.
         ByteBuffer mcastv4packet = ByteBuffer.wrap(new byte[100]);
@@ -915,9 +915,9 @@
         assertPass(program, bcastv4unicastl2packet.array());
 
         // Turn on multicast filter and verify it works
-        ipManagerCallback.resetApfProgramWait();
+        ipClientCallback.resetApfProgramWait();
         apfFilter.setMulticastFilter(true);
-        program = ipManagerCallback.getApfProgram();
+        program = ipClientCallback.getApfProgram();
         assertDrop(program, mcastv4packet.array());
         assertDrop(program, mcastv6packet.array());
         assertDrop(program, bcastv4packet1.array());
@@ -925,9 +925,9 @@
         assertDrop(program, bcastv4unicastl2packet.array());
 
         // Turn off multicast filter and verify it's off
-        ipManagerCallback.resetApfProgramWait();
+        ipClientCallback.resetApfProgramWait();
         apfFilter.setMulticastFilter(false);
-        program = ipManagerCallback.getApfProgram();
+        program = ipClientCallback.getApfProgram();
         assertPass(program, mcastv4packet.array());
         assertPass(program, mcastv6packet.array());
         assertPass(program, bcastv4packet1.array());
@@ -935,13 +935,13 @@
         assertPass(program, bcastv4unicastl2packet.array());
 
         // Verify it can be initialized to on
-        ipManagerCallback.resetApfProgramWait();
+        ipClientCallback.resetApfProgramWait();
         apfFilter.shutdown();
         config.multicastFilter = DROP_MULTICAST;
         config.ieee802_3Filter = DROP_802_3_FRAMES;
-        apfFilter = new TestApfFilter(mContext, config, ipManagerCallback, mLog);
+        apfFilter = new TestApfFilter(mContext, config, ipClientCallback, mLog);
         apfFilter.setLinkProperties(lp);
-        program = ipManagerCallback.getApfProgram();
+        program = ipClientCallback.getApfProgram();
         assertDrop(program, mcastv4packet.array());
         assertDrop(program, mcastv6packet.array());
         assertDrop(program, bcastv4packet1.array());
@@ -956,8 +956,8 @@
 
     @Test
     public void testApfFilterMulticastPingWhileDozing() throws Exception {
-        MockIpManagerCallback ipManagerCallback = new MockIpManagerCallback();
-        ApfFilter apfFilter = setupApfFilter(ipManagerCallback, getDefaultConfig());
+        MockIpClientCallback ipClientCallback = new MockIpClientCallback();
+        ApfFilter apfFilter = setupApfFilter(ipClientCallback, getDefaultConfig());
 
         // Construct a multicast ICMPv6 ECHO request.
         final byte[] multicastIpv6Addr = {(byte)0xff,2,0,0,0,0,0,0,0,0,0,0,0,0,0,(byte)0xfb};
@@ -968,35 +968,35 @@
         put(packet, IPV6_DEST_ADDR_OFFSET, multicastIpv6Addr);
 
         // Normally, we let multicast pings alone...
-        assertPass(ipManagerCallback.getApfProgram(), packet.array());
+        assertPass(ipClientCallback.getApfProgram(), packet.array());
 
         // ...and even while dozing...
         apfFilter.setDozeMode(true);
-        assertPass(ipManagerCallback.getApfProgram(), packet.array());
+        assertPass(ipClientCallback.getApfProgram(), packet.array());
 
         // ...but when the multicast filter is also enabled, drop the multicast pings to save power.
         apfFilter.setMulticastFilter(true);
-        assertDrop(ipManagerCallback.getApfProgram(), packet.array());
+        assertDrop(ipClientCallback.getApfProgram(), packet.array());
 
         // However, we should still let through all other ICMPv6 types.
         ByteBuffer raPacket = ByteBuffer.wrap(packet.array().clone());
         raPacket.put(ICMP6_TYPE_OFFSET, (byte)ICMPV6_ROUTER_ADVERTISEMENT);
-        assertPass(ipManagerCallback.getApfProgram(), raPacket.array());
+        assertPass(ipClientCallback.getApfProgram(), raPacket.array());
 
         // Now wake up from doze mode to ensure that we no longer drop the packets.
         // (The multicast filter is still enabled at this point).
         apfFilter.setDozeMode(false);
-        assertPass(ipManagerCallback.getApfProgram(), packet.array());
+        assertPass(ipClientCallback.getApfProgram(), packet.array());
 
         apfFilter.shutdown();
     }
 
     @Test
     public void testApfFilter802_3() throws Exception {
-        MockIpManagerCallback ipManagerCallback = new MockIpManagerCallback();
+        MockIpClientCallback ipClientCallback = new MockIpClientCallback();
         ApfConfiguration config = getDefaultConfig();
-        ApfFilter apfFilter = setupApfFilter(ipManagerCallback, config);
-        byte[] program = ipManagerCallback.getApfProgram();
+        ApfFilter apfFilter = setupApfFilter(ipClientCallback, config);
+        byte[] program = ipClientCallback.getApfProgram();
 
         // Verify empty packet of 100 zero bytes is passed
         // Note that eth-type = 0 makes it an IEEE802.3 frame
@@ -1012,11 +1012,11 @@
         assertPass(program, packet.array());
 
         // Now turn on the filter
-        ipManagerCallback.resetApfProgramWait();
+        ipClientCallback.resetApfProgramWait();
         apfFilter.shutdown();
         config.ieee802_3Filter = DROP_802_3_FRAMES;
-        apfFilter = setupApfFilter(ipManagerCallback, config);
-        program = ipManagerCallback.getApfProgram();
+        apfFilter = setupApfFilter(ipClientCallback, config);
+        program = ipClientCallback.getApfProgram();
 
         // Verify that IEEE802.3 frame is dropped
         // In this case ethtype is used for payload length
@@ -1040,10 +1040,10 @@
         final int[] ipv4BlackList = {ETH_P_IP};
         final int[] ipv4Ipv6BlackList = {ETH_P_IP, ETH_P_IPV6};
 
-        MockIpManagerCallback ipManagerCallback = new MockIpManagerCallback();
+        MockIpClientCallback ipClientCallback = new MockIpClientCallback();
         ApfConfiguration config = getDefaultConfig();
-        ApfFilter apfFilter = setupApfFilter(ipManagerCallback, config);
-        byte[] program = ipManagerCallback.getApfProgram();
+        ApfFilter apfFilter = setupApfFilter(ipClientCallback, config);
+        byte[] program = ipClientCallback.getApfProgram();
 
         // Verify empty packet of 100 zero bytes is passed
         // Note that eth-type = 0 makes it an IEEE802.3 frame
@@ -1059,11 +1059,11 @@
         assertPass(program, packet.array());
 
         // Now add IPv4 to the black list
-        ipManagerCallback.resetApfProgramWait();
+        ipClientCallback.resetApfProgramWait();
         apfFilter.shutdown();
         config.ethTypeBlackList = ipv4BlackList;
-        apfFilter = setupApfFilter(ipManagerCallback, config);
-        program = ipManagerCallback.getApfProgram();
+        apfFilter = setupApfFilter(ipClientCallback, config);
+        program = ipClientCallback.getApfProgram();
 
         // Verify that IPv4 frame will be dropped
         packet.putShort(ETH_ETHERTYPE_OFFSET, (short)ETH_P_IP);
@@ -1074,11 +1074,11 @@
         assertPass(program, packet.array());
 
         // Now let us have both IPv4 and IPv6 in the black list
-        ipManagerCallback.resetApfProgramWait();
+        ipClientCallback.resetApfProgramWait();
         apfFilter.shutdown();
         config.ethTypeBlackList = ipv4Ipv6BlackList;
-        apfFilter = setupApfFilter(ipManagerCallback, config);
-        program = ipManagerCallback.getApfProgram();
+        apfFilter = setupApfFilter(ipClientCallback, config);
+        program = ipClientCallback.getApfProgram();
 
         // Verify that IPv4 frame will be dropped
         packet.putShort(ETH_ETHERTYPE_OFFSET, (short)ETH_P_IP);
@@ -1091,7 +1091,7 @@
         apfFilter.shutdown();
     }
 
-    private byte[] getProgram(MockIpManagerCallback cb, ApfFilter filter, LinkProperties lp) {
+    private byte[] getProgram(MockIpClientCallback cb, ApfFilter filter, LinkProperties lp) {
         cb.resetApfProgramWait();
         filter.setLinkProperties(lp);
         return cb.getApfProgram();
@@ -1114,23 +1114,23 @@
 
     @Test
     public void testApfFilterArp() throws Exception {
-        MockIpManagerCallback ipManagerCallback = new MockIpManagerCallback();
+        MockIpClientCallback ipClientCallback = new MockIpClientCallback();
         ApfConfiguration config = getDefaultConfig();
         config.multicastFilter = DROP_MULTICAST;
         config.ieee802_3Filter = DROP_802_3_FRAMES;
-        TestApfFilter apfFilter = new TestApfFilter(mContext, config, ipManagerCallback, mLog);
+        TestApfFilter apfFilter = new TestApfFilter(mContext, config, ipClientCallback, mLog);
 
         // Verify initially ARP request filter is off, and GARP filter is on.
-        verifyArpFilter(ipManagerCallback.getApfProgram(), PASS);
+        verifyArpFilter(ipClientCallback.getApfProgram(), PASS);
 
         // Inform ApfFilter of our address and verify ARP filtering is on
         LinkAddress linkAddress = new LinkAddress(InetAddress.getByAddress(MOCK_IPV4_ADDR), 24);
         LinkProperties lp = new LinkProperties();
         assertTrue(lp.addLinkAddress(linkAddress));
-        verifyArpFilter(getProgram(ipManagerCallback, apfFilter, lp), DROP);
+        verifyArpFilter(getProgram(ipClientCallback, apfFilter, lp), DROP);
 
         // Inform ApfFilter of loss of IP and verify ARP filtering is off
-        verifyArpFilter(getProgram(ipManagerCallback, apfFilter, new LinkProperties()), PASS);
+        verifyArpFilter(getProgram(ipClientCallback, apfFilter, new LinkProperties()), PASS);
 
         apfFilter.shutdown();
     }
@@ -1161,7 +1161,7 @@
         return packet.array();
     }
 
-    // Verify that the last program pushed to the IpManager.Callback properly filters the
+    // Verify that the last program pushed to the IpClient.Callback properly filters the
     // given packet for the given lifetime.
     private void verifyRaLifetime(byte[] program, ByteBuffer packet, int lifetime) {
         final int FRACTION_OF_LIFETIME = 6;
@@ -1191,12 +1191,12 @@
 
     // Test that when ApfFilter is shown the given packet, it generates a program to filter it
     // for the given lifetime.
-    private void verifyRaLifetime(TestApfFilter apfFilter, MockIpManagerCallback ipManagerCallback,
+    private void verifyRaLifetime(TestApfFilter apfFilter, MockIpClientCallback ipClientCallback,
             ByteBuffer packet, int lifetime) throws IOException, ErrnoException {
         // Verify new program generated if ApfFilter witnesses RA
-        ipManagerCallback.resetApfProgramWait();
+        ipClientCallback.resetApfProgramWait();
         apfFilter.pretendPacketReceived(packet.array());
-        byte[] program = ipManagerCallback.getApfProgram();
+        byte[] program = ipClientCallback.getApfProgram();
         verifyRaLifetime(program, packet, lifetime);
     }
 
@@ -1229,21 +1229,21 @@
                 && (ev1.dnsslLifetime == ev2.dnsslLifetime);
     }
 
-    private void assertInvalidRa(TestApfFilter apfFilter, MockIpManagerCallback ipManagerCallback,
+    private void assertInvalidRa(TestApfFilter apfFilter, MockIpClientCallback ipClientCallback,
             ByteBuffer packet) throws IOException, ErrnoException {
-        ipManagerCallback.resetApfProgramWait();
+        ipClientCallback.resetApfProgramWait();
         apfFilter.pretendPacketReceived(packet.array());
-        ipManagerCallback.assertNoProgramUpdate();
+        ipClientCallback.assertNoProgramUpdate();
     }
 
     @Test
     public void testApfFilterRa() throws Exception {
-        MockIpManagerCallback ipManagerCallback = new MockIpManagerCallback();
+        MockIpClientCallback ipClientCallback = new MockIpClientCallback();
         ApfConfiguration config = getDefaultConfig();
         config.multicastFilter = DROP_MULTICAST;
         config.ieee802_3Filter = DROP_802_3_FRAMES;
-        TestApfFilter apfFilter = new TestApfFilter(mContext, config, ipManagerCallback, mLog);
-        byte[] program = ipManagerCallback.getApfProgram();
+        TestApfFilter apfFilter = new TestApfFilter(mContext, config, ipClientCallback, mLog);
+        byte[] program = ipClientCallback.getApfProgram();
 
         final int ROUTER_LIFETIME = 1000;
         final int PREFIX_VALID_LIFETIME = 200;
@@ -1268,7 +1268,7 @@
         basePacket.put(IPV6_ALL_NODES_ADDRESS);
         assertPass(program, basePacket.array());
 
-        verifyRaLifetime(apfFilter, ipManagerCallback, basePacket, ROUTER_LIFETIME);
+        verifyRaLifetime(apfFilter, ipClientCallback, basePacket, ROUTER_LIFETIME);
         verifyRaEvent(new RaEvent(ROUTER_LIFETIME, -1, -1, -1, -1, -1));
 
         ByteBuffer newFlowLabelPacket = ByteBuffer.wrap(new byte[ICMP6_RA_OPTION_OFFSET]);
@@ -1286,7 +1286,7 @@
         zeroLengthOptionPacket.put(basePacket);
         zeroLengthOptionPacket.put((byte)ICMP6_PREFIX_OPTION_TYPE);
         zeroLengthOptionPacket.put((byte)0);
-        assertInvalidRa(apfFilter, ipManagerCallback, zeroLengthOptionPacket);
+        assertInvalidRa(apfFilter, ipClientCallback, zeroLengthOptionPacket);
 
         // Generate several RAs with different options and lifetimes, and verify when
         // ApfFilter is shown these packets, it generates programs to filter them for the
@@ -1304,7 +1304,7 @@
                 ICMP6_RA_OPTION_OFFSET + ICMP6_PREFIX_OPTION_VALID_LIFETIME_OFFSET,
                 PREFIX_VALID_LIFETIME);
         verifyRaLifetime(
-                apfFilter, ipManagerCallback, prefixOptionPacket, PREFIX_PREFERRED_LIFETIME);
+                apfFilter, ipClientCallback, prefixOptionPacket, PREFIX_PREFERRED_LIFETIME);
         verifyRaEvent(new RaEvent(
                 ROUTER_LIFETIME, PREFIX_VALID_LIFETIME, PREFIX_PREFERRED_LIFETIME, -1, -1, -1));
 
@@ -1316,7 +1316,7 @@
         rdnssOptionPacket.put((byte)(ICMP6_4_BYTE_OPTION_LEN / 8));
         rdnssOptionPacket.putInt(
                 ICMP6_RA_OPTION_OFFSET + ICMP6_4_BYTE_LIFETIME_OFFSET, RDNSS_LIFETIME);
-        verifyRaLifetime(apfFilter, ipManagerCallback, rdnssOptionPacket, RDNSS_LIFETIME);
+        verifyRaLifetime(apfFilter, ipClientCallback, rdnssOptionPacket, RDNSS_LIFETIME);
         verifyRaEvent(new RaEvent(ROUTER_LIFETIME, -1, -1, -1, RDNSS_LIFETIME, -1));
 
         ByteBuffer routeInfoOptionPacket = ByteBuffer.wrap(
@@ -1327,7 +1327,7 @@
         routeInfoOptionPacket.put((byte)(ICMP6_4_BYTE_OPTION_LEN / 8));
         routeInfoOptionPacket.putInt(
                 ICMP6_RA_OPTION_OFFSET + ICMP6_4_BYTE_LIFETIME_OFFSET, ROUTE_LIFETIME);
-        verifyRaLifetime(apfFilter, ipManagerCallback, routeInfoOptionPacket, ROUTE_LIFETIME);
+        verifyRaLifetime(apfFilter, ipClientCallback, routeInfoOptionPacket, ROUTE_LIFETIME);
         verifyRaEvent(new RaEvent(ROUTER_LIFETIME, -1, -1, ROUTE_LIFETIME, -1, -1));
 
         ByteBuffer dnsslOptionPacket = ByteBuffer.wrap(
@@ -1338,11 +1338,11 @@
         dnsslOptionPacket.put((byte)(ICMP6_4_BYTE_OPTION_LEN / 8));
         dnsslOptionPacket.putInt(
                 ICMP6_RA_OPTION_OFFSET + ICMP6_4_BYTE_LIFETIME_OFFSET, DNSSL_LIFETIME);
-        verifyRaLifetime(apfFilter, ipManagerCallback, dnsslOptionPacket, ROUTER_LIFETIME);
+        verifyRaLifetime(apfFilter, ipClientCallback, dnsslOptionPacket, ROUTER_LIFETIME);
         verifyRaEvent(new RaEvent(ROUTER_LIFETIME, -1, -1, -1, -1, DNSSL_LIFETIME));
 
         // Verify that current program filters all five RAs:
-        program = ipManagerCallback.getApfProgram();
+        program = ipClientCallback.getApfProgram();
         verifyRaLifetime(program, basePacket, ROUTER_LIFETIME);
         verifyRaLifetime(program, newFlowLabelPacket, ROUTER_LIFETIME);
         verifyRaLifetime(program, prefixOptionPacket, PREFIX_PREFERRED_LIFETIME);
@@ -1384,7 +1384,7 @@
     public void testRaParsing() throws Exception {
         final int maxRandomPacketSize = 512;
         final Random r = new Random();
-        MockIpManagerCallback cb = new MockIpManagerCallback();
+        MockIpClientCallback cb = new MockIpClientCallback();
         ApfConfiguration config = getDefaultConfig();
         config.multicastFilter = DROP_MULTICAST;
         config.ieee802_3Filter = DROP_802_3_FRAMES;
@@ -1405,7 +1405,7 @@
     public void testRaProcessing() throws Exception {
         final int maxRandomPacketSize = 512;
         final Random r = new Random();
-        MockIpManagerCallback cb = new MockIpManagerCallback();
+        MockIpClientCallback cb = new MockIpClientCallback();
         ApfConfiguration config = getDefaultConfig();
         config.multicastFilter = DROP_MULTICAST;
         config.ieee802_3Filter = DROP_802_3_FRAMES;
diff --git a/tools/aapt2/Android.bp b/tools/aapt2/Android.bp
index 2ff92e6..e6d94e6 100644
--- a/tools/aapt2/Android.bp
+++ b/tools/aapt2/Android.bp
@@ -111,6 +111,7 @@
         "link/XmlReferenceLinker.cpp",
         "optimize/MultiApkGenerator.cpp",
         "optimize/ResourceDeduper.cpp",
+        "optimize/ResourceFilter.cpp",
         "optimize/VersionCollapser.cpp",
         "process/SymbolTable.cpp",
         "split/TableSplitter.cpp",
diff --git a/tools/aapt2/cmd/Link.cpp b/tools/aapt2/cmd/Link.cpp
index 5dff8d8..54509d9 100644
--- a/tools/aapt2/cmd/Link.cpp
+++ b/tools/aapt2/cmd/Link.cpp
@@ -17,6 +17,7 @@
 #include <sys/stat.h>
 #include <cinttypes>
 
+#include <algorithm>
 #include <queue>
 #include <unordered_map>
 #include <vector>
diff --git a/tools/aapt2/cmd/Optimize.cpp b/tools/aapt2/cmd/Optimize.cpp
index da32010..4afa8f0 100644
--- a/tools/aapt2/cmd/Optimize.cpp
+++ b/tools/aapt2/cmd/Optimize.cpp
@@ -38,6 +38,7 @@
 #include "io/Util.h"
 #include "optimize/MultiApkGenerator.h"
 #include "optimize/ResourceDeduper.h"
+#include "optimize/ResourceFilter.h"
 #include "optimize/VersionCollapser.h"
 #include "split/TableSplitter.h"
 #include "util/Files.h"
@@ -62,6 +63,9 @@
   // Details of the app extracted from the AndroidManifest.xml
   AppInfo app_info;
 
+  // Blacklist of unused resources that should be removed from the apk.
+  std::unordered_set<ResourceName> resources_blacklist;
+
   // Split APK options.
   TableSplitterOptions table_splitter_options;
 
@@ -151,6 +155,13 @@
     if (context_->IsVerbose()) {
       context_->GetDiagnostics()->Note(DiagMessage() << "Optimizing APK...");
     }
+    if (!options_.resources_blacklist.empty()) {
+      ResourceFilter filter(options_.resources_blacklist);
+      if (!filter.Consume(context_, apk->GetResourceTable())) {
+        context_->GetDiagnostics()->Error(DiagMessage() << "failed filtering resources");
+        return 1;
+      }
+    }
 
     VersionCollapser collapser;
     if (!collapser.Consume(context_, apk->GetResourceTable())) {
@@ -288,16 +299,62 @@
   OptimizeContext* context_;
 };
 
-bool ExtractWhitelistFromConfig(const std::string& path, OptimizeContext* context,
-                                OptimizeOptions* options) {
+bool ExtractObfuscationWhitelistFromConfig(const std::string& path, OptimizeContext* context,
+                                           OptimizeOptions* options) {
   std::string contents;
   if (!ReadFileToString(path, &contents, true)) {
     context->GetDiagnostics()->Error(DiagMessage()
                                      << "failed to parse whitelist from config file: " << path);
     return false;
   }
-  for (const StringPiece& resource_name : util::Tokenize(contents, ',')) {
-    options->table_flattener_options.whitelisted_resources.insert(resource_name.to_string());
+  for (StringPiece resource_name : util::Tokenize(contents, ',')) {
+    options->table_flattener_options.whitelisted_resources.insert(
+        resource_name.to_string());
+  }
+  return true;
+}
+
+bool ExtractConfig(const std::string& path, OptimizeContext* context,
+                                    OptimizeOptions* options) {
+  std::string content;
+  if (!android::base::ReadFileToString(path, &content, true /*follow_symlinks*/)) {
+    context->GetDiagnostics()->Error(DiagMessage(path) << "failed reading whitelist");
+    return false;
+  }
+
+  size_t line_no = 0;
+  for (StringPiece line : util::Tokenize(content, '\n')) {
+    line_no++;
+    line = util::TrimWhitespace(line);
+    if (line.empty()) {
+      continue;
+    }
+
+    auto split_line = util::Split(line, '#');
+    if (split_line.size() < 2) {
+      context->GetDiagnostics()->Error(DiagMessage(line) << "No # found in line");
+      return false;
+    }
+    StringPiece resource_string = split_line[0];
+    StringPiece directives = split_line[1];
+    ResourceNameRef resource_name;
+    if (!ResourceUtils::ParseResourceName(resource_string, &resource_name)) {
+      context->GetDiagnostics()->Error(DiagMessage(line) << "Malformed resource name");
+      return false;
+    }
+    if (!resource_name.package.empty()) {
+      context->GetDiagnostics()->Error(DiagMessage(line)
+                                       << "Package set for resource. Only use type/name");
+      return false;
+    }
+    for (StringPiece directive : util::Tokenize(directives, ',')) {
+      if (directive == "remove") {
+        options->resources_blacklist.insert(resource_name.ToResourceName());
+      } else if (directive == "no_obfuscate") {
+        options->table_flattener_options.whitelisted_resources.insert(
+            resource_name.entry.to_string());
+      }
+    }
   }
   return true;
 }
@@ -326,6 +383,7 @@
   OptimizeOptions options;
   Maybe<std::string> config_path;
   Maybe<std::string> whitelist_path;
+  Maybe<std::string> resources_config_path;
   Maybe<std::string> target_densities;
   std::vector<std::string> configs;
   std::vector<std::string> split_args;
@@ -344,10 +402,15 @@
               "All the resources that would be unused on devices of the given densities will be \n"
               "removed from the APK.",
               &target_densities)
-          .OptionalFlag("--whitelist-config-path",
+          .OptionalFlag("--whitelist-path",
                         "Path to the whitelist.cfg file containing whitelisted resources \n"
                         "whose names should not be altered in final resource tables.",
                         &whitelist_path)
+          .OptionalFlag("--resources-config-path",
+                        "Path to the resources.cfg file containing the list of resources and \n"
+                        "directives to each resource. \n"
+                        "Format: type/resource_name#[directive][,directive]",
+                        &resources_config_path)
           .OptionalFlagList("-c",
                             "Comma separated list of configurations to include. The default\n"
                             "is all configurations.",
@@ -464,12 +527,19 @@
   if (options.table_flattener_options.collapse_key_stringpool) {
     if (whitelist_path) {
       std::string& path = whitelist_path.value();
-      if (!ExtractWhitelistFromConfig(path, &context, &options)) {
+      if (!ExtractObfuscationWhitelistFromConfig(path, &context, &options)) {
         return 1;
       }
     }
   }
 
+  if (resources_config_path) {
+    std::string& path = resources_config_path.value();
+    if (!ExtractConfig(path, &context, &options)) {
+      return 1;
+    }
+  }
+
   if (!ExtractAppDataFromManifest(&context, apk.get(), &options)) {
     return 1;
   }
diff --git a/tools/aapt2/optimize/ResourceFilter.cpp b/tools/aapt2/optimize/ResourceFilter.cpp
new file mode 100644
index 0000000..250b651
--- /dev/null
+++ b/tools/aapt2/optimize/ResourceFilter.cpp
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "optimize/ResourceFilter.h"
+
+#include "ResourceTable.h"
+
+namespace aapt {
+
+ResourceFilter::ResourceFilter(const std::unordered_set<ResourceName>& blacklist)
+    : blacklist_(blacklist) {
+}
+
+bool ResourceFilter::Consume(IAaptContext* context, ResourceTable* table) {
+  for (auto& package : table->packages) {
+    for (auto& type : package->types) {
+      for (auto it = type->entries.begin(); it != type->entries.end(); ) {
+        ResourceName resource = ResourceName({}, type->type, (*it)->name);
+        if (blacklist_.find(resource) != blacklist_.end()) {
+          it = type->entries.erase(it);
+        } else {
+          ++it;
+        }
+      }
+    }
+  }
+  return true;
+}
+
+}  // namespace aapt
diff --git a/tools/aapt2/optimize/ResourceFilter.h b/tools/aapt2/optimize/ResourceFilter.h
new file mode 100644
index 0000000..d4baf65
--- /dev/null
+++ b/tools/aapt2/optimize/ResourceFilter.h
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef AAPT_OPTIMIZE_RESOURCEFILTER_H
+#define AAPT_OPTIMIZE_RESOURCEFILTER_H
+
+#include "android-base/macros.h"
+
+#include "process/IResourceTableConsumer.h"
+
+#include <unordered_set>
+
+namespace aapt {
+
+// Removes non-whitelisted entries from resource table.
+class ResourceFilter : public IResourceTableConsumer {
+ public:
+  explicit ResourceFilter(const std::unordered_set<ResourceName>& blacklist);
+
+  bool Consume(IAaptContext* context, ResourceTable* table) override;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(ResourceFilter);
+  std::unordered_set<ResourceName> blacklist_;
+};
+
+} // namespace aapt
+
+#endif  // AAPT_OPTIMIZE_RESOURCEFILTER_H
diff --git a/tools/aapt2/optimize/ResourceFilter_test.cpp b/tools/aapt2/optimize/ResourceFilter_test.cpp
new file mode 100644
index 0000000..800b2bf
--- /dev/null
+++ b/tools/aapt2/optimize/ResourceFilter_test.cpp
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "optimize/ResourceFilter.h"
+
+#include "ResourceTable.h"
+#include "test/Test.h"
+
+using ::aapt::test::HasValue;
+using ::testing::Not;
+
+namespace aapt {
+
+TEST(ResourceFilterTest, SomeValuesAreFilteredOut) {
+  std::unique_ptr<IAaptContext> context = test::ContextBuilder().Build();
+  const ConfigDescription default_config = {};
+
+  std::unique_ptr<ResourceTable> table =
+      test::ResourceTableBuilder()
+          .AddString("android:string/notblacklisted", ResourceId{}, default_config, "value")
+          .AddString("android:string/blacklisted", ResourceId{}, default_config, "value")
+          .AddString("android:string/notblacklisted2", ResourceId{}, default_config, "value")
+          .AddString("android:string/blacklisted2", ResourceId{}, default_config, "value")
+          .Build();
+
+  std::unordered_set<ResourceName> blacklist = {
+    ResourceName({}, ResourceType::kString, "blacklisted"),
+    ResourceName({}, ResourceType::kString, "blacklisted2"),
+  };
+
+  ASSERT_TRUE(ResourceFilter(blacklist).Consume(context.get(), table.get()));
+  EXPECT_THAT(table, HasValue("android:string/notblacklisted", default_config));
+  EXPECT_THAT(table, HasValue("android:string/notblacklisted2", default_config));
+  EXPECT_THAT(table, Not(HasValue("android:string/blacklisted", default_config)));
+  EXPECT_THAT(table, Not(HasValue("android:string/blacklisted2", default_config)));
+}
+
+TEST(ResourceFilterTest, TypeIsCheckedBeforeFiltering) {
+  std::unique_ptr<IAaptContext> context = test::ContextBuilder().Build();
+  const ConfigDescription default_config = {};
+
+  std::unique_ptr<ResourceTable> table =
+      test::ResourceTableBuilder()
+          .AddString("android:string/notblacklisted", ResourceId{}, default_config, "value")
+          .AddString("android:string/blacklisted", ResourceId{}, default_config, "value")
+          .AddString("android:drawable/notblacklisted", ResourceId{}, default_config, "value")
+          .AddString("android:drawable/blacklisted", ResourceId{}, default_config, "value")
+          .Build();
+
+  std::unordered_set<ResourceName> blacklist = {
+    ResourceName({}, ResourceType::kString, "blacklisted"),
+  };
+
+  ASSERT_TRUE(ResourceFilter(blacklist).Consume(context.get(), table.get()));
+  EXPECT_THAT(table, HasValue("android:string/notblacklisted", default_config));
+  EXPECT_THAT(table, HasValue("android:drawable/blacklisted", default_config));
+  EXPECT_THAT(table, HasValue("android:drawable/notblacklisted", default_config));
+  EXPECT_THAT(table, Not(HasValue("android:string/blacklisted", default_config)));
+}
+
+}  // namespace aapt
diff --git a/wifi/java/android/net/wifi/WifiConfiguration.java b/wifi/java/android/net/wifi/WifiConfiguration.java
index 1a79a76..f6c67c9 100644
--- a/wifi/java/android/net/wifi/WifiConfiguration.java
+++ b/wifi/java/android/net/wifi/WifiConfiguration.java
@@ -613,37 +613,6 @@
 
     /**
      * @hide
-     * Last time the system tried to connect and failed.
-     */
-    public long lastConnectionFailure;
-
-    /**
-     * @hide
-     * Last time the system tried to roam and failed because of authentication failure or DHCP
-     * RENEW failure.
-     */
-    public long lastRoamingFailure;
-
-    /** @hide */
-    public static int ROAMING_FAILURE_IP_CONFIG = 1;
-    /** @hide */
-    public static int ROAMING_FAILURE_AUTH_FAILURE = 2;
-
-    /**
-     * @hide
-     * Initial amount of time this Wifi configuration gets blacklisted for network switching
-     * because of roaming failure
-     */
-    public long roamingFailureBlackListTimeMilli = 1000;
-
-    /**
-     * @hide
-     * Last roaming failure reason code
-     */
-    public int lastRoamingFailureReason;
-
-    /**
-     * @hide
      * Last time the system was disconnected to this configuration.
      */
     public long lastDisconnected;
@@ -1752,28 +1721,6 @@
             sbuf.append("lastConnected: ").append(TimeUtils.logTimeOfDay(this.lastConnected));
             sbuf.append(" ");
         }
-        if (this.lastConnectionFailure != 0) {
-            sbuf.append('\n');
-            long diff = now_ms - this.lastConnectionFailure;
-            if (diff <= 0) {
-                sbuf.append("lastConnectionFailure since <incorrect> ");
-            } else {
-                sbuf.append("lastConnectionFailure: ").append(Long.toString(diff / 1000));
-                sbuf.append("sec ");
-            }
-        }
-        if (this.lastRoamingFailure != 0) {
-            sbuf.append('\n');
-            long diff = now_ms - this.lastRoamingFailure;
-            if (diff <= 0) {
-                sbuf.append("lastRoamingFailure since <incorrect> ");
-            } else {
-                sbuf.append("lastRoamingFailure: ").append(Long.toString(diff / 1000));
-                sbuf.append("sec ");
-            }
-        }
-        sbuf.append("roamingFailureBlackListTimeMilli: ").
-                append(Long.toString(this.roamingFailureBlackListTimeMilli));
         sbuf.append('\n');
         if (this.linkedConfigurations != null) {
             for (String key : this.linkedConfigurations.keySet()) {
@@ -2118,10 +2065,6 @@
 
             lastConnected = source.lastConnected;
             lastDisconnected = source.lastDisconnected;
-            lastConnectionFailure = source.lastConnectionFailure;
-            lastRoamingFailure = source.lastRoamingFailure;
-            lastRoamingFailureReason = source.lastRoamingFailureReason;
-            roamingFailureBlackListTimeMilli = source.roamingFailureBlackListTimeMilli;
             numScorerOverride = source.numScorerOverride;
             numScorerOverrideAndSwitchedNetwork = source.numScorerOverrideAndSwitchedNetwork;
             numAssociation = source.numAssociation;
@@ -2187,10 +2130,6 @@
         dest.writeInt(lastUpdateUid);
         dest.writeString(creatorName);
         dest.writeString(lastUpdateName);
-        dest.writeLong(lastConnectionFailure);
-        dest.writeLong(lastRoamingFailure);
-        dest.writeInt(lastRoamingFailureReason);
-        dest.writeLong(roamingFailureBlackListTimeMilli);
         dest.writeInt(numScorerOverride);
         dest.writeInt(numScorerOverrideAndSwitchedNetwork);
         dest.writeInt(numAssociation);
@@ -2256,10 +2195,6 @@
                 config.lastUpdateUid = in.readInt();
                 config.creatorName = in.readString();
                 config.lastUpdateName = in.readString();
-                config.lastConnectionFailure = in.readLong();
-                config.lastRoamingFailure = in.readLong();
-                config.lastRoamingFailureReason = in.readInt();
-                config.roamingFailureBlackListTimeMilli = in.readLong();
                 config.numScorerOverride = in.readInt();
                 config.numScorerOverrideAndSwitchedNetwork = in.readInt();
                 config.numAssociation = in.readInt();