merge in oc-release history after reset to master
diff --git a/api/current.txt b/api/current.txt
index bc41d65..6da09e3 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -1234,6 +1234,7 @@
     field public static final int summaryOff = 16843248; // 0x10101f0
     field public static final int summaryOn = 16843247; // 0x10101ef
     field public static final int supportsAssist = 16844016; // 0x10104f0
+    field public static final int supportsDismissingWindow = 16844104; // 0x1010548
     field public static final int supportsLaunchVoiceAssistFromKeyguard = 16844017; // 0x10104f1
     field public static final int supportsLocalInteraction = 16844047; // 0x101050f
     field public static final int supportsPictureInPicture = 16844023; // 0x10104f7
@@ -8959,6 +8960,7 @@
     field public static final java.lang.String EXTRA_CHOOSER_TARGETS = "android.intent.extra.CHOOSER_TARGETS";
     field public static final java.lang.String EXTRA_CHOSEN_COMPONENT = "android.intent.extra.CHOSEN_COMPONENT";
     field public static final java.lang.String EXTRA_CHOSEN_COMPONENT_INTENT_SENDER = "android.intent.extra.CHOSEN_COMPONENT_INTENT_SENDER";
+    field public static final java.lang.String EXTRA_CONTENT_ANNOTATIONS = "android.intent.extra.CONTENT_ANNOTATIONS";
     field public static final java.lang.String EXTRA_DATA_REMOVED = "android.intent.extra.DATA_REMOVED";
     field public static final java.lang.String EXTRA_DOCK_STATE = "android.intent.extra.DOCK_STATE";
     field public static final int EXTRA_DOCK_STATE_CAR = 2; // 0x2
diff --git a/api/system-current.txt b/api/system-current.txt
index 24f9aad..5640eaf 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -1347,6 +1347,7 @@
     field public static final int summaryOff = 16843248; // 0x10101f0
     field public static final int summaryOn = 16843247; // 0x10101ef
     field public static final int supportsAssist = 16844016; // 0x10104f0
+    field public static final int supportsDismissingWindow = 16844104; // 0x1010548
     field public static final int supportsLaunchVoiceAssistFromKeyguard = 16844017; // 0x10104f1
     field public static final int supportsLocalInteraction = 16844047; // 0x101050f
     field public static final int supportsPictureInPicture = 16844023; // 0x10104f7
@@ -9335,6 +9336,7 @@
     field public static final java.lang.String EXTRA_CHOOSER_TARGETS = "android.intent.extra.CHOOSER_TARGETS";
     field public static final java.lang.String EXTRA_CHOSEN_COMPONENT = "android.intent.extra.CHOSEN_COMPONENT";
     field public static final java.lang.String EXTRA_CHOSEN_COMPONENT_INTENT_SENDER = "android.intent.extra.CHOSEN_COMPONENT_INTENT_SENDER";
+    field public static final java.lang.String EXTRA_CONTENT_ANNOTATIONS = "android.intent.extra.CONTENT_ANNOTATIONS";
     field public static final java.lang.String EXTRA_DATA_REMOVED = "android.intent.extra.DATA_REMOVED";
     field public static final java.lang.String EXTRA_DOCK_STATE = "android.intent.extra.DOCK_STATE";
     field public static final int EXTRA_DOCK_STATE_CAR = 2; // 0x2
@@ -41395,6 +41397,8 @@
     method public java.lang.String getDeviceSoftwareVersion();
     method public java.lang.String getGroupIdLevel1();
     method public java.lang.String getIccAuthentication(int, int, java.lang.String);
+    method public java.lang.String getImei();
+    method public java.lang.String getImei(int);
     method public java.lang.String getLine1Number();
     method public java.lang.String getMmsUAProfUrl();
     method public java.lang.String getMmsUserAgent();
diff --git a/api/test-current.txt b/api/test-current.txt
index 4c277e4..714cac9 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -1234,6 +1234,7 @@
     field public static final int summaryOff = 16843248; // 0x10101f0
     field public static final int summaryOn = 16843247; // 0x10101ef
     field public static final int supportsAssist = 16844016; // 0x10104f0
+    field public static final int supportsDismissingWindow = 16844104; // 0x1010548
     field public static final int supportsLaunchVoiceAssistFromKeyguard = 16844017; // 0x10104f1
     field public static final int supportsLocalInteraction = 16844047; // 0x101050f
     field public static final int supportsPictureInPicture = 16844023; // 0x10104f7
@@ -8984,6 +8985,7 @@
     field public static final java.lang.String EXTRA_CHOOSER_TARGETS = "android.intent.extra.CHOOSER_TARGETS";
     field public static final java.lang.String EXTRA_CHOSEN_COMPONENT = "android.intent.extra.CHOSEN_COMPONENT";
     field public static final java.lang.String EXTRA_CHOSEN_COMPONENT_INTENT_SENDER = "android.intent.extra.CHOSEN_COMPONENT_INTENT_SENDER";
+    field public static final java.lang.String EXTRA_CONTENT_ANNOTATIONS = "android.intent.extra.CONTENT_ANNOTATIONS";
     field public static final java.lang.String EXTRA_DATA_REMOVED = "android.intent.extra.DATA_REMOVED";
     field public static final java.lang.String EXTRA_DOCK_STATE = "android.intent.extra.DOCK_STATE";
     field public static final int EXTRA_DOCK_STATE_CAR = 2; // 0x2
diff --git a/core/java/android/app/admin/DeviceAdminReceiver.java b/core/java/android/app/admin/DeviceAdminReceiver.java
index f2a8777..a248bce 100644
--- a/core/java/android/app/admin/DeviceAdminReceiver.java
+++ b/core/java/android/app/admin/DeviceAdminReceiver.java
@@ -768,6 +768,10 @@
     /**
      * Called when a new batch of security logs can be retrieved.
      *
+     * <p>If a secondary user or profile is created, this callback won't be received until all users
+     * become affiliated again (even if security logging is enabled).
+     * See {@link DevicePolicyManager#setAffiliationIds}
+     *
      * <p>This callback is only applicable to device owners.
      *
      * @param context The running context as per {@link #onReceive}.
@@ -782,13 +786,18 @@
      * ever be called when network logging is enabled. The logs can only be retrieved while network
      * logging is enabled.
      *
+     * <p>If a secondary user or profile is created, this callback won't be received until all users
+     * become affiliated again (even if network logging is enabled). It will also no longer be
+     * possible to retrieve the network logs batch with the most recent {@code batchToken} provided
+     * by this callback. See {@link DevicePolicyManager#setAffiliationIds}.
+     *
      * <p>This callback is only applicable to device owners.
      *
      * @param context The running context as per {@link #onReceive}.
      * @param intent The received intent as per {@link #onReceive}.
      * @param batchToken The token representing the current batch of network logs.
      * @param networkLogsCount The total count of events in the current batch of network logs.
-     * @see DevicePolicyManager#retrieveNetworkLogs(ComponentName)
+     * @see DevicePolicyManager#retrieveNetworkLogs
      */
     public void onNetworkLogsAvailable(Context context, Intent intent, long batchToken,
             int networkLogsCount) {
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index 074326f..c95e011 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -3651,15 +3651,16 @@
     /**
      * Called by a device owner to request a bugreport.
      * <p>
-     * There must be only one user on the device, managed by the device owner. Otherwise a
-     * {@link SecurityException} will be thrown.
+     * If the device contains secondary users or profiles, they must be affiliated with the device
+     * owner user. Otherwise a {@link SecurityException} will be thrown. See
+     * {@link #setAffiliationIds}.
      *
      * @param admin Which {@link DeviceAdminReceiver} this request is associated with.
      * @return {@code true} if the bugreport collection started successfully, or {@code false} if it
      *         wasn't triggered because a previous bugreport operation is still active (either the
      *         bugreport is still running or waiting for the user to share or decline)
-     * @throws SecurityException if {@code admin} is not a device owner, or if there are users other
-     *             than the one managed by the device owner.
+     * @throws SecurityException if {@code admin} is not a device owner, or there is at least one
+     *         profile or secondary user that is not affiliated with the device owner user.
      */
     public boolean requestBugreport(@NonNull ComponentName admin) {
         throwIfParentInstance("requestBugreport");
@@ -6631,14 +6632,16 @@
     }
 
     /**
-     * Called by device owner to control the security logging feature. Logging can only be
-     * enabled on single user devices where the sole user is managed by the device owner.
+     * Called by device owner to control the security logging feature.
      *
      * <p> Security logs contain various information intended for security auditing purposes.
      * See {@link SecurityEvent} for details.
      *
-     * <p>There must be only one user on the device, managed by the device owner.
-     * Otherwise a {@link SecurityException} will be thrown.
+     * <p><strong>Note:</strong> The device owner won't be able to retrieve security logs if there
+     * are unaffiliated secondary users or profiles on the device, regardless of whether the
+     * feature is enabled. Logs will be discarded if the internal buffer fills up while waiting for
+     * all users to become affiliated. Therefore it's recommended that affiliation ids are set for
+     * new users as soon as possible after provisioning via {@link #setAffiliationIds}.
      *
      * @param admin Which device owner this request is associated with.
      * @param enabled whether security logging should be enabled or not.
@@ -6680,13 +6683,16 @@
      * <p> Access to the logs is rate limited and it will only return new logs after the device
      * owner has been notified via {@link DeviceAdminReceiver#onSecurityLogsAvailable}.
      *
-     * <p>There must be only one user on the device, managed by the device owner.
-     * Otherwise a {@link SecurityException} will be thrown.
+     * <p>If there is any other user or profile on the device, it must be affiliated with the
+     * device owner. Otherwise a {@link SecurityException} will be thrown. See
+     * {@link #setAffiliationIds}
      *
      * @param admin Which device owner this request is associated with.
      * @return the new batch of security logs which is a list of {@link SecurityEvent},
      * or {@code null} if rate limitation is exceeded or if logging is currently disabled.
-     * @throws SecurityException if {@code admin} is not a device owner.
+     * @throws SecurityException if {@code admin} is not a device owner, or there is at least one
+     * profile or secondary user that is not affiliated with the device owner user.
+     * @see DeviceAdminReceiver#onSecurityLogsAvailable
      */
     public @Nullable List<SecurityEvent> retrieveSecurityLogs(@NonNull ComponentName admin) {
         throwIfParentInstance("retrieveSecurityLogs");
@@ -6726,14 +6732,17 @@
      * will result in {@code null} being returned. The device logs are retrieved from a RAM region
      * which is not guaranteed to be corruption-free during power cycles, as a result be cautious
      * about data corruption when parsing. </strong>
-     * <p>
-     * There must be only one user on the device, managed by the device owner. Otherwise a
-     * {@link SecurityException} will be thrown.
+     *
+     * <p>If there is any other user or profile on the device, it must be affiliated with the
+     * device owner. Otherwise a {@link SecurityException} will be thrown. See
+     * {@link #setAffiliationIds}
      *
      * @param admin Which device owner this request is associated with.
      * @return Device logs from before the latest reboot of the system, or {@code null} if this API
      *         is not supported on the device.
-     * @throws SecurityException if {@code admin} is not a device owner.
+     * @throws SecurityException if {@code admin} is not a device owner, or there is at least one
+     * profile or secondary user that is not affiliated with the device owner user.
+     * @see #retrieveSecurityLogs
      */
     public @Nullable List<SecurityEvent> retrievePreRebootSecurityLogs(
             @NonNull ComponentName admin) {
@@ -6939,6 +6948,12 @@
      * Indicates the entity that controls the device or profile owner. Two users/profiles are
      * affiliated if the set of ids set by their device or profile owners intersect.
      *
+     * <p><strong>Note:</strong> Features that depend on user affiliation (such as security logging
+     * or {@link #bindDeviceAdminServiceAsUser}) won't be available when a secondary user or profile
+     * is created, until it becomes affiliated. Therefore it is recommended that the appropriate
+     * affiliation ids are set by its profile owner as soon as possible after the user/profile is
+     * created.
+     *
      * @param admin Which profile or device owner this request is associated with.
      * @param ids A list of opaque non-empty affiliation ids. Duplicate elements will be ignored.
      *
@@ -7138,15 +7153,19 @@
     }
 
     /**
-     * Called by a device owner to control the network logging feature. Logging can only be
-     * enabled on single user devices where the sole user is managed by the device owner. If a new
-     * user is added on the device, logging is disabled.
+     * Called by a device owner to control the network logging feature.
      *
      * <p> Network logs contain DNS lookup and connect() library call events.
      *
+     * <p><strong>Note:</strong> The device owner won't be able to retrieve network logs if there
+     * are unaffiliated secondary users or profiles on the device, regardless of whether the
+     * feature is enabled. Logs will be discarded if the internal buffer fills up while waiting for
+     * all users to become affiliated. Therefore it's recommended that affiliation ids are set for
+     * new users as soon as possible after provisioning via {@link #setAffiliationIds}.
+     *
      * @param admin Which {@link DeviceAdminReceiver} this request is associated with.
      * @param enabled whether network logging should be enabled or not.
-     * @throws {@link SecurityException} if {@code admin} is not a device owner.
+     * @throws SecurityException if {@code admin} is not a device owner.
      * @see #retrieveNetworkLogs
      */
     public void setNetworkLoggingEnabled(@NonNull ComponentName admin, boolean enabled) {
@@ -7164,7 +7183,7 @@
      * @param admin Which {@link DeviceAdminReceiver} this request is associated with. Can only
      * be {@code null} if the caller has MANAGE_USERS permission.
      * @return {@code true} if network logging is enabled by device owner, {@code false} otherwise.
-     * @throws {@link SecurityException} if {@code admin} is not a device owner and caller has
+     * @throws SecurityException if {@code admin} is not a device owner and caller has
      * no MANAGE_USERS permission
      */
     public boolean isNetworkLoggingEnabled(@Nullable ComponentName admin) {
@@ -7190,12 +7209,19 @@
      * after the device device owner has been notified via
      * {@link DeviceAdminReceiver#onNetworkLogsAvailable}.
      *
+     * <p>If a secondary user or profile is created, calling this method will throw a
+     * {@link SecurityException} until all users become affiliated again. It will also no longer be
+     * possible to retrieve the network logs batch with the most recent batchToken provided
+     * by {@link DeviceAdminReceiver#onNetworkLogsAvailable}. See
+     * {@link DevicePolicyManager#setAffiliationIds}.
+     *
      * @param admin Which {@link DeviceAdminReceiver} this request is associated with.
      * @param batchToken A token of the batch to retrieve
      * @return A new batch of network logs which is a list of {@link NetworkEvent}. Returns
      *        {@code null} if the batch represented by batchToken is no longer available or if
      *        logging is disabled.
-     * @throws {@link SecurityException} if {@code admin} is not a device owner.
+     * @throws SecurityException if {@code admin} is not a device owner, or there is at least one
+     * profile or secondary user that is not affiliated with the device owner user.
      * @see DeviceAdminReceiver#onNetworkLogsAvailable
      */
     public @Nullable List<NetworkEvent> retrieveNetworkLogs(@NonNull ComponentName admin,
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index b05ceaa..d8358f9 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -3843,6 +3843,52 @@
             = "android.intent.extra.CHOOSER_REFINEMENT_INTENT_SENDER";
 
     /**
+     * An {@code ArrayList} of {@code String} annotations describing content for
+     * {@link #ACTION_CHOOSER}.
+     *
+     * <p>If {@link #EXTRA_CONTENT_ANNOTATIONS} is present in an intent used to start a
+     * {@link #ACTION_CHOOSER} activity, the first three annotations will be used to rank apps.</p>
+     *
+     * <p>Annotations should describe the major components or topics of the content. It is up to
+     * apps initiating {@link #ACTION_CHOOSER} to learn and add annotations. Annotations should be
+     * learned in advance, e.g., when creating or saving content, to avoid increasing latency to
+     * start {@link #ACTION_CHOOSER}. Performance on customized annotations can suffer, if they are
+     * rarely used for {@link #ACTION_CHOOSER} in the past 14 days. Therefore, it is recommended to
+     * use the following annotations when applicable:</p>
+     * <ul>
+     *     <li>"product": represents that the topic of the content is mainly about products, e.g.,
+     *     health & beauty, and office supplies.</li>
+     *     <li>"emotion": represents that the topic of the content is mainly about emotions, e.g.,
+     *     happy, and sad.</li>
+     *     <li>"person": represents that the topic of the content is mainly about persons, e.g.,
+     *     face, finger, standing, and walking.</li>
+     *     <li>"child": represents that the topic of the content is mainly about children, e.g.,
+     *     child, and baby.</li>
+     *     <li>"selfie": represents that the topic of the content is mainly about selfies.</li>
+     *     <li>"crowd": represents that the topic of the content is mainly about crowds.</li>
+     *     <li>"party": represents that the topic of the content is mainly about parties.</li>
+     *     <li>"animal": represent that the topic of the content is mainly about animals.</li>
+     *     <li>"plant": represents that the topic of the content is mainly about plants, e.g.,
+     *     flowers.</li>
+     *     <li>"vacation": represents that the topic of the content is mainly about vacations.</li>
+     *     <li>"fashion": represents that the topic of the content is mainly about fashion, e.g.
+     *     sunglasses, jewelry, handbags and clothing.</li>
+     *     <li>"material": represents that the topic of the content is mainly about materials, e.g.,
+     *     paper, and silk.</li>
+     *     <li>"vehicle": represents that the topic of the content is mainly about vehicles, like
+     *     cars, and boats.</li>
+     *     <li>"document": represents that the topic of the content is mainly about documents, e.g.
+     *     posters.</li>
+     *     <li>"design": represents that the topic of the content is mainly about design, e.g. arts
+     *     and designs of houses.</li>
+     *     <li>"holiday": represents that the topic of the content is mainly about holidays, e.g.,
+     *     Christmas and Thanksgiving.</li>
+     * </ul>
+     */
+    public static final String EXTRA_CONTENT_ANNOTATIONS
+            = "android.intent.extra.CONTENT_ANNOTATIONS";
+
+    /**
      * A {@link ResultReceiver} used to return data back to the sender.
      *
      * <p>Used to complete an app-specific
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index a48762b..083e4cc6 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -950,8 +950,8 @@
         try {
             final byte[] bytes = IoUtils.readFileAsByteArray(cacheFile.getAbsolutePath());
             return fromCacheEntry(bytes);
-        } catch (IOException ioe) {
-            Slog.w(TAG, "Error reading package cache: ", ioe);
+        } catch (Exception e) {
+            Slog.w(TAG, "Error reading package cache: ", e);
 
             // If something went wrong while reading the cache entry, delete the cache file
             // so that we regenerate it the next time.
diff --git a/core/java/android/hardware/SensorEvent.java b/core/java/android/hardware/SensorEvent.java
index d67b6a8..9b72757a 100644
--- a/core/java/android/hardware/SensorEvent.java
+++ b/core/java/android/hardware/SensorEvent.java
@@ -438,7 +438,8 @@
      * Soft iron - These distortions arise due to the interaction with the earth's magnetic
      * field.
      * </p>
-     * <h4> {@link android.hardware.Sensor#TYPE_GAME_ROTATION_VECTOR}:</h4>
+     * <h4> {@link android.hardware.Sensor#TYPE_GAME_ROTATION_VECTOR
+     * Sensor.TYPE_GAME_ROTATION_VECTOR}:</h4>
      * Identical to {@link android.hardware.Sensor#TYPE_ROTATION_VECTOR} except that it
      * doesn't use the geomagnetic field. Therefore the Y axis doesn't
      * point north, but instead to some other reference, that reference is
@@ -482,22 +483,6 @@
      * <p><b>Pro Tip:</b> Always use the length of the values array while performing operations
      * on it. In earlier versions, this used to be always 3 which has changed now. </p>
      *
-     * @see GeomagneticField
-     *
-     * <h4> {@link android.hardware.Sensor#TYPE_DEVICE_ORIENTATION
-     * Sensor.TYPE_DEVICE_ORIENTATION}:</h4>
-     * The current device orientation will be available in values[0]. The only
-     * available values are:
-     * <ul>
-     * <li> 0: device is in default orientation (Y axis is vertical and points up)
-     * <li> 1: device is rotated 90 degrees counter-clockwise from default
-     *         orientation (X axis is vertical and points up)
-     * <li> 2: device is rotated 180 degrees from default orientation (Y axis is
-     *         vertical and points down)
-     * <li> 3: device is rotated 90 degrees clockwise from default orientation (X axis
-     *         is vertical and points down)
-     * </ul>
-     *
      *   <h4>{@link android.hardware.Sensor#TYPE_POSE_6DOF
      * Sensor.TYPE_POSE_6DOF}:</h4>
      *
@@ -605,6 +590,8 @@
      * temperature compensation is allowed).
      * x_bias, y_bias, z_bias are the estimated biases.
      * </p>
+     *
+     * @see GeomagneticField
      */
     public final float[] values;
 
diff --git a/core/java/android/os/HwParcel.java b/core/java/android/os/HwParcel.java
index c7612d1..a265dd0 100644
--- a/core/java/android/os/HwParcel.java
+++ b/core/java/android/os/HwParcel.java
@@ -212,7 +212,7 @@
     public native final HwBlob readBuffer();
 
     public native final HwBlob readEmbeddedBuffer(
-            long parentHandle, long offset);
+            long parentHandle, long offset, boolean nullable);
 
     public native final void writeBuffer(HwBlob blob);
 
diff --git a/core/java/android/os/INetworkManagementService.aidl b/core/java/android/os/INetworkManagementService.aidl
index 1c3d6bd..dbe2f6d 100644
--- a/core/java/android/os/INetworkManagementService.aidl
+++ b/core/java/android/os/INetworkManagementService.aidl
@@ -312,11 +312,6 @@
      */
     void setDnsConfigurationForNetwork(int netId, in String[] servers, String domains);
 
-    /**
-     * Bind name servers to a network in the DNS resolver.
-     */
-    void setDnsServersForNetwork(int netId, in String[] servers, String domains);
-
     void setFirewallEnabled(boolean enabled);
     boolean isFirewallEnabled();
     void setFirewallInterfaceRule(String iface, boolean allow);
diff --git a/core/java/android/os/IRecoverySystem.aidl b/core/java/android/os/IRecoverySystem.aidl
index c5ceecd..1ee83ae 100644
--- a/core/java/android/os/IRecoverySystem.aidl
+++ b/core/java/android/os/IRecoverySystem.aidl
@@ -25,5 +25,5 @@
     boolean uncrypt(in String packageFile, IRecoverySystemProgressListener listener);
     boolean setupBcb(in String command);
     boolean clearBcb();
-    void rebootRecoveryWithCommand(in String command);
+    void rebootRecoveryWithCommand(in String command, in boolean update);
 }
diff --git a/core/java/android/os/RecoverySystem.java b/core/java/android/os/RecoverySystem.java
index d48431a..7f9ea438 100644
--- a/core/java/android/os/RecoverySystem.java
+++ b/core/java/android/os/RecoverySystem.java
@@ -491,15 +491,10 @@
                 command += securityArg;
             }
 
+            // RECOVERY_SERVICE writes to BCB (bootloader control block) and triggers the reboot.
             RecoverySystem rs = (RecoverySystem) context.getSystemService(
                     Context.RECOVERY_SERVICE);
-            if (!rs.setupBcb(command)) {
-                throw new IOException("Setup BCB failed");
-            }
-
-            // Having set up the BCB (bootloader control block), go ahead and reboot
-            PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
-            pm.reboot(PowerManager.REBOOT_RECOVERY_UPDATE);
+            rs.rebootRecoveryWithCommand(command, true /* update */);
 
             throw new IOException("Reboot failed (no permissions?)");
         }
@@ -713,7 +708,7 @@
         // Write the command into BCB (bootloader control block) and boot from
         // there. Will not return unless failed.
         RecoverySystem rs = (RecoverySystem) context.getSystemService(Context.RECOVERY_SERVICE);
-        rs.rebootRecoveryWithCommand(command.toString());
+        rs.rebootRecoveryWithCommand(command.toString(), false);
 
         throw new IOException("Reboot failed (no permissions?)");
     }
@@ -913,9 +908,9 @@
      * Talks to RecoverySystemService via Binder to set up the BCB command and
      * reboot into recovery accordingly.
      */
-    private void rebootRecoveryWithCommand(String command) {
+    private void rebootRecoveryWithCommand(String command, boolean update) {
         try {
-            mService.rebootRecoveryWithCommand(command);
+            mService.rebootRecoveryWithCommand(command, update);
         } catch (RemoteException ignored) {
         }
     }
diff --git a/core/java/android/view/inputmethod/InputMethodInfo.java b/core/java/android/view/inputmethod/InputMethodInfo.java
index 5c8e6dc..b6da1d8 100644
--- a/core/java/android/view/inputmethod/InputMethodInfo.java
+++ b/core/java/android/view/inputmethod/InputMethodInfo.java
@@ -57,6 +57,7 @@
  * @attr ref android.R.styleable#InputMethod_settingsActivity
  * @attr ref android.R.styleable#InputMethod_isDefault
  * @attr ref android.R.styleable#InputMethod_supportsSwitchingToNextInputMethod
+ * @attr ref android.R.styleable#InputMethod_supportsDismissingWindow
  */
 public final class InputMethodInfo implements Parcelable {
     static final String TAG = "InputMethodInfo";
@@ -104,6 +105,11 @@
     private final boolean mSupportsSwitchingToNextInputMethod;
 
     /**
+     * The flag whether this IME supports ways to dismiss its window (e.g. dismiss button.)
+     */
+    private final boolean mSupportsDismissingWindow;
+
+    /**
      * Constructor.
      *
      * @param context The Context in which we are parsing the input method.
@@ -132,6 +138,7 @@
         mId = new ComponentName(si.packageName, si.name).flattenToShortString();
         boolean isAuxIme = true;
         boolean supportsSwitchingToNextInputMethod = false; // false as default
+        boolean supportsDismissingWindow = false; // false as default
         mForceDefault = false;
 
         PackageManager pm = context.getPackageManager();
@@ -171,6 +178,8 @@
             supportsSwitchingToNextInputMethod = sa.getBoolean(
                     com.android.internal.R.styleable.InputMethod_supportsSwitchingToNextInputMethod,
                     false);
+            supportsDismissingWindow = sa.getBoolean(
+                    com.android.internal.R.styleable.InputMethod_supportsDismissingWindow, false);
             sa.recycle();
 
             final int depth = parser.getDepth();
@@ -242,6 +251,7 @@
         mIsDefaultResId = isDefaultResId;
         mIsAuxIme = isAuxIme;
         mSupportsSwitchingToNextInputMethod = supportsSwitchingToNextInputMethod;
+        mSupportsDismissingWindow = supportsDismissingWindow;
     }
 
     InputMethodInfo(Parcel source) {
@@ -250,6 +260,7 @@
         mIsDefaultResId = source.readInt();
         mIsAuxIme = source.readInt() == 1;
         mSupportsSwitchingToNextInputMethod = source.readInt() == 1;
+        mSupportsDismissingWindow = source.readInt() == 1;
         mService = ResolveInfo.CREATOR.createFromParcel(source);
         mSubtypes = new InputMethodSubtypeArray(source);
         mForceDefault = false;
@@ -260,8 +271,10 @@
      */
     public InputMethodInfo(String packageName, String className,
             CharSequence label, String settingsActivity) {
-        this(buildDummyResolveInfo(packageName, className, label), false, settingsActivity, null,
-                0, false /* forceDefault */, true /* supportsSwitchingToNextInputMethod */);
+        this(buildDummyResolveInfo(packageName, className, label), false /* isAuxIme */,
+                settingsActivity, null /* subtypes */, 0 /* isDefaultResId */,
+                false /* forceDefault */, true /* supportsSwitchingToNextInputMethod */,
+                true /* supportsDismissingWindow */);
     }
 
     /**
@@ -271,17 +284,18 @@
     public InputMethodInfo(ResolveInfo ri, boolean isAuxIme,
             String settingsActivity, List<InputMethodSubtype> subtypes, int isDefaultResId,
             boolean forceDefault) {
-        this(ri, isAuxIme, settingsActivity, subtypes, isDefaultResId,
-                forceDefault, true /* supportsSwitchingToNextInputMethod */);
+        this(ri, isAuxIme, settingsActivity, subtypes, isDefaultResId, forceDefault,
+                 true /* supportsSwitchingToNextInputMethod */,
+                 true /* supportsDismissingWindow */);
     }
 
     /**
      * Temporary API for creating a built-in input method for test.
      * @hide
      */
-    public InputMethodInfo(ResolveInfo ri, boolean isAuxIme,
-            String settingsActivity, List<InputMethodSubtype> subtypes, int isDefaultResId,
-            boolean forceDefault, boolean supportsSwitchingToNextInputMethod) {
+    public InputMethodInfo(ResolveInfo ri, boolean isAuxIme, String settingsActivity,
+            List<InputMethodSubtype> subtypes, int isDefaultResId, boolean forceDefault,
+            boolean supportsSwitchingToNextInputMethod, boolean supportsDismissingWindow) {
         final ServiceInfo si = ri.serviceInfo;
         mService = ri;
         mId = new ComponentName(si.packageName, si.name).flattenToShortString();
@@ -291,6 +305,7 @@
         mSubtypes = new InputMethodSubtypeArray(subtypes);
         mForceDefault = forceDefault;
         mSupportsSwitchingToNextInputMethod = supportsSwitchingToNextInputMethod;
+        mSupportsDismissingWindow = supportsDismissingWindow;
     }
 
     private static ResolveInfo buildDummyResolveInfo(String packageName, String className,
@@ -431,7 +446,8 @@
     public void dump(Printer pw, String prefix) {
         pw.println(prefix + "mId=" + mId
                 + " mSettingsActivityName=" + mSettingsActivityName
-                + " mSupportsSwitchingToNextInputMethod=" + mSupportsSwitchingToNextInputMethod);
+                + " mSupportsSwitchingToNextInputMethod=" + mSupportsSwitchingToNextInputMethod
+                + " mSupportsDismissingWindow=" + mSupportsDismissingWindow);
         pw.println(prefix + "mIsDefaultResId=0x"
                 + Integer.toHexString(mIsDefaultResId));
         pw.println(prefix + "Service:");
@@ -484,6 +500,14 @@
     }
 
     /**
+     * @return true if this input method supports ways to dismiss its window.
+     * @hide
+     */
+    public boolean supportsDismissingWindow() {
+        return mSupportsDismissingWindow;
+    }
+
+    /**
      * Used to package this object into a {@link Parcel}.
      *
      * @param dest The {@link Parcel} to be written.
@@ -496,6 +520,7 @@
         dest.writeInt(mIsDefaultResId);
         dest.writeInt(mIsAuxIme ? 1 : 0);
         dest.writeInt(mSupportsSwitchingToNextInputMethod ? 1 : 0);
+        dest.writeInt(mSupportsDismissingWindow ? 1 : 0);
         mService.writeToParcel(dest, flags);
         mSubtypes.writeToParcel(dest);
     }
diff --git a/core/java/com/android/internal/app/ChooserActivity.java b/core/java/com/android/internal/app/ChooserActivity.java
index 7878611..d4baa18 100644
--- a/core/java/com/android/internal/app/ChooserActivity.java
+++ b/core/java/com/android/internal/app/ChooserActivity.java
@@ -418,7 +418,7 @@
                 }
             }
         }
-        updateChooserCounts(target);
+        updateModelAndChooserCounts(target);
         return super.onTargetSelected(target, alwaysCheck);
     }
 
@@ -575,27 +575,18 @@
         // Do nothing. We'll send the voice stuff ourselves.
     }
 
-    void updateChooserCounts(TargetInfo info) {
+    void updateModelAndChooserCounts(TargetInfo info) {
         if (info != null) {
-            UsageStatsManager usageStatsManager =
-                    (UsageStatsManager) getSystemService(Context.USAGE_STATS_SERVICE);
-            if (usageStatsManager == null) {
-                if (DEBUG) {
-                    Log.d(TAG, "Can not start UsageStatsManager");
-                }
-                return;
-            }
             final ResolveInfo ri = info.getResolveInfo();
             Intent targetIntent = getTargetIntent();
             if (ri != null && ri.activityInfo != null && targetIntent != null) {
-                usageStatsManager.reportChooserSelection(ri.activityInfo.packageName, getUserId(),
-                        targetIntent.getType(), null, targetIntent.getAction());
                 if (mAdapter != null) {
                     mAdapter.updateModel(info.getResolvedComponentName());
+                    mAdapter.updateChooserCounts(ri.activityInfo.packageName, getUserId(),
+                            targetIntent.getAction());
                 }
                 if (DEBUG) {
                     Log.d(TAG, "ResolveInfo Package is " + ri.activityInfo.packageName);
-                    Log.d(TAG, "Annotation to be updated is " + targetIntent.getType());
                     Log.d(TAG, "Action to be updated is " + targetIntent.getAction());
                 }
             } else if(DEBUG) {
@@ -618,7 +609,7 @@
         } else {
             TargetInfo clonedTarget = selectedTarget.cloneFilledIn(matchingIntent, 0);
             if (super.onTargetSelected(clonedTarget, false)) {
-                updateChooserCounts(clonedTarget);
+                updateModelAndChooserCounts(clonedTarget);
                 finish();
                 return;
             }
diff --git a/core/java/com/android/internal/app/ResolverActivity.java b/core/java/com/android/internal/app/ResolverActivity.java
index 0950630..d734d17 100644
--- a/core/java/com/android/internal/app/ResolverActivity.java
+++ b/core/java/com/android/internal/app/ResolverActivity.java
@@ -1299,6 +1299,10 @@
             mResolverListController.updateModel(componentName);
         }
 
+        public void updateChooserCounts(String packageName, int userId, String action) {
+            mResolverListController.updateChooserCounts(packageName, userId, action);
+        }
+
         /**
          * Rebuild the list of resolvers. In some cases some parts will need some asynchronous work
          * to complete.
diff --git a/core/java/com/android/internal/app/ResolverComparator.java b/core/java/com/android/internal/app/ResolverComparator.java
index 45fad97f..d9ab47e 100644
--- a/core/java/com/android/internal/app/ResolverComparator.java
+++ b/core/java/com/android/internal/app/ResolverComparator.java
@@ -52,6 +52,8 @@
 
     private static final boolean DEBUG = false;
 
+    private static final int NUM_OF_TOP_ANNOTATIONS_TO_USE = 3;
+
     // One week
     private static final long USAGE_STATS_PERIOD = 1000 * 60 * 60 * 24 * 7;
 
@@ -74,7 +76,8 @@
     private final long mSinceTime;
     private final LinkedHashMap<ComponentName, ScoredTarget> mScoredTargets = new LinkedHashMap<>();
     private final String mReferrerPackage;
-    public String mContentType;
+    private String mContentType;
+    private String[] mAnnotations;
     private String mAction;
     private LogisticRegressionAppRanker mRanker;
 
@@ -91,10 +94,26 @@
         mSinceTime = mCurrentTime - USAGE_STATS_PERIOD;
         mStats = mUsm.queryAndAggregateUsageStats(mSinceTime, mCurrentTime);
         mContentType = intent.getType();
+        getContentAnnotations(intent);
         mAction = intent.getAction();
         mRanker = new LogisticRegressionAppRanker(context);
     }
 
+    public void getContentAnnotations(Intent intent) {
+        ArrayList<String> annotations = intent.getStringArrayListExtra(
+                Intent.EXTRA_CONTENT_ANNOTATIONS);
+        if (annotations != null) {
+            int size = annotations.size();
+            if (size > NUM_OF_TOP_ANNOTATIONS_TO_USE) {
+                size = NUM_OF_TOP_ANNOTATIONS_TO_USE;
+            }
+            mAnnotations = new String[size];
+            for (int i = 0; i < size; i++) {
+                mAnnotations[i] = annotations.get(i);
+            }
+        }
+    }
+
     public void compute(List<ResolvedComponentInfo> targets) {
         mScoredTargets.clear();
 
@@ -132,12 +151,18 @@
                 if (launched > mostLaunched) {
                     mostLaunched = launched;
                 }
-                // TODO(kanlig): get and combine counts of categories.
 
                 int selected = 0;
                 if (pkStats.mChooserCounts != null && mAction != null
                         && pkStats.mChooserCounts.get(mAction) != null) {
                     selected = pkStats.mChooserCounts.get(mAction).getOrDefault(mContentType, 0);
+                    if (mAnnotations != null) {
+                        final int size = mAnnotations.length;
+                        for (int i = 0; i < size; i++) {
+                            selected += pkStats.mChooserCounts.get(mAction)
+                                    .getOrDefault(mAnnotations[i], 0);
+                        }
+                    }
                 }
                 if (DEBUG) {
                     if (mAction == null) {
@@ -288,6 +313,12 @@
         }
     }
 
+    public void updateChooserCounts(String packageName, int userId, String action) {
+        if (mUsm != null) {
+            mUsm.reportChooserSelection(packageName, userId, mContentType, mAnnotations, action);
+        }
+    }
+
     public void updateModel(ComponentName componentName) {
         if (mScoredTargets == null || componentName == null ||
                 !mScoredTargets.containsKey(componentName)) {
diff --git a/core/java/com/android/internal/app/ResolverListController.java b/core/java/com/android/internal/app/ResolverListController.java
index d864a31..f88f6f9 100644
--- a/core/java/com/android/internal/app/ResolverListController.java
+++ b/core/java/com/android/internal/app/ResolverListController.java
@@ -224,4 +224,10 @@
             mResolverComparator.updateModel(componentName);
         }
     }
+
+    public void updateChooserCounts(String packageName, int userId, String action) {
+        if (mResolverComparator != null) {
+            mResolverComparator.updateChooserCounts(packageName, userId, action);
+        }
+    }
 }
diff --git a/core/jni/android_os_HwParcel.cpp b/core/jni/android_os_HwParcel.cpp
index aefdc84..1bd2333 100644
--- a/core/jni/android_os_HwParcel.cpp
+++ b/core/jni/android_os_HwParcel.cpp
@@ -822,7 +822,8 @@
 }
 
 static jobject JHwParcel_native_readEmbeddedBuffer(
-        JNIEnv *env, jobject thiz, jlong parentHandle, jlong offset) {
+        JNIEnv *env, jobject thiz, jlong parentHandle, jlong offset,
+        jboolean nullable) {
     hardware::Parcel *parcel =
         JHwParcel::GetNativeContext(env, thiz)->getParcel();
 
@@ -830,11 +831,15 @@
 
     const void *ptr;
     status_t status =
-        parcel->readEmbeddedBuffer(&childHandle, parentHandle, offset, &ptr);
+        parcel->readNullableEmbeddedBuffer(&childHandle, parentHandle, offset,
+                &ptr);
 
     if (status != OK) {
         jniThrowException(env, "java/util/NoSuchElementException", NULL);
         return 0;
+    } else if (status == OK && !nullable && ptr == nullptr) {
+        jniThrowException(env, "java/lang/NullPointerException", NULL);
+        return 0;
     }
 
     return JHwBlob::NewObject(env, ptr, childHandle);
@@ -945,7 +950,7 @@
     { "readBuffer", "()L" PACKAGE_PATH "/HwBlob;",
         (void *)JHwParcel_native_readBuffer },
 
-    { "readEmbeddedBuffer", "(JJ)L" PACKAGE_PATH "/HwBlob;",
+    { "readEmbeddedBuffer", "(JJZ)L" PACKAGE_PATH "/HwBlob;",
         (void *)JHwParcel_native_readEmbeddedBuffer },
 
     { "writeBuffer", "(L" PACKAGE_PATH "/HwBlob;)V",
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index 5458e7c..dd33718 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -3178,6 +3178,18 @@
              and subtype in order to provide the consistent user experience in switching
              between IMEs and subtypes. -->
         <attr name="supportsSwitchingToNextInputMethod" format="boolean" />
+        <!-- Set to true if this input method supports ways to dismiss the windows assigned to
+             the input method (e.g. a dismiss button rendered by the input method itself).  The
+             System UI may optimize the UI by not showing system-level dismiss button if this
+             value is true.
+             <p> Must be a boolean value, either "true" or "false". The default value is "false".
+             <p> This may also be a reference to a resource (in the form "@[package:]type:name")
+             or theme attribute (in the form "?[package:]type:name") containing a value of this
+             type.
+             <p> A UI element that dismisses the input method window should report
+             {@link android.view.accessibility.AccessibilityNodeInfo#ACTION_DISMISS} action, so
+             that accessibility services can handle it accordingly. -->
+        <attr name="supportsDismissingWindow" format="boolean" />
     </declare-styleable>
 
     <!-- This is the subtype of InputMethod. Subtype can describe locales (e.g. en_US, fr_FR...)
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index 73cba89..064d31e 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -2784,6 +2784,7 @@
         <public name="focusedByDefault" />
         <public name="appCategory" />
         <public name="autoSizeMaxTextSize" />
+        <public name="supportsDismissingWindow" />
     </public-group>
 
     <public-group type="style" first-id="0x010302e0">
diff --git a/core/tests/coretests/res/xml/ime_meta.xml b/core/tests/coretests/res/xml/ime_meta.xml
new file mode 100644
index 0000000..a975718
--- /dev/null
+++ b/core/tests/coretests/res/xml/ime_meta.xml
@@ -0,0 +1,27 @@
+<?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.
+-->
+
+<input-method
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:settingsActivity="com.android.inputmethod.latin.settings.SettingsActivity"
+>
+  <subtype
+      android:label="subtype1"
+      android:imeSubtypeLocale="en_US"
+      android:imeSubtypeMode="keyboard" />
+</input-method>
diff --git a/core/tests/coretests/res/xml/ime_meta_dismiss.xml b/core/tests/coretests/res/xml/ime_meta_dismiss.xml
new file mode 100644
index 0000000..59f8ecc
--- /dev/null
+++ b/core/tests/coretests/res/xml/ime_meta_dismiss.xml
@@ -0,0 +1,28 @@
+<?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.
+-->
+
+<input-method
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:settingsActivity="com.android.inputmethod.latin.settings.SettingsActivity"
+    android:supportsDismissingWindow="true"
+>
+  <subtype
+      android:label="subtype1"
+      android:imeSubtypeLocale="en_US"
+      android:imeSubtypeMode="keyboard" />
+</input-method>
diff --git a/core/tests/coretests/res/xml/ime_meta_sw_next.xml b/core/tests/coretests/res/xml/ime_meta_sw_next.xml
new file mode 100644
index 0000000..2e2ee33
--- /dev/null
+++ b/core/tests/coretests/res/xml/ime_meta_sw_next.xml
@@ -0,0 +1,28 @@
+<?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.
+-->
+
+<input-method
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:settingsActivity="com.android.inputmethod.latin.settings.SettingsActivity"
+    android:supportsSwitchingToNextInputMethod="true"
+>
+  <subtype
+      android:label="subtype1"
+      android:imeSubtypeLocale="en_US"
+      android:imeSubtypeMode="keyboard" />
+</input-method>
diff --git a/core/tests/coretests/src/android/view/inputmethod/InputMethodInfoTest.java b/core/tests/coretests/src/android/view/inputmethod/InputMethodInfoTest.java
new file mode 100644
index 0000000..23dc80f
--- /dev/null
+++ b/core/tests/coretests/src/android/view/inputmethod/InputMethodInfoTest.java
@@ -0,0 +1,111 @@
+/*
+ * 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.inputmethod;
+
+import static org.hamcrest.CoreMatchers.equalTo;
+import static org.hamcrest.CoreMatchers.is;
+import static org.junit.Assert.assertThat;
+
+import android.annotation.XmlRes;
+import android.content.Context;
+import android.content.pm.ResolveInfo;
+import android.content.pm.ServiceInfo;
+import android.os.Bundle;
+import android.os.Parcel;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.runner.AndroidJUnit4;
+
+import com.android.frameworks.coretests.R;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+public class InputMethodInfoTest {
+
+    @Test
+    public void testEqualsAndHashCode() throws Exception {
+        final InputMethodInfo imi = buildInputMethodForTest(R.xml.ime_meta);
+        final InputMethodInfo clone = cloneViaParcel(imi);
+
+        assertThat(clone.equals(imi), is(true));
+        assertThat(clone.hashCode(), equalTo(imi.hashCode()));
+    }
+
+    @Test
+    public void testBooleanAttributes_DefaultValues() throws Exception {
+        final InputMethodInfo imi = buildInputMethodForTest(R.xml.ime_meta);
+
+        assertThat(imi.supportsSwitchingToNextInputMethod(), is(false));
+        assertThat(imi.supportsDismissingWindow(), is(false));
+
+        final InputMethodInfo clone = cloneViaParcel(imi);
+
+        assertThat(clone.supportsSwitchingToNextInputMethod(), is(false));
+        assertThat(clone.supportsDismissingWindow(), is(false));
+    }
+
+    @Test
+    public void testSupportsSwitchingToNextInputMethod() throws Exception {
+        final InputMethodInfo imi = buildInputMethodForTest(R.xml.ime_meta_sw_next);
+
+        assertThat(imi.supportsSwitchingToNextInputMethod(), is(true));
+
+        final InputMethodInfo clone = cloneViaParcel(imi);
+
+        assertThat(clone.supportsSwitchingToNextInputMethod(), is(true));
+    }
+
+    @Test
+    public void testSupportsDismissingWindow() throws Exception {
+        final InputMethodInfo imi = buildInputMethodForTest(R.xml.ime_meta_dismiss);
+
+        assertThat(imi.supportsDismissingWindow(), is(true));
+
+        final InputMethodInfo clone = cloneViaParcel(imi);
+
+        assertThat(clone.supportsDismissingWindow(), is(true));
+    }
+
+    private InputMethodInfo buildInputMethodForTest(final @XmlRes int metaDataRes)
+            throws Exception {
+        final Context context = InstrumentationRegistry.getContext();
+        final ServiceInfo serviceInfo = new ServiceInfo();
+        serviceInfo.applicationInfo = context.getApplicationInfo();
+        serviceInfo.packageName = context.getPackageName();
+        serviceInfo.name = "DummyImeForTest";
+        serviceInfo.metaData = new Bundle();
+        serviceInfo.metaData.putInt(InputMethod.SERVICE_META_DATA, metaDataRes);
+        final ResolveInfo resolveInfo = new ResolveInfo();
+        resolveInfo.serviceInfo = serviceInfo;
+        return new InputMethodInfo(context, resolveInfo, null /* additionalSubtypesMap */);
+    }
+
+    private InputMethodInfo cloneViaParcel(final InputMethodInfo original) {
+        Parcel parcel = null;
+        try {
+            parcel = Parcel.obtain();
+            original.writeToParcel(parcel, 0);
+            parcel.setDataPosition(0);
+            return InputMethodInfo.CREATOR.createFromParcel(parcel);
+        } finally {
+            if (parcel != null) {
+                parcel.recycle();
+            }
+        }
+    }
+}
diff --git a/core/tests/coretests/src/com/android/internal/app/ChooserActivityTest.java b/core/tests/coretests/src/com/android/internal/app/ChooserActivityTest.java
index 8385b69..aab4698 100644
--- a/core/tests/coretests/src/com/android/internal/app/ChooserActivityTest.java
+++ b/core/tests/coretests/src/com/android/internal/app/ChooserActivityTest.java
@@ -124,7 +124,7 @@
     }
 
     @Test
-    public void updateChooserCountsAfterUserSelection() throws InterruptedException {
+    public void updateChooserCountsAndModelAfterUserSelection() throws InterruptedException {
         Intent sendIntent = createSendImageIntent();
         List<ResolvedComponentInfo> resolvedComponentInfos = createResolvedComponentsForTest(2);
 
@@ -142,19 +142,15 @@
         sOverrides.onSafelyStartCallback = targetInfo -> {
             return true;
         };
-        String action = sendIntent.getAction();
-        String annotation = sendIntent.getType();
         ResolveInfo toChoose = resolvedComponentInfos.get(0).getResolveInfoAt(0);
-        String packageName = toChoose.activityInfo.packageName;
-        long toChooseCount = getCount(usm, packageName, action, annotation);
         onView(withText(toChoose.activityInfo.name))
                 .perform(click());
         waitForIdle();
         verify(sOverrides.resolverListController, times(1))
+                .updateChooserCounts(Mockito.anyString(), Mockito.anyInt(), Mockito.anyString());
+        verify(sOverrides.resolverListController, times(1))
                 .updateModel(toChoose.activityInfo.getComponentName());
         assertThat(activity.getIsSelected(), is(true));
-        long updatedCount = getCount(usm, packageName, action, annotation);
-        assertThat(updatedCount, is(toChooseCount + 1l));
     }
 
     @Test
diff --git a/core/tests/coretests/src/com/android/internal/inputmethod/InputMethodSubtypeSwitchingControllerTest.java b/core/tests/coretests/src/com/android/internal/inputmethod/InputMethodSubtypeSwitchingControllerTest.java
index ba5206a..34c34d7 100644
--- a/core/tests/coretests/src/com/android/internal/inputmethod/InputMethodSubtypeSwitchingControllerTest.java
+++ b/core/tests/coretests/src/com/android/internal/inputmethod/InputMethodSubtypeSwitchingControllerTest.java
@@ -74,7 +74,8 @@
         }
         final InputMethodInfo imi = new InputMethodInfo(ri, DUMMY_IS_AUX_IME,
                 DUMMY_SETTING_ACTIVITY_NAME, subtypes, DUMMY_IS_DEFAULT_RES_ID,
-                DUMMY_FORCE_DEFAULT, supportsSwitchingToNextInputMethod);
+                DUMMY_FORCE_DEFAULT, supportsSwitchingToNextInputMethod,
+                false /* supportsDismissingWindow */);
         if (subtypes == null) {
             items.add(new ImeSubtypeListItem(imeName, null /* variableName */, imi,
                     NOT_A_SUBTYPE_ID, null, SYSTEM_LOCALE));
diff --git a/packages/SettingsLib/tests/integ/src/com/android/settingslib/users/AppRestrictionsHelperTest.java b/packages/SettingsLib/tests/integ/src/com/android/settingslib/users/AppRestrictionsHelperTest.java
index f33362f..4df199c 100644
--- a/packages/SettingsLib/tests/integ/src/com/android/settingslib/users/AppRestrictionsHelperTest.java
+++ b/packages/SettingsLib/tests/integ/src/com/android/settingslib/users/AppRestrictionsHelperTest.java
@@ -159,14 +159,14 @@
         for (String pkg : defaultImes) {
             final ResolveInfo ri = createResolveInfoForSystemApp(pkg);
             final InputMethodInfo inputMethodInfo = new InputMethodInfo(
-                    ri, false, null, null, 0, true, true);
+                    ri, false, null, null, 0, true, true, false);
             inputMethods.add(inputMethodInfo);
             addInstalledApp(ri);
         }
         for (String pkg : otherImes) {
             final ResolveInfo ri = createResolveInfoForSystemApp(pkg);
             final InputMethodInfo inputMethodInfo = new InputMethodInfo(
-                    ri, false, null, null, 0, false, true);
+                    ri, false, null, null, 0, false, true, false);
             inputMethods.add(inputMethodInfo);
             addInstalledApp(ri);
         }
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index f271b08..0a088e9 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -883,7 +883,7 @@
 
     private void handleMobileDataAlwaysOn() {
         final boolean enable = (Settings.Global.getInt(
-                mContext.getContentResolver(), Settings.Global.MOBILE_DATA_ALWAYS_ON, 0) == 1);
+                mContext.getContentResolver(), Settings.Global.MOBILE_DATA_ALWAYS_ON, 1) == 1);
         final boolean isEnabled = (mNetworkRequests.get(mDefaultMobileDataRequest) != null);
         if (enable == isEnabled) {
             return;  // Nothing to do.
diff --git a/services/core/java/com/android/server/NetworkManagementService.java b/services/core/java/com/android/server/NetworkManagementService.java
index 5654096..74e44d5 100644
--- a/services/core/java/com/android/server/NetworkManagementService.java
+++ b/services/core/java/com/android/server/NetworkManagementService.java
@@ -1912,31 +1912,6 @@
     }
 
     @Override
-    public void setDnsServersForNetwork(int netId, String[] servers, String domains) {
-        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
-
-        Command cmd;
-        if (servers.length > 0) {
-            cmd = new Command("resolver", "setnetdns", netId,
-                    (domains == null ? "" : domains));
-            for (String s : servers) {
-                InetAddress a = NetworkUtils.numericToInetAddress(s);
-                if (a.isAnyLocalAddress() == false) {
-                    cmd.appendArg(a.getHostAddress());
-                }
-            }
-        } else {
-            cmd = new Command("resolver", "clearnetdns", netId);
-        }
-
-        try {
-            mConnector.execute(cmd);
-        } catch (NativeDaemonConnectorException e) {
-            throw e.rethrowAsParcelableException();
-        }
-    }
-
-    @Override
     public void addVpnUidRanges(int netId, UidRange[] ranges) {
         mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
         Object[] argv = new Object[3 + MAX_UID_RANGES_PER_COMMAND];
diff --git a/services/core/java/com/android/server/RecoverySystemService.java b/services/core/java/com/android/server/RecoverySystemService.java
index 3c8c699..2010e64 100644
--- a/services/core/java/com/android/server/RecoverySystemService.java
+++ b/services/core/java/com/android/server/RecoverySystemService.java
@@ -181,7 +181,7 @@
         }
 
         @Override // Binder call
-        public void rebootRecoveryWithCommand(String command) {
+        public void rebootRecoveryWithCommand(String command, boolean update) {
             if (DEBUG) Slog.d(TAG, "rebootRecoveryWithCommand: [" + command + "]");
             synchronized (sRequestLock) {
                 if (!setupOrClearBcb(true, command)) {
@@ -190,7 +190,10 @@
 
                 // Having set up the BCB, go ahead and reboot.
                 PowerManager pm = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
-                pm.reboot(PowerManager.REBOOT_RECOVERY);
+                // PowerManagerService may additionally request uncrypting the package when it's
+                // to install an update (REBOOT_RECOVERY_UPDATE).
+                pm.reboot(update ? PowerManager.REBOOT_RECOVERY_UPDATE :
+                        PowerManager.REBOOT_RECOVERY);
             }
         }
 
diff --git a/services/core/java/com/android/server/connectivity/NetworkNotificationManager.java b/services/core/java/com/android/server/connectivity/NetworkNotificationManager.java
index 70faa5a..68fe505 100644
--- a/services/core/java/com/android/server/connectivity/NetworkNotificationManager.java
+++ b/services/core/java/com/android/server/connectivity/NetworkNotificationManager.java
@@ -182,7 +182,7 @@
             details = r.getString(R.string.network_switch_metered_detail, toTransport,
                     fromTransport);
         } else {
-            Slog.wtf(TAG, "Unknown notification type " + notifyType + "on network transport "
+            Slog.wtf(TAG, "Unknown notification type " + notifyType + " on network transport "
                     + getTransportName(transportType));
             return;
         }
diff --git a/services/core/java/com/android/server/connectivity/Tethering.java b/services/core/java/com/android/server/connectivity/Tethering.java
index 57381f2..0c80166 100644
--- a/services/core/java/com/android/server/connectivity/Tethering.java
+++ b/services/core/java/com/android/server/connectivity/Tethering.java
@@ -32,7 +32,6 @@
 import android.content.res.Resources;
 import android.hardware.usb.UsbManager;
 import android.net.ConnectivityManager;
-import android.net.ConnectivityManager.NetworkCallback;
 import android.net.INetworkPolicyManager;
 import android.net.INetworkStatsService;
 import android.net.LinkProperties;
@@ -73,6 +72,7 @@
 import com.android.server.connectivity.tethering.IPv6TetheringCoordinator;
 import com.android.server.connectivity.tethering.IPv6TetheringInterfaceServices;
 import com.android.server.connectivity.tethering.TetherInterfaceStateMachine;
+import com.android.server.connectivity.tethering.UpstreamNetworkMonitor;
 import com.android.server.net.BaseNetworkObserver;
 
 import java.io.FileDescriptor;
@@ -1031,249 +1031,6 @@
         }
     }
 
-    /**
-     * A class to centralize all the network and link properties information
-     * pertaining to the current and any potential upstream network.
-     *
-     * Calling #start() registers two callbacks: one to track the system default
-     * network and a second to specifically observe TYPE_MOBILE_DUN networks.
-     *
-     * The methods and data members of this class are only to be accessed and
-     * modified from the tethering master state machine thread. Any other
-     * access semantics would necessitate the addition of locking.
-     *
-     * TODO: Investigate whether more "upstream-specific" logic/functionality
-     * could/should be moved here.
-     */
-    public class UpstreamNetworkMonitor {
-        public static final int EVENT_ON_AVAILABLE      = 1;
-        public static final int EVENT_ON_CAPABILITIES   = 2;
-        public static final int EVENT_ON_LINKPROPERTIES = 3;
-        public static final int EVENT_ON_LOST           = 4;
-
-        private final Context mContext;
-        private final StateMachine mTarget;
-        private final int mWhat;
-        private final HashMap<Network, NetworkState> mNetworkMap = new HashMap<>();
-        private ConnectivityManager mCM;
-        private NetworkCallback mDefaultNetworkCallback;
-        private NetworkCallback mDunTetheringCallback;
-        private NetworkCallback mMobileNetworkCallback;
-        private boolean mDunRequired;
-
-        public UpstreamNetworkMonitor(Context ctx, StateMachine tgt, int what) {
-            mContext = ctx;
-            mTarget = tgt;
-            mWhat = what;
-        }
-
-        public void start() {
-            stop();
-
-            mDefaultNetworkCallback = new UpstreamNetworkCallback();
-            cm().registerDefaultNetworkCallback(mDefaultNetworkCallback);
-
-            final NetworkRequest dunTetheringRequest = new NetworkRequest.Builder()
-                    .addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR)
-                    .removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED)
-                    .addCapability(NetworkCapabilities.NET_CAPABILITY_DUN)
-                    .build();
-            mDunTetheringCallback = new UpstreamNetworkCallback();
-            cm().registerNetworkCallback(dunTetheringRequest, mDunTetheringCallback);
-        }
-
-        public void stop() {
-            releaseMobileNetworkRequest();
-
-            releaseCallback(mDefaultNetworkCallback);
-            mDefaultNetworkCallback = null;
-
-            releaseCallback(mDunTetheringCallback);
-            mDunTetheringCallback = null;
-
-            mNetworkMap.clear();
-        }
-
-        public void mobileUpstreamRequiresDun(boolean dunRequired) {
-            final boolean valueChanged = (mDunRequired != dunRequired);
-            mDunRequired = dunRequired;
-            if (valueChanged && mobileNetworkRequested()) {
-                releaseMobileNetworkRequest();
-                registerMobileNetworkRequest();
-            }
-        }
-
-        public boolean mobileNetworkRequested() {
-            return (mMobileNetworkCallback != null);
-        }
-
-        public void registerMobileNetworkRequest() {
-            if (mMobileNetworkCallback != null) return;
-
-            final NetworkRequest.Builder builder = new NetworkRequest.Builder()
-                    .addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR);
-            if (mDunRequired) {
-                builder.removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED)
-                       .addCapability(NetworkCapabilities.NET_CAPABILITY_DUN);
-            } else {
-                builder.addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET);
-            }
-            final NetworkRequest mobileUpstreamRequest = builder.build();
-
-            // The existing default network and DUN callbacks will be notified.
-            // Therefore, to avoid duplicate notifications, we only register a no-op.
-            mMobileNetworkCallback = new NetworkCallback();
-
-            // TODO: Change the timeout from 0 (no onUnavailable callback) to use some
-            // moderate callback time (once timeout callbacks are implemented). This might
-            // be useful for updating some UI. Additionally, we should definitely log a
-            // message to aid in any subsequent debugging
-            if (DBG) Log.d(TAG, "requesting mobile upstream network: " + mobileUpstreamRequest);
-
-            cm().requestNetwork(mobileUpstreamRequest, mMobileNetworkCallback);
-        }
-
-        public void releaseMobileNetworkRequest() {
-            if (mMobileNetworkCallback == null) return;
-
-            cm().unregisterNetworkCallback(mMobileNetworkCallback);
-            mMobileNetworkCallback = null;
-        }
-
-        public NetworkState lookup(Network network) {
-            return (network != null) ? mNetworkMap.get(network) : null;
-        }
-
-        private void handleAvailable(Network network) {
-            if (VDBG) {
-                Log.d(TAG, "EVENT_ON_AVAILABLE for " + network);
-            }
-            if (!mNetworkMap.containsKey(network)) {
-                mNetworkMap.put(network,
-                        new NetworkState(null, null, null, network, null, null));
-            }
-
-            final ConnectivityManager cm = cm();
-
-            if (mDefaultNetworkCallback != null) {
-                cm.requestNetworkCapabilities(mDefaultNetworkCallback);
-                cm.requestLinkProperties(mDefaultNetworkCallback);
-            }
-
-            // Requesting updates for mDunTetheringCallback is not
-            // necessary. Because it's a listen, it will already have
-            // heard all NetworkCapabilities and LinkProperties updates
-            // since UpstreamNetworkMonitor was started. Because we
-            // start UpstreamNetworkMonitor before chooseUpstreamType()
-            // is ever invoked (it can register a DUN request) this is
-            // mostly safe. However, if a DUN network is already up for
-            // some reason (unlikely, because DUN is restricted and,
-            // unless the DUN network is shared with another APN, only
-            // the system can request it and this is the only part of
-            // the system that requests it) we won't know its
-            // LinkProperties or NetworkCapabilities.
-
-            notifyTarget(EVENT_ON_AVAILABLE, network);
-        }
-
-        private void handleNetCap(Network network, NetworkCapabilities newNc) {
-            if (!mNetworkMap.containsKey(network)) {
-                // Ignore updates for networks for which we have not yet
-                // received onAvailable() - which should never happen -
-                // or for which we have already received onLost().
-                return;
-            }
-            if (VDBG) {
-                Log.d(TAG, String.format("EVENT_ON_CAPABILITIES for %s: %s",
-                        network, newNc));
-            }
-
-            final NetworkState prev = mNetworkMap.get(network);
-            mNetworkMap.put(network,
-                    new NetworkState(null, prev.linkProperties, newNc,
-                                     network, null, null));
-            notifyTarget(EVENT_ON_CAPABILITIES, network);
-        }
-
-        private void handleLinkProp(Network network, LinkProperties newLp) {
-            if (!mNetworkMap.containsKey(network)) {
-                // Ignore updates for networks for which we have not yet
-                // received onAvailable() - which should never happen -
-                // or for which we have already received onLost().
-                return;
-            }
-            if (VDBG) {
-                Log.d(TAG, String.format("EVENT_ON_LINKPROPERTIES for %s: %s",
-                        network, newLp));
-            }
-
-            final NetworkState prev = mNetworkMap.get(network);
-            mNetworkMap.put(network,
-                    new NetworkState(null, newLp, prev.networkCapabilities,
-                                     network, null, null));
-            notifyTarget(EVENT_ON_LINKPROPERTIES, network);
-        }
-
-        private void handleLost(Network network) {
-            if (!mNetworkMap.containsKey(network)) {
-                // Ignore updates for networks for which we have not yet
-                // received onAvailable() - which should never happen -
-                // or for which we have already received onLost().
-                return;
-            }
-            if (VDBG) {
-                Log.d(TAG, "EVENT_ON_LOST for " + network);
-            }
-            notifyTarget(EVENT_ON_LOST, mNetworkMap.remove(network));
-        }
-
-        // Fetch (and cache) a ConnectivityManager only if and when we need one.
-        private ConnectivityManager cm() {
-            if (mCM == null) {
-                mCM = mContext.getSystemService(ConnectivityManager.class);
-            }
-            return mCM;
-        }
-
-        /**
-         * A NetworkCallback class that relays information of interest to the
-         * tethering master state machine thread for subsequent processing.
-         */
-        private class UpstreamNetworkCallback extends NetworkCallback {
-            @Override
-            public void onAvailable(Network network) {
-                mTarget.getHandler().post(() -> handleAvailable(network));
-            }
-
-            @Override
-            public void onCapabilitiesChanged(Network network, NetworkCapabilities newNc) {
-                mTarget.getHandler().post(() -> handleNetCap(network, newNc));
-            }
-
-            @Override
-            public void onLinkPropertiesChanged(Network network, LinkProperties newLp) {
-                mTarget.getHandler().post(() -> handleLinkProp(network, newLp));
-            }
-
-            @Override
-            public void onLost(Network network) {
-                mTarget.getHandler().post(() -> handleLost(network));
-            }
-        }
-
-        private void releaseCallback(NetworkCallback cb) {
-            if (cb != null) cm().unregisterNetworkCallback(cb);
-        }
-
-        private void notifyTarget(int which, Network network) {
-            notifyTarget(which, mNetworkMap.get(network));
-        }
-
-        private void notifyTarget(int which, NetworkState netstate) {
-            mTarget.sendMessage(mWhat, which, 0, netstate);
-        }
-    }
-
     // Needed because the canonical source of upstream truth is just the
     // upstream interface name, |mCurrentUpstreamIface|.  This is ripe for
     // future simplification, once the upstream Network is canonical.
diff --git a/services/core/java/com/android/server/connectivity/Vpn.java b/services/core/java/com/android/server/connectivity/Vpn.java
index afc6247..a5876dd 100644
--- a/services/core/java/com/android/server/connectivity/Vpn.java
+++ b/services/core/java/com/android/server/connectivity/Vpn.java
@@ -1586,9 +1586,6 @@
         public void exit() {
             // We assume that everything is reset after stopping the daemons.
             interrupt();
-            for (LocalSocket socket : mSockets) {
-                IoUtils.closeQuietly(socket);
-            }
             agentDisconnect();
             try {
                 mContext.unregisterReceiver(mBroadcastReceiver);
@@ -1601,8 +1598,26 @@
             Log.v(TAG, "Waiting");
             synchronized (TAG) {
                 Log.v(TAG, "Executing");
-                execute();
-                monitorDaemons();
+                try {
+                    execute();
+                    monitorDaemons();
+                    interrupted(); // Clear interrupt flag if execute called exit.
+                } catch (InterruptedException e) {
+                } finally {
+                    for (LocalSocket socket : mSockets) {
+                        IoUtils.closeQuietly(socket);
+                    }
+                    // This sleep is necessary for racoon to successfully complete sending delete
+                    // message to server.
+                    try {
+                        Thread.sleep(50);
+                    } catch (InterruptedException e) {
+                    }
+                    for (String daemon : mDaemons) {
+                        SystemService.stop(daemon);
+                    }
+                }
+                agentDisconnect();
             }
         }
 
@@ -1801,18 +1816,6 @@
                 Log.i(TAG, "Aborting", e);
                 updateState(DetailedState.FAILED, e.getMessage());
                 exit();
-            } finally {
-                // Kill the daemons if they fail to stop.
-                if (!initFinished) {
-                    for (String daemon : mDaemons) {
-                        SystemService.stop(daemon);
-                    }
-                }
-
-                // Do not leave an unstable state.
-                if (!initFinished || mNetworkInfo.getDetailedState() == DetailedState.CONNECTING) {
-                    agentDisconnect();
-                }
             }
         }
 
@@ -1820,28 +1823,17 @@
          * Monitor the daemons we started, moving to disconnected state if the
          * underlying services fail.
          */
-        private void monitorDaemons() {
+        private void monitorDaemons() throws InterruptedException{
             if (!mNetworkInfo.isConnected()) {
                 return;
             }
-
-            try {
-                while (true) {
-                    Thread.sleep(2000);
-                    for (int i = 0; i < mDaemons.length; i++) {
-                        if (mArguments[i] != null && SystemService.isStopped(mDaemons[i])) {
-                            return;
-                        }
+            while (true) {
+                Thread.sleep(2000);
+                for (int i = 0; i < mDaemons.length; i++) {
+                    if (mArguments[i] != null && SystemService.isStopped(mDaemons[i])) {
+                        return;
                     }
                 }
-            } catch (InterruptedException e) {
-                Log.d(TAG, "interrupted during monitorDaemons(); stopping services");
-            } finally {
-                for (String daemon : mDaemons) {
-                    SystemService.stop(daemon);
-                }
-
-                agentDisconnect();
             }
         }
     }
diff --git a/services/core/java/com/android/server/connectivity/tethering/UpstreamNetworkMonitor.java b/services/core/java/com/android/server/connectivity/tethering/UpstreamNetworkMonitor.java
new file mode 100644
index 0000000..927dfd5
--- /dev/null
+++ b/services/core/java/com/android/server/connectivity/tethering/UpstreamNetworkMonitor.java
@@ -0,0 +1,287 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.connectivity.tethering;
+
+import android.content.Context;
+import android.net.ConnectivityManager;
+import android.net.ConnectivityManager.NetworkCallback;
+import android.net.LinkProperties;
+import android.net.Network;
+import android.net.NetworkCapabilities;
+import android.net.NetworkRequest;
+import android.net.NetworkState;
+import android.util.Log;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.StateMachine;
+
+import java.util.HashMap;
+
+
+/**
+ * A class to centralize all the network and link properties information
+ * pertaining to the current and any potential upstream network.
+ *
+ * Calling #start() registers two callbacks: one to track the system default
+ * network and a second to specifically observe TYPE_MOBILE_DUN networks.
+ *
+ * The methods and data members of this class are only to be accessed and
+ * modified from the tethering master state machine thread. Any other
+ * access semantics would necessitate the addition of locking.
+ *
+ * TODO: Move upstream selection logic here.
+ *
+ * @hide
+ */
+public class UpstreamNetworkMonitor {
+    private static final String TAG = UpstreamNetworkMonitor.class.getSimpleName();
+    private static final boolean DBG = false;
+    private static final boolean VDBG = false;
+
+    public static final int EVENT_ON_AVAILABLE      = 1;
+    public static final int EVENT_ON_CAPABILITIES   = 2;
+    public static final int EVENT_ON_LINKPROPERTIES = 3;
+    public static final int EVENT_ON_LOST           = 4;
+
+    private final Context mContext;
+    private final StateMachine mTarget;
+    private final int mWhat;
+    private final HashMap<Network, NetworkState> mNetworkMap = new HashMap<>();
+    private ConnectivityManager mCM;
+    private NetworkCallback mDefaultNetworkCallback;
+    private NetworkCallback mDunTetheringCallback;
+    private NetworkCallback mMobileNetworkCallback;
+    private boolean mDunRequired;
+
+    public UpstreamNetworkMonitor(Context ctx, StateMachine tgt, int what) {
+        mContext = ctx;
+        mTarget = tgt;
+        mWhat = what;
+    }
+
+    @VisibleForTesting
+    public UpstreamNetworkMonitor(StateMachine tgt, int what, ConnectivityManager cm) {
+        this(null, tgt, what);
+        mCM = cm;
+    }
+
+    public void start() {
+        stop();
+
+        mDefaultNetworkCallback = new UpstreamNetworkCallback();
+        cm().registerDefaultNetworkCallback(mDefaultNetworkCallback);
+
+        final NetworkRequest dunTetheringRequest = new NetworkRequest.Builder()
+                .addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR)
+                .removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED)
+                .addCapability(NetworkCapabilities.NET_CAPABILITY_DUN)
+                .build();
+        mDunTetheringCallback = new UpstreamNetworkCallback();
+        cm().registerNetworkCallback(dunTetheringRequest, mDunTetheringCallback);
+    }
+
+    public void stop() {
+        releaseMobileNetworkRequest();
+
+        releaseCallback(mDefaultNetworkCallback);
+        mDefaultNetworkCallback = null;
+
+        releaseCallback(mDunTetheringCallback);
+        mDunTetheringCallback = null;
+
+        mNetworkMap.clear();
+    }
+
+    public void mobileUpstreamRequiresDun(boolean dunRequired) {
+        final boolean valueChanged = (mDunRequired != dunRequired);
+        mDunRequired = dunRequired;
+        if (valueChanged && mobileNetworkRequested()) {
+            releaseMobileNetworkRequest();
+            registerMobileNetworkRequest();
+        }
+    }
+
+    public boolean mobileNetworkRequested() {
+        return (mMobileNetworkCallback != null);
+    }
+
+    public void registerMobileNetworkRequest() {
+        if (mMobileNetworkCallback != null) return;
+
+        final NetworkRequest.Builder builder = new NetworkRequest.Builder()
+                .addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR);
+        if (mDunRequired) {
+            builder.removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED)
+                   .addCapability(NetworkCapabilities.NET_CAPABILITY_DUN);
+        } else {
+            builder.addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET);
+        }
+        final NetworkRequest mobileUpstreamRequest = builder.build();
+
+        // The existing default network and DUN callbacks will be notified.
+        // Therefore, to avoid duplicate notifications, we only register a no-op.
+        mMobileNetworkCallback = new NetworkCallback();
+
+        // TODO: Change the timeout from 0 (no onUnavailable callback) to use some
+        // moderate callback time (once timeout callbacks are implemented). This might
+        // be useful for updating some UI. Additionally, we should definitely log a
+        // message to aid in any subsequent debugging
+        if (DBG) Log.d(TAG, "requesting mobile upstream network: " + mobileUpstreamRequest);
+
+        cm().requestNetwork(mobileUpstreamRequest, mMobileNetworkCallback);
+    }
+
+    public void releaseMobileNetworkRequest() {
+        if (mMobileNetworkCallback == null) return;
+
+        cm().unregisterNetworkCallback(mMobileNetworkCallback);
+        mMobileNetworkCallback = null;
+    }
+
+    public NetworkState lookup(Network network) {
+        return (network != null) ? mNetworkMap.get(network) : null;
+    }
+
+    private void handleAvailable(Network network) {
+        if (VDBG) {
+            Log.d(TAG, "EVENT_ON_AVAILABLE for " + network);
+        }
+        if (!mNetworkMap.containsKey(network)) {
+            mNetworkMap.put(network,
+                    new NetworkState(null, null, null, network, null, null));
+        }
+
+        final ConnectivityManager cm = cm();
+
+        if (mDefaultNetworkCallback != null) {
+            cm.requestNetworkCapabilities(mDefaultNetworkCallback);
+            cm.requestLinkProperties(mDefaultNetworkCallback);
+        }
+
+        // Requesting updates for mDunTetheringCallback is not
+        // necessary. Because it's a listen, it will already have
+        // heard all NetworkCapabilities and LinkProperties updates
+        // since UpstreamNetworkMonitor was started. Because we
+        // start UpstreamNetworkMonitor before chooseUpstreamType()
+        // is ever invoked (it can register a DUN request) this is
+        // mostly safe. However, if a DUN network is already up for
+        // some reason (unlikely, because DUN is restricted and,
+        // unless the DUN network is shared with another APN, only
+        // the system can request it and this is the only part of
+        // the system that requests it) we won't know its
+        // LinkProperties or NetworkCapabilities.
+
+        notifyTarget(EVENT_ON_AVAILABLE, network);
+    }
+
+    private void handleNetCap(Network network, NetworkCapabilities newNc) {
+        if (!mNetworkMap.containsKey(network)) {
+            // Ignore updates for networks for which we have not yet
+            // received onAvailable() - which should never happen -
+            // or for which we have already received onLost().
+            return;
+        }
+        if (VDBG) {
+            Log.d(TAG, String.format("EVENT_ON_CAPABILITIES for %s: %s",
+                    network, newNc));
+        }
+
+        final NetworkState prev = mNetworkMap.get(network);
+        mNetworkMap.put(network,
+                new NetworkState(null, prev.linkProperties, newNc,
+                                 network, null, null));
+        notifyTarget(EVENT_ON_CAPABILITIES, network);
+    }
+
+    private void handleLinkProp(Network network, LinkProperties newLp) {
+        if (!mNetworkMap.containsKey(network)) {
+            // Ignore updates for networks for which we have not yet
+            // received onAvailable() - which should never happen -
+            // or for which we have already received onLost().
+            return;
+        }
+        if (VDBG) {
+            Log.d(TAG, String.format("EVENT_ON_LINKPROPERTIES for %s: %s",
+                    network, newLp));
+        }
+
+        final NetworkState prev = mNetworkMap.get(network);
+        mNetworkMap.put(network,
+                new NetworkState(null, newLp, prev.networkCapabilities,
+                                 network, null, null));
+        notifyTarget(EVENT_ON_LINKPROPERTIES, network);
+    }
+
+    private void handleLost(Network network) {
+        if (!mNetworkMap.containsKey(network)) {
+            // Ignore updates for networks for which we have not yet
+            // received onAvailable() - which should never happen -
+            // or for which we have already received onLost().
+            return;
+        }
+        if (VDBG) {
+            Log.d(TAG, "EVENT_ON_LOST for " + network);
+        }
+        notifyTarget(EVENT_ON_LOST, mNetworkMap.remove(network));
+    }
+
+    // Fetch (and cache) a ConnectivityManager only if and when we need one.
+    private ConnectivityManager cm() {
+        if (mCM == null) {
+            mCM = mContext.getSystemService(ConnectivityManager.class);
+        }
+        return mCM;
+    }
+
+    /**
+     * A NetworkCallback class that relays information of interest to the
+     * tethering master state machine thread for subsequent processing.
+     */
+    private class UpstreamNetworkCallback extends NetworkCallback {
+        @Override
+        public void onAvailable(Network network) {
+            mTarget.getHandler().post(() -> handleAvailable(network));
+        }
+
+        @Override
+        public void onCapabilitiesChanged(Network network, NetworkCapabilities newNc) {
+            mTarget.getHandler().post(() -> handleNetCap(network, newNc));
+        }
+
+        @Override
+        public void onLinkPropertiesChanged(Network network, LinkProperties newLp) {
+            mTarget.getHandler().post(() -> handleLinkProp(network, newLp));
+        }
+
+        @Override
+        public void onLost(Network network) {
+            mTarget.getHandler().post(() -> handleLost(network));
+        }
+    }
+
+    private void releaseCallback(NetworkCallback cb) {
+        if (cb != null) cm().unregisterNetworkCallback(cb);
+    }
+
+    private void notifyTarget(int which, Network network) {
+        notifyTarget(which, mNetworkMap.get(network));
+    }
+
+    private void notifyTarget(int which, NetworkState netstate) {
+        mTarget.sendMessage(mWhat, which, 0, netstate);
+    }
+}
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
index 7c5550a..d81e092 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
@@ -796,7 +796,7 @@
     void onNewAvrAdded(HdmiDeviceInfo avr) {
         assertRunOnServiceThread();
         addAndStartAction(new SystemAudioAutoInitiationAction(this, avr.getLogicalAddress()));
-        if (isArcFeatureEnabled(avr.getPortId())
+        if (isConnected(avr.getPortId()) && isArcFeatureEnabled(avr.getPortId())
                 && !hasAction(SetArcTransmissionStateAction.class)) {
             startArcAction(true);
         }
@@ -900,29 +900,6 @@
     }
 
     @ServiceThreadOnly
-    private void updateArcFeatureStatus(int portId, boolean isConnected) {
-        assertRunOnServiceThread();
-        HdmiPortInfo portInfo = mService.getPortInfo(portId);
-        if (!portInfo.isArcSupported()) {
-            return;
-        }
-        HdmiDeviceInfo avr = getAvrDeviceInfo();
-        if (avr == null) {
-            if (isConnected) {
-                // Update the status (since TV may not have seen AVR yet) so
-                // that ARC can be initiated after discovery.
-                mArcFeatureEnabled.put(portId, isConnected);
-            }
-            return;
-        }
-        // HEAC 2.4, HEACT 5-15
-        // Should not activate ARC if +5V status is false.
-        if (avr.getPortId() == portId) {
-            changeArcFeatureEnabled(portId, isConnected);
-        }
-    }
-
-    @ServiceThreadOnly
     boolean isConnected(int portId) {
         assertRunOnServiceThread();
         return mService.isConnected(portId);
@@ -952,18 +929,18 @@
     @ServiceThreadOnly
     void changeArcFeatureEnabled(int portId, boolean enabled) {
         assertRunOnServiceThread();
-
-        if (mArcFeatureEnabled.get(portId) != enabled) {
-            mArcFeatureEnabled.put(portId, enabled);
-            if (enabled) {
-                if (!mArcEstablished) {
-                    startArcAction(true);
-                }
-            } else {
-                if (mArcEstablished) {
-                    startArcAction(false);
-                }
-            }
+        if (mArcFeatureEnabled.get(portId) == enabled) {
+            return;
+        }
+        mArcFeatureEnabled.put(portId, enabled);
+        HdmiDeviceInfo avr = getAvrDeviceInfo();
+        if (avr == null || avr.getPortId() != portId) {
+            return;
+        }
+        if (enabled && !mArcEstablished) {
+            startArcAction(true);
+        } else if (!enabled && mArcEstablished) {
+            startArcAction(false);
         }
     }
 
@@ -1097,14 +1074,14 @@
         return true;
     }
 
-    private boolean canStartArcUpdateAction(int avrAddress, boolean shouldCheckArcFeatureEnabled) {
+    private boolean canStartArcUpdateAction(int avrAddress, boolean enabled) {
         HdmiDeviceInfo avr = getAvrDeviceInfo();
         if (avr != null
                 && (avrAddress == avr.getLogicalAddress())
                 && isConnectedToArcPort(avr.getPhysicalAddress())
                 && isDirectConnectAddress(avr.getPhysicalAddress())) {
-            if (shouldCheckArcFeatureEnabled) {
-                return isArcFeatureEnabled(avr.getPortId());
+            if (enabled) {
+                return isConnected(avr.getPortId()) && isArcFeatureEnabled(avr.getPortId());
             } else {
                 return true;
             }
@@ -1566,7 +1543,6 @@
             // It covers seq #40, #43.
             hotplugActions.get(0).pollAllDevicesNow();
         }
-        updateArcFeatureStatus(portId, connected);
     }
 
     private void removeCecSwitches(int portId) {
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 7e47a11..7362a51 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -576,7 +576,7 @@
     /**
      * Whether the package parser cache is enabled.
      */
-    private static final boolean DEFAULT_PACKAGE_PARSER_CACHE_ENABLED = false;
+    private static final boolean DEFAULT_PACKAGE_PARSER_CACHE_ENABLED = true;
 
     final ServiceThread mHandlerThread;
 
@@ -2842,7 +2842,12 @@
             return null;
         }
 
-        if (SystemProperties.getBoolean("ro.boot.disable_package_cache", false)) {
+        // Disable package parsing on eng builds to allow for faster incremental development.
+        if ("eng".equals(Build.TYPE)) {
+            return null;
+        }
+
+        if (SystemProperties.getBoolean("pm.boot.disable_package_cache", false)) {
             Slog.i(TAG, "Disabling package parser cache due to system property.");
             return null;
         }
@@ -2861,9 +2866,33 @@
             FileUtils.deleteContents(cacheBaseDir);
         }
 
+
         // Return the versioned package cache directory. This is something like
         // "/data/system/package_cache/1"
-        return FileUtils.createDir(cacheBaseDir, PACKAGE_PARSER_CACHE_VERSION);
+        File cacheDir = FileUtils.createDir(cacheBaseDir, PACKAGE_PARSER_CACHE_VERSION);
+
+        // The following is a workaround to aid development on non-numbered userdebug
+        // builds or cases where "adb sync" is used on userdebug builds. If we detect that
+        // the system partition is newer.
+        //
+        // NOTE: When no BUILD_NUMBER is set by the build system, it defaults to a build
+        // that starts with "eng." to signify that this is an engineering build and not
+        // destined for release.
+        if ("userdebug".equals(Build.TYPE) && Build.VERSION.INCREMENTAL.startsWith("eng.")) {
+            Slog.w(TAG, "Wiping cache directory because the system partition changed.");
+
+            // Heuristic: If the /system directory has been modified recently due to an "adb sync"
+            // or a regular make, then blow away the cache. Note that mtimes are *NOT* reliable
+            // in general and should not be used for production changes. In this specific case,
+            // we know that they will work.
+            File frameworkDir = new File(Environment.getRootDirectory(), "framework");
+            if (cacheDir.lastModified() < frameworkDir.lastModified()) {
+                FileUtils.deleteContents(cacheBaseDir);
+                cacheDir = FileUtils.createDir(cacheBaseDir, PACKAGE_PARSER_CACHE_VERSION);
+            }
+        }
+
+        return cacheDir;
     }
 
     @Override
@@ -10610,12 +10639,30 @@
                             int flags = permissionState != null
                                     ? permissionState.getFlags() : 0;
                             if (origPermissions.hasRuntimePermission(bp.name, userId)) {
-                                if (permissionsState.grantRuntimePermission(bp, userId) ==
-                                        PermissionsState.PERMISSION_OPERATION_FAILURE) {
-                                    // If we cannot put the permission as it was, we have to write.
+                                // Don't propagate the permission in a permission review mode if
+                                // the former was revoked, i.e. marked to not propagate on upgrade.
+                                // Note that in a permission review mode install permissions are
+                                // represented as constantly granted runtime ones since we need to
+                                // keep a per user state associated with the permission. Also the
+                                // revoke on upgrade flag is no longer applicable and is reset.
+                                final boolean revokeOnUpgrade = (flags & PackageManager
+                                        .FLAG_PERMISSION_REVOKE_ON_UPGRADE) != 0;
+                                if (revokeOnUpgrade) {
+                                    flags &= ~PackageManager.FLAG_PERMISSION_REVOKE_ON_UPGRADE;
+                                    // Since we changed the flags, we have to write.
                                     changedRuntimePermissionUserIds = ArrayUtils.appendInt(
                                             changedRuntimePermissionUserIds, userId);
                                 }
+                                if (!mPermissionReviewRequired || !revokeOnUpgrade) {
+                                    if (permissionsState.grantRuntimePermission(bp, userId) ==
+                                            PermissionsState.PERMISSION_OPERATION_FAILURE) {
+                                        // If we cannot put the permission as it was,
+                                        // we have to write.
+                                        changedRuntimePermissionUserIds = ArrayUtils.appendInt(
+                                                changedRuntimePermissionUserIds, userId);
+                                    }
+                                }
+
                                 // If the app supports runtime permissions no need for a review.
                                 if (mPermissionReviewRequired
                                         && appSupportsRuntimePermissions
@@ -12989,8 +13036,10 @@
                         + " is not installer for " + packageName);
             }
 
-            ps.categoryHint = categoryHint;
-            scheduleWriteSettingsLocked();
+            if (ps.categoryHint != categoryHint) {
+                ps.categoryHint = categoryHint;
+                scheduleWriteSettingsLocked();
+            }
         }
     }
 
diff --git a/services/core/java/com/android/server/wm/AppWindowContainerController.java b/services/core/java/com/android/server/wm/AppWindowContainerController.java
index 9b2d3c6b..a900702 100644
--- a/services/core/java/com/android/server/wm/AppWindowContainerController.java
+++ b/services/core/java/com/android/server/wm/AppWindowContainerController.java
@@ -617,7 +617,7 @@
             }
             return dc.screenshotApplications(mToken.asBinder(), width, height,
                     false /* includeFullDisplay */, frameScale, Bitmap.Config.RGB_565,
-                    false /* wallpaperOnly */, false /* includeDecor */, true /* toAshmem */);
+                    false /* wallpaperOnly */, false /* includeDecor */);
         } finally {
             Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER);
         }
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 66267bd..73bf3dc 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -94,6 +94,7 @@
 import android.app.ActivityManager.StackId;
 import android.content.res.Configuration;
 import android.graphics.Bitmap;
+import android.graphics.GraphicBuffer;
 import android.graphics.Matrix;
 import android.graphics.Rect;
 import android.graphics.RectF;
@@ -2105,12 +2106,55 @@
      * @param wallpaperOnly true if only the wallpaper layer should be included in the screenshot
      * @param includeDecor whether to include window decors, like the status or navigation bar
      *                     background of the window
-     * @param toAshmem whether to convert the resulting bitmap to ashmem; this should be set to
-     *                 true if the Bitmap is sent over binder, and false otherwise
      */
     Bitmap screenshotApplications(IBinder appToken, int width, int height,
             boolean includeFullDisplay, float frameScale, Bitmap.Config config,
-            boolean wallpaperOnly, boolean includeDecor, boolean toAshmem) {
+            boolean wallpaperOnly, boolean includeDecor) {
+        Bitmap bitmap = screenshotApplications(appToken, width, height, includeFullDisplay,
+                frameScale, wallpaperOnly, includeDecor, SurfaceControl::screenshot);
+
+        if (DEBUG_SCREENSHOT) {
+            // TEST IF IT's ALL BLACK
+            int[] buffer = new int[bitmap.getWidth() * bitmap.getHeight()];
+            bitmap.getPixels(buffer, 0, bitmap.getWidth(), 0, 0, bitmap.getWidth(),
+                    bitmap.getHeight());
+            boolean allBlack = true;
+            final int firstColor = buffer[0];
+            for (int i = 0; i < buffer.length; i++) {
+                if (buffer[i] != firstColor) {
+                    allBlack = false;
+                    break;
+                }
+            }
+            if (allBlack) {
+                final WindowState appWin = mScreenshotApplicationState.appWin;
+                final int maxLayer = mScreenshotApplicationState.maxLayer;
+                final int minLayer = mScreenshotApplicationState.minLayer;
+                Slog.i(TAG_WM, "Screenshot " + appWin + " was monochrome(" +
+                        Integer.toHexString(firstColor) + ")! mSurfaceLayer=" +
+                        (appWin != null ?
+                                appWin.mWinAnimator.mSurfaceController.getLayer() : "null") +
+                        " minLayer=" + minLayer + " maxLayer=" + maxLayer);
+            }
+        }
+
+        // Create a copy of the screenshot that is immutable and backed in ashmem.
+        // This greatly reduces the overhead of passing the bitmap between processes.
+        Bitmap ret = bitmap.createAshmemBitmap(config);
+        bitmap.recycle();
+        return ret;
+    }
+
+    GraphicBuffer screenshotApplicationsToBuffer(IBinder appToken, int width, int height,
+            boolean includeFullDisplay, float frameScale, boolean wallpaperOnly,
+            boolean includeDecor) {
+        return screenshotApplications(appToken, width, height, includeFullDisplay, frameScale,
+                wallpaperOnly, includeDecor, SurfaceControl::screenshotToBuffer);
+    }
+
+    private <E> E screenshotApplications(IBinder appToken, int width, int height,
+            boolean includeFullDisplay, float frameScale, boolean wallpaperOnly,
+            boolean includeDecor, Screenshoter<E> screenshoter) {
         int dw = mDisplayInfo.logicalWidth;
         int dh = mDisplayInfo.logicalHeight;
         if (dw == 0 || dh == 0) {
@@ -2119,7 +2163,7 @@
             return null;
         }
 
-        Bitmap bm = null;
+        E bitmap;
 
         mScreenshotApplicationState.reset(appToken == null && !wallpaperOnly);
         final Rect frame = new Rect();
@@ -2327,48 +2371,15 @@
             SurfaceControl.openTransaction();
             SurfaceControl.closeTransactionSync();
 
-            bm = SurfaceControl.screenshot(crop, width, height, minLayer, maxLayer,
+            bitmap = screenshoter.screenshot(crop, width, height, minLayer, maxLayer,
                     inRotation, rot);
-            if (bm == null) {
+            if (bitmap == null) {
                 Slog.w(TAG_WM, "Screenshot failure taking screenshot for (" + dw + "x" + dh
                         + ") to layer " + maxLayer);
                 return null;
             }
         }
-
-        if (DEBUG_SCREENSHOT) {
-            // TEST IF IT's ALL BLACK
-            int[] buffer = new int[bm.getWidth() * bm.getHeight()];
-            bm.getPixels(buffer, 0, bm.getWidth(), 0, 0, bm.getWidth(), bm.getHeight());
-            boolean allBlack = true;
-            final int firstColor = buffer[0];
-            for (int i = 0; i < buffer.length; i++) {
-                if (buffer[i] != firstColor) {
-                    allBlack = false;
-                    break;
-                }
-            }
-            if (allBlack) {
-                final WindowState appWin = mScreenshotApplicationState.appWin;
-                final int maxLayer = mScreenshotApplicationState.maxLayer;
-                final int minLayer = mScreenshotApplicationState.minLayer;
-                Slog.i(TAG_WM, "Screenshot " + appWin + " was monochrome(" +
-                        Integer.toHexString(firstColor) + ")! mSurfaceLayer=" +
-                        (appWin != null ?
-                                appWin.mWinAnimator.mSurfaceController.getLayer() : "null") +
-                        " minLayer=" + minLayer + " maxLayer=" + maxLayer);
-            }
-        }
-
-        // Create a copy of the screenshot that is immutable and backed in ashmem.
-        // This greatly reduces the overhead of passing the bitmap between processes.
-        if (toAshmem) {
-            Bitmap ret = bm.createAshmemBitmap(config);
-            bm.recycle();
-            return ret;
-        } else {
-            return bm;
-        }
+        return bitmap;
     }
 
     // TODO: Can this use createRotationMatrix()?
@@ -2721,4 +2732,13 @@
             return mName;
         }
     }
+
+    /**
+     * Interface to screenshot into various types, i.e. {@link Bitmap} and {@link GraphicBuffer}.
+     */
+    @FunctionalInterface
+    private interface Screenshoter<E> {
+        E screenshot(Rect sourceCrop, int width, int height, int minLayer, int maxLayer,
+                boolean useIdentityTransform, int rotation);
+    }
 }
diff --git a/services/core/java/com/android/server/wm/TaskSnapshotController.java b/services/core/java/com/android/server/wm/TaskSnapshotController.java
index 4421d61..68aceae 100644
--- a/services/core/java/com/android/server/wm/TaskSnapshotController.java
+++ b/services/core/java/com/android/server/wm/TaskSnapshotController.java
@@ -96,17 +96,11 @@
         if (top == null) {
             return null;
         }
-        final Bitmap bmp = top.mDisplayContent.screenshotApplications(top.token, -1, -1, false,
-                1.0f, ARGB_8888, false, true, false);
-        if (bmp == null) {
+        final GraphicBuffer buffer = top.mDisplayContent.screenshotApplicationsToBuffer(top.token,
+                -1, -1, false, 1.0f, false, true);
+        if (buffer == null) {
             return null;
         }
-        // TODO: Already use a GraphicBuffer when snapshotting the content.
-        final GraphicBuffer buffer = GraphicBuffer.create(bmp.getWidth(), bmp.getHeight(),
-                RGBA_8888, USAGE_HW_TEXTURE | USAGE_SW_WRITE_NEVER | USAGE_SW_READ_NEVER);
-        final Canvas c = buffer.lockCanvas();
-        c.drawBitmap(bmp, 0, 0, null);
-        buffer.unlockCanvasAndPost(c);
         return new TaskSnapshot(buffer, top.getConfiguration().orientation,
                 top.findMainWindow().mStableInsets);
     }
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 195d4c3..7cc77de 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -3862,8 +3862,7 @@
             Trace.traceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "screenshotWallpaper");
             return screenshotApplications(null /* appToken */, DEFAULT_DISPLAY, -1 /* width */,
                     -1 /* height */, true /* includeFullDisplay */, 1f /* frameScale */,
-                    Bitmap.Config.ARGB_8888, true /* wallpaperOnly */, false /* includeDecor */,
-                    true /* toAshmem */);
+                    Bitmap.Config.ARGB_8888, true /* wallpaperOnly */, false /* includeDecor */);
         } finally {
             Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER);
         }
@@ -3885,7 +3884,7 @@
             Bitmap bm = screenshotApplications(null /* appToken */, DEFAULT_DISPLAY,
                     -1 /* width */, -1 /* height */, true /* includeFullDisplay */,
                     1f /* frameScale */, Bitmap.Config.ARGB_8888, false /* wallpaperOnly */,
-                    false /* includeDecor */, true /* toAshmem */);
+                    false /* includeDecor */);
             try {
                 receiver.send(bm);
             } catch (RemoteException e) {
@@ -3908,12 +3907,10 @@
      * @param wallpaperOnly true if only the wallpaper layer should be included in the screenshot
      * @param includeDecor whether to include window decors, like the status or navigation bar
      *                     background of the window
-     * @param toAshmem whether to convert the resulting bitmap to ashmem; this should be set to
-     *                 true if the Bitmap is sent over binder, and false otherwise
      */
     private Bitmap screenshotApplications(IBinder appToken, int displayId, int width,
             int height, boolean includeFullDisplay, float frameScale, Bitmap.Config config,
-            boolean wallpaperOnly, boolean includeDecor, boolean toAshmem) {
+            boolean wallpaperOnly, boolean includeDecor) {
         final DisplayContent displayContent;
         synchronized(mWindowMap) {
             displayContent = mRoot.getDisplayContentOrCreate(displayId);
@@ -3924,7 +3921,7 @@
             }
         }
         return displayContent.screenshotApplications(appToken, width, height,
-                includeFullDisplay, frameScale, config, wallpaperOnly, includeDecor, toAshmem);
+                includeFullDisplay, frameScale, config, wallpaperOnly, includeDecor);
     }
 
     /**
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index bdd1a0f51..040188d 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -17,7 +17,6 @@
 package com.android.server.devicepolicy;
 
 import static android.Manifest.permission.MANAGE_CA_CERTIFICATES;
-import static android.app.admin.DevicePolicyManager.ACTION_SHOW_DEVICE_MONITORING_DIALOG;
 import static android.app.admin.DevicePolicyManager.CODE_ACCOUNTS_NOT_EMPTY;
 import static android.app.admin.DevicePolicyManager.CODE_ADD_MANAGED_PROFILE_DISALLOWED;
 import static android.app.admin.DevicePolicyManager.CODE_CANNOT_ADD_MANAGED_PROFILE;
@@ -49,7 +48,6 @@
 import android.accessibilityservice.AccessibilityServiceInfo;
 import android.accounts.Account;
 import android.accounts.AccountManager;
-import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.UserIdInt;
@@ -180,8 +178,6 @@
 import java.io.FileOutputStream;
 import java.io.IOException;
 import java.io.PrintWriter;
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
 import java.nio.charset.StandardCharsets;
 import java.security.cert.CertificateException;
 import java.security.cert.CertificateFactory;
@@ -337,7 +333,8 @@
     private static final long MINIMUM_STRONG_AUTH_TIMEOUT_MS = 1 * 60 * 60 * 1000; // 1h
 
     /**
-     * Strings logged with {@link #PROVISIONING_ENTRY_POINT_ADB}.
+     * Strings logged with {@link
+     * com.android.internal.logging.nano.MetricsProto.MetricsEvent#PROVISIONING_ENTRY_POINT_ADB}.
      */
     private static final String LOG_TAG_PROFILE_OWNER = "profile-owner";
     private static final String LOG_TAG_DEVICE_OWNER = "device-owner";
@@ -552,11 +549,25 @@
             }
             if (Intent.ACTION_USER_ADDED.equals(action)) {
                 sendUserAddedOrRemovedCommand(DeviceAdminReceiver.ACTION_USER_ADDED, userHandle);
-                disableDeviceOwnerManagedSingleUserFeaturesIfNeeded();
+                synchronized (DevicePolicyManagerService.this) {
+                    // It might take a while for the user to become affiliated. Make security
+                    // and network logging unavailable in the meantime.
+                    maybePauseDeviceWideLoggingLocked();
+                }
             } else if (Intent.ACTION_USER_REMOVED.equals(action)) {
                 sendUserAddedOrRemovedCommand(DeviceAdminReceiver.ACTION_USER_REMOVED, userHandle);
-                disableDeviceOwnerManagedSingleUserFeaturesIfNeeded();
-                removeUserData(userHandle);
+                synchronized (DevicePolicyManagerService.this) {
+                    // Check whether the user is affiliated, *before* removing its data.
+                    boolean isRemovedUserAffiliated = isUserAffiliatedWithDeviceLocked(userHandle);
+                    removeUserData(userHandle);
+                    if (!isRemovedUserAffiliated) {
+                        // We discard the logs when unaffiliated users are deleted (so that the
+                        // device owner cannot retrieve data about that user after it's gone).
+                        discardDeviceWideLogsLocked();
+                        // Resume logging if all remaining users are affiliated.
+                        maybeResumeDeviceWideLoggingLocked();
+                    }
+                }
             } else if (Intent.ACTION_USER_STARTED.equals(action)) {
                 synchronized (DevicePolicyManagerService.this) {
                     // Reset the policy data
@@ -1858,9 +1869,10 @@
             if (mOwners.hasDeviceOwner()) {
                 mInjector.systemPropertiesSet(PROPERTY_DEVICE_OWNER_PRESENT, "true");
                 Slog.i(LOG_TAG, "Set ro.device_owner property to true");
-                disableDeviceOwnerManagedSingleUserFeaturesIfNeeded();
+
                 if (mInjector.securityLogGetLoggingEnabledProperty()) {
                     mSecurityLogMonitor.start();
+                    maybePauseDeviceWideLoggingLocked();
                 }
             } else {
                 mInjector.systemPropertiesSet(PROPERTY_DEVICE_OWNER_PRESENT, "false");
@@ -3155,7 +3167,7 @@
     }
 
     // It's temporary solution to clear DISALLOW_ADD_USER after CTS
-    // TODO: b/31952368 when the restriction is moved from system to the device owner,
+    // STOPSHIP(b/31952368) when the restriction is moved from system to the device owner,
     // it can be removed.
     private void clearDeviceOwnerUserRestrictionLocked(UserHandle userHandle) {
         if (mUserManager.hasUserRestriction(UserManager.DISALLOW_ADD_USER, userHandle)) {
@@ -5650,34 +5662,12 @@
         }
     }
 
-    private boolean isDeviceOwnerManagedSingleUserDevice() {
-        synchronized (this) {
-            if (!mOwners.hasDeviceOwner()) {
-                return false;
-            }
-        }
-        final long callingIdentity = mInjector.binderClearCallingIdentity();
-        try {
-            if (mInjector.userManagerIsSplitSystemUser()) {
-                // In split system user mode, only allow the case where the device owner is managing
-                // the only non-system user of the device
-                return (mUserManager.getUserCount() == 2
-                        && mOwners.getDeviceOwnerUserId() != UserHandle.USER_SYSTEM);
-            } else  {
-                return mUserManager.getUserCount() == 1;
-            }
-        } finally {
-            mInjector.binderRestoreCallingIdentity(callingIdentity);
-        }
-    }
-
-    private void ensureDeviceOwnerManagingSingleUser(ComponentName who) throws SecurityException {
+    private void ensureDeviceOwnerAndAllUsersAffiliated(ComponentName who) throws SecurityException {
         synchronized (this) {
             getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_DEVICE_OWNER);
-        }
-        if (!isDeviceOwnerManagedSingleUserDevice()) {
-            throw new SecurityException(
-                    "There should only be one user, managed by Device Owner");
+            if (!areAllUsersAffiliatedWithDeviceLocked()) {
+                throw new SecurityException("Not all users are affiliated.");
+            }
         }
     }
 
@@ -5687,7 +5677,11 @@
             return false;
         }
         Preconditions.checkNotNull(who, "ComponentName is null");
-        ensureDeviceOwnerManagingSingleUser(who);
+
+        // TODO: If an unaffiliated user is removed, the admin will be able to request a bugreport
+        // which could still contain data related to that user. Should we disallow that, e.g. until
+        // next boot? Might not be needed given that this still requires user consent.
+        ensureDeviceOwnerAndAllUsersAffiliated(who);
 
         if (mRemoteBugreportServiceIsActive.get()
                 || (getDeviceOwnerRemoteBugreportUri() != null)) {
@@ -5712,7 +5706,8 @@
             mRemoteBugreportServiceIsActive.set(true);
             mRemoteBugreportSharingAccepted.set(false);
             registerRemoteBugreportReceivers();
-            mInjector.getNotificationManager().notifyAsUser(LOG_TAG, RemoteBugreportUtils.NOTIFICATION_ID,
+            mInjector.getNotificationManager().notifyAsUser(LOG_TAG,
+                    RemoteBugreportUtils.NOTIFICATION_ID,
                     RemoteBugreportUtils.buildNotification(mContext,
                             DevicePolicyManager.NOTIFICATION_BUGREPORT_STARTED), UserHandle.ALL);
             mHandler.postDelayed(mRemoteBugreportTimeoutRunnable,
@@ -6259,6 +6254,7 @@
             admin.userRestrictions = null;
             admin.defaultEnabledRestrictionsAlreadySet.clear();
             admin.forceEphemeralUsers = false;
+            admin.isNetworkLoggingEnabled = false;
             mUserManagerInternal.setForceEphemeralUsers(admin.forceEphemeralUsers);
             final DevicePolicyData policyData = getUserData(UserHandle.USER_SYSTEM);
             policyData.mLastSecurityLogRetrievalTime = -1;
@@ -6271,7 +6267,11 @@
         mOwners.clearDeviceOwner();
         mOwners.writeDeviceOwner();
         updateDeviceOwnerLocked();
-        disableDeviceOwnerManagedSingleUserFeaturesIfNeeded();
+
+        mInjector.securityLogSetLoggingEnabledProperty(false);
+        mSecurityLogMonitor.stop();
+        setNetworkLoggingActiveInternal(false);
+
         try {
             if (mInjector.getIBackupManager() != null) {
                 // Reactivate backup service.
@@ -8261,7 +8261,7 @@
         synchronized (this) {
             getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
             final int userHandle = mInjector.userHandleGetCallingUserId();
-            if (isUserAffiliatedWithDevice(userHandle)) {
+            if (isUserAffiliatedWithDeviceLocked(userHandle)) {
                 setLockTaskPackagesLocked(userHandle, new ArrayList<>(Arrays.asList(packages)));
             } else {
                 throw new SecurityException("Admin " + who +
@@ -9442,6 +9442,12 @@
                 getUserData(UserHandle.USER_SYSTEM).mAffiliationIds = affiliationIds;
                 saveSettingsLocked(UserHandle.USER_SYSTEM);
             }
+
+            // Affiliation status for any user, not just the calling user, might have changed.
+            // The device owner user will still be affiliated after changing its affiliation ids,
+            // but as a result of that other users might become affiliated or un-affiliated.
+            maybePauseDeviceWideLoggingLocked();
+            maybeResumeDeviceWideLoggingLocked();
         }
     }
 
@@ -9461,84 +9467,78 @@
 
     @Override
     public boolean isAffiliatedUser() {
-        return isUserAffiliatedWithDevice(mInjector.userHandleGetCallingUserId());
+        if (!mHasFeature) {
+            return false;
+        }
+
+        synchronized (this) {
+            return isUserAffiliatedWithDeviceLocked(mInjector.userHandleGetCallingUserId());
+        }
     }
 
-    private boolean isUserAffiliatedWithDevice(int userId) {
-        synchronized (this) {
-            if (!mOwners.hasDeviceOwner()) {
-                return false;
-            }
-            if (userId == mOwners.getDeviceOwnerUserId()) {
-                // The user that the DO is installed on is always affiliated with the device.
+    private boolean isUserAffiliatedWithDeviceLocked(int userId) {
+        if (!mOwners.hasDeviceOwner()) {
+            return false;
+        }
+        if (userId == mOwners.getDeviceOwnerUserId()) {
+            // The user that the DO is installed on is always affiliated with the device.
+            return true;
+        }
+        if (userId == UserHandle.USER_SYSTEM) {
+            // The system user is always affiliated in a DO device, even if the DO is set on a
+            // different user. This could be the case if the DO is set in the primary user
+            // of a split user device.
+            return true;
+        }
+        final ComponentName profileOwner = getProfileOwner(userId);
+        if (profileOwner == null) {
+            return false;
+        }
+        final Set<String> userAffiliationIds = getUserData(userId).mAffiliationIds;
+        final Set<String> deviceAffiliationIds =
+                getUserData(UserHandle.USER_SYSTEM).mAffiliationIds;
+        for (String id : userAffiliationIds) {
+            if (deviceAffiliationIds.contains(id)) {
                 return true;
             }
-            if (userId == UserHandle.USER_SYSTEM) {
-                // The system user is always affiliated in a DO device, even if the DO is set on a
-                // different user. This could be the case if the DO is set in the primary user
-                // of a split user device.
-                return true;
-            }
-            final ComponentName profileOwner = getProfileOwner(userId);
-            if (profileOwner == null) {
-                return false;
-            }
-            final Set<String> userAffiliationIds = getUserData(userId).mAffiliationIds;
-            final Set<String> deviceAffiliationIds =
-                    getUserData(UserHandle.USER_SYSTEM).mAffiliationIds;
-            for (String id : userAffiliationIds) {
-                if (deviceAffiliationIds.contains(id)) {
-                    return true;
-                }
-            }
         }
         return false;
     }
 
-    private synchronized void disableDeviceOwnerManagedSingleUserFeaturesIfNeeded() {
-        final boolean isSingleUserManagedDevice = isDeviceOwnerManagedSingleUserDevice();
-
-        // disable security logging if needed
-        if (!isSingleUserManagedDevice) {
-            mInjector.securityLogSetLoggingEnabledProperty(false);
-            Slog.w(LOG_TAG, "Security logging turned off as it's no longer a single user managed"
-                    + " device.");
-        }
-
-        // disable backup service if needed
-        // note: when clearing DO, the backup service shouldn't be disabled if it was enabled by
-        // the device owner
-        if (mOwners.hasDeviceOwner() && !isSingleUserManagedDevice) {
-            setBackupServiceEnabledInternal(false);
-            Slog.w(LOG_TAG, "Backup is off as it's a managed device that has more that one user.");
-        }
-
-        // disable network logging if needed
-        if (!isSingleUserManagedDevice) {
-            setNetworkLoggingActiveInternal(false);
-            Slog.w(LOG_TAG, "Network logging turned off as it's no longer a single user managed"
-                    + " device.");
-            // if there still is a device owner, disable logging policy, otherwise the admin
-            // has been nuked
-            if (mOwners.hasDeviceOwner()) {
-                getDeviceOwnerAdminLocked().isNetworkLoggingEnabled = false;
-                saveSettingsLocked(mOwners.getDeviceOwnerUserId());
+    private boolean areAllUsersAffiliatedWithDeviceLocked() {
+        final long ident = mInjector.binderClearCallingIdentity();
+        try {
+            final List<UserInfo> userInfos = mUserManager.getUsers();
+            for (int i = 0; i < userInfos.size(); i++) {
+                int userId = userInfos.get(i).id;
+                if (!isUserAffiliatedWithDeviceLocked(userId)) {
+                    Slog.d(LOG_TAG, "User id " + userId + " not affiliated.");
+                    return false;
+                }
             }
+        } finally {
+            mInjector.binderRestoreCallingIdentity(ident);
         }
+
+        return true;
     }
 
     @Override
     public void setSecurityLoggingEnabled(ComponentName admin, boolean enabled) {
+        if (!mHasFeature) {
+            return;
+        }
         Preconditions.checkNotNull(admin);
-        ensureDeviceOwnerManagingSingleUser(admin);
 
         synchronized (this) {
+            getActiveAdminForCallerLocked(admin, DeviceAdminInfo.USES_POLICY_DEVICE_OWNER);
             if (enabled == mInjector.securityLogGetLoggingEnabledProperty()) {
                 return;
             }
             mInjector.securityLogSetLoggingEnabledProperty(enabled);
             if (enabled) {
                 mSecurityLogMonitor.start();
+                maybePauseDeviceWideLoggingLocked();
             } else {
                 mSecurityLogMonitor.stop();
             }
@@ -9547,6 +9547,10 @@
 
     @Override
     public boolean isSecurityLoggingEnabled(ComponentName admin) {
+        if (!mHasFeature) {
+            return false;
+        }
+
         Preconditions.checkNotNull(admin);
         synchronized (this) {
             getActiveAdminForCallerLocked(admin, DeviceAdminInfo.USES_POLICY_DEVICE_OWNER);
@@ -9565,10 +9569,15 @@
 
     @Override
     public ParceledListSlice<SecurityEvent> retrievePreRebootSecurityLogs(ComponentName admin) {
-        Preconditions.checkNotNull(admin);
-        ensureDeviceOwnerManagingSingleUser(admin);
+        if (!mHasFeature) {
+            return null;
+        }
 
-        if (!mContext.getResources().getBoolean(R.bool.config_supportPreRebootSecurityLogs)) {
+        Preconditions.checkNotNull(admin);
+        ensureDeviceOwnerAndAllUsersAffiliated(admin);
+
+        if (!mContext.getResources().getBoolean(R.bool.config_supportPreRebootSecurityLogs)
+                || !mInjector.securityLogGetLoggingEnabledProperty()) {
             return null;
         }
 
@@ -9586,8 +9595,16 @@
 
     @Override
     public ParceledListSlice<SecurityEvent> retrieveSecurityLogs(ComponentName admin) {
+        if (!mHasFeature) {
+            return null;
+        }
+
         Preconditions.checkNotNull(admin);
-        ensureDeviceOwnerManagingSingleUser(admin);
+        ensureDeviceOwnerAndAllUsersAffiliated(admin);
+
+        if (!mInjector.securityLogGetLoggingEnabledProperty()) {
+            return null;
+        }
 
         recordSecurityLogRetrievalTime();
 
@@ -9794,18 +9811,21 @@
         }
     }
 
+    // TODO(b/22388012): When backup is available for secondary users and profiles, consider
+    // whether there are any privacy/security implications of enabling the backup service here
+    // if there are other users or profiles unmanaged or managed by a different entity (i.e. not
+    // affiliated).
     @Override
     public void setBackupServiceEnabled(ComponentName admin, boolean enabled) {
-        Preconditions.checkNotNull(admin);
         if (!mHasFeature) {
             return;
         }
-        ensureDeviceOwnerManagingSingleUser(admin);
-        setBackupServiceEnabledInternal(enabled);
-    }
+        Preconditions.checkNotNull(admin);
+        synchronized (this) {
+            getActiveAdminForCallerLocked(admin, DeviceAdminInfo.USES_POLICY_DEVICE_OWNER);
+        }
 
-    private synchronized void setBackupServiceEnabledInternal(boolean enabled) {
-        long ident = mInjector.binderClearCallingIdentity();
+        final long ident = mInjector.binderClearCallingIdentity();
         try {
             IBackupManager ibm = mInjector.getIBackupManager();
             if (ibm != null) {
@@ -9906,7 +9926,7 @@
             final boolean isCallerDeviceOwner = isDeviceOwner(callingOwner);
             final boolean isCallerManagedProfile = isManagedProfile(callingUserId);
             if ((!isCallerDeviceOwner && !isCallerManagedProfile)
-                    || !isUserAffiliatedWithDevice(callingUserId)) {
+                    || !isUserAffiliatedWithDeviceLocked(callingUserId)) {
                 return targetUsers;
             }
 
@@ -9926,7 +9946,7 @@
 
                         // Both must be the same package and be affiliated in order to bind.
                         if (callingOwnerPackage.equals(targetOwnerPackage)
-                               && isUserAffiliatedWithDevice(userId)) {
+                               && isUserAffiliatedWithDeviceLocked(userId)) {
                             targetUsers.add(UserHandle.of(userId));
                         }
                     }
@@ -10024,7 +10044,7 @@
             return;
         }
         Preconditions.checkNotNull(admin);
-        ensureDeviceOwnerManagingSingleUser(admin);
+        getActiveAdminForCallerLocked(admin, DeviceAdminInfo.USES_POLICY_DEVICE_OWNER);
 
         if (enabled == isNetworkLoggingEnabledInternalLocked()) {
             // already in the requested state
@@ -10051,10 +10071,10 @@
                     Slog.wtf(LOG_TAG, "Network logging could not be started due to the logging"
                             + " service not being available yet.");
                 }
+                maybePauseDeviceWideLoggingLocked();
                 sendNetworkLoggingNotificationLocked();
             } else {
                 if (mNetworkLogger != null && !mNetworkLogger.stopNetworkLogging()) {
-                    mNetworkLogger = null;
                     Slog.wtf(LOG_TAG, "Network logging could not be stopped due to the logging"
                             + " service not being available yet.");
                 }
@@ -10066,6 +10086,44 @@
         }
     }
 
+    /** Pauses security and network logging if there are unaffiliated users on the device */
+    private void maybePauseDeviceWideLoggingLocked() {
+        if (!areAllUsersAffiliatedWithDeviceLocked()) {
+            Slog.i(LOG_TAG, "There are unaffiliated users, security and network logging will be "
+                    + "paused if enabled.");
+            mSecurityLogMonitor.pause();
+            if (mNetworkLogger != null) {
+                mNetworkLogger.pause();
+            }
+        }
+    }
+
+    /** Resumes security and network logging (if they are enabled) if all users are affiliated */
+    private void maybeResumeDeviceWideLoggingLocked() {
+        if (areAllUsersAffiliatedWithDeviceLocked()) {
+            final long ident = mInjector.binderClearCallingIdentity();
+            try {
+                mSecurityLogMonitor.resume();
+                if (mNetworkLogger != null) {
+                    mNetworkLogger.resume();
+                }
+            } finally {
+                mInjector.binderRestoreCallingIdentity(ident);
+            }
+        }
+    }
+
+    /** Deletes any security and network logs that might have been collected so far */
+    private void discardDeviceWideLogsLocked() {
+        mSecurityLogMonitor.discardLogs();
+        if (mNetworkLogger != null) {
+            mNetworkLogger.discardLogs();
+        }
+        // TODO: We should discard pre-boot security logs here too, as otherwise those
+        // logs (which might contain data from the user just removed) will be
+        // available after next boot.
+    }
+
     @Override
     public boolean isNetworkLoggingEnabled(ComponentName admin) {
         if (!mHasFeature) {
@@ -10090,32 +10148,27 @@
      * @see NetworkLoggingHandler#MAX_EVENTS_PER_BATCH
      */
     @Override
-    public synchronized List<NetworkEvent> retrieveNetworkLogs(ComponentName admin,
-            long batchToken) {
+    public List<NetworkEvent> retrieveNetworkLogs(ComponentName admin, long batchToken) {
         if (!mHasFeature) {
             return null;
         }
         Preconditions.checkNotNull(admin);
-        ensureDeviceOwnerManagingSingleUser(admin);
+        ensureDeviceOwnerAndAllUsersAffiliated(admin);
 
-        if (mNetworkLogger == null) {
-            return null;
-        }
-
-        if (!isNetworkLoggingEnabledInternalLocked()) {
-            return null;
-        }
-
-        final long currentTime = System.currentTimeMillis();
         synchronized (this) {
+            if (mNetworkLogger == null
+                    || !isNetworkLoggingEnabledInternalLocked()) {
+                return null;
+            }
+
+            final long currentTime = System.currentTimeMillis();
             DevicePolicyData policyData = getUserData(UserHandle.USER_SYSTEM);
             if (currentTime > policyData.mLastNetworkLogsRetrievalTime) {
                 policyData.mLastNetworkLogsRetrievalTime = currentTime;
                 saveSettingsLocked(UserHandle.USER_SYSTEM);
             }
+            return mNetworkLogger.retrieveLogs(batchToken);
         }
-
-        return mNetworkLogger.retrieveLogs(batchToken);
     }
 
     private void sendNetworkLoggingNotificationLocked() {
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/NetworkLogger.java b/services/devicepolicy/java/com/android/server/devicepolicy/NetworkLogger.java
index b82cb3c..0085931 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/NetworkLogger.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/NetworkLogger.java
@@ -31,7 +31,6 @@
 
 import com.android.server.ServiceThread;
 
-import java.util.ArrayList;
 import java.util.List;
 import java.util.concurrent.atomic.AtomicBoolean;
 
@@ -130,6 +129,8 @@
         Log.d(TAG, "Stopping network logging");
         // stop the logging regardless of whether we fail to unregister listener
         mIsLoggingEnabled.set(false);
+        discardLogs();
+
         try {
             if (!checkIpConnectivityMetricsService()) {
                 // the IIpConnectivityMetrics service should have been present at this point
@@ -140,9 +141,43 @@
             return mIpConnectivityMetrics.unregisterNetdEventCallback();
         } catch (RemoteException re) {
             Slog.wtf(TAG, "Failed to make remote calls to unregister the callback", re);
-        } finally {
-            mHandlerThread.quitSafely();
             return true;
+        } finally {
+            if (mHandlerThread != null) {
+                mHandlerThread.quitSafely();
+            }
+        }
+    }
+
+    /**
+     * If logs are being collected, keep collecting them but stop notifying the device owner that
+     * new logs are available (since they cannot be retrieved)
+     */
+    void pause() {
+        if (mNetworkLoggingHandler != null) {
+            mNetworkLoggingHandler.pause();
+        }
+    }
+
+    /**
+     * If logs are being collected, start notifying the device owner when logs are ready to be
+     * collected again (if it was paused).
+     * <p>If logging is enabled and there are logs ready to be retrieved, this method will attempt
+     * to notify the device owner. Therefore calling identity should be cleared before calling it
+     * (in case the method is called from a user other than the DO's user).
+     */
+    void resume() {
+        if (mNetworkLoggingHandler != null) {
+            mNetworkLoggingHandler.resume();
+        }
+    }
+
+    /**
+     * Discard all collected logs.
+     */
+    void discardLogs() {
+        if (mNetworkLoggingHandler != null) {
+            mNetworkLoggingHandler.discardLogs();
         }
     }
 
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/NetworkLoggingHandler.java b/services/devicepolicy/java/com/android/server/devicepolicy/NetworkLoggingHandler.java
index baa4c13..7d68412 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/NetworkLoggingHandler.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/NetworkLoggingHandler.java
@@ -55,10 +55,16 @@
     @GuardedBy("this")
     private ArrayList<NetworkEvent> mFullBatch;
 
-    // each full batch is represented by its token, which the DPC has to provide back to revieve it
+    @GuardedBy("this")
+    private boolean mPaused = false;
+
+    // each full batch is represented by its token, which the DPC has to provide back to retrieve it
     @GuardedBy("this")
     private long mCurrentFullBatchToken;
 
+    @GuardedBy("this")
+    private long mLastRetrievedFullBatchToken;
+
     NetworkLoggingHandler(Looper looper, DevicePolicyManagerService dpm) {
         super(looper);
         mDpm = dpm;
@@ -70,15 +76,19 @@
             case LOG_NETWORK_EVENT_MSG: {
                 NetworkEvent networkEvent = msg.getData().getParcelable(NETWORK_EVENT_KEY);
                 if (networkEvent != null) {
-                    mNetworkEvents.add(networkEvent);
-                    if (mNetworkEvents.size() >= MAX_EVENTS_PER_BATCH) {
-                        finalizeBatchAndNotifyDeviceOwnerIfNotEmpty();
+                    synchronized (NetworkLoggingHandler.this) {
+                        mNetworkEvents.add(networkEvent);
+                        if (mNetworkEvents.size() >= MAX_EVENTS_PER_BATCH) {
+                            finalizeBatchAndNotifyDeviceOwnerLocked();
+                        }
                     }
                 }
                 break;
             }
             case FINALIZE_BATCH_MSG: {
-                finalizeBatchAndNotifyDeviceOwnerIfNotEmpty();
+                synchronized (NetworkLoggingHandler.this) {
+                    finalizeBatchAndNotifyDeviceOwnerLocked();
+                }
                 break;
             }
         }
@@ -91,22 +101,49 @@
                 + "ms from now.");
     }
 
-    private synchronized void finalizeBatchAndNotifyDeviceOwnerIfNotEmpty() {
+    synchronized void pause() {
+        Log.d(TAG, "Paused network logging");
+        mPaused = true;
+    }
+
+    synchronized void resume() {
+        if (!mPaused) {
+            Log.d(TAG, "Attempted to resume network logging, but logging is not paused.");
+            return;
+        }
+
+        Log.d(TAG, "Resumed network logging. Current batch="
+                + mCurrentFullBatchToken + ", LastRetrievedBatch=" + mLastRetrievedFullBatchToken);
+        mPaused = false;
+
+        // If there is a full batch ready that the device owner hasn't been notified about, do it
+        // now.
+        if (mFullBatch != null && mFullBatch.size() > 0
+                && mLastRetrievedFullBatchToken != mCurrentFullBatchToken) {
+            scheduleBatchFinalization();
+            notifyDeviceOwnerLocked();
+        }
+    }
+
+    synchronized void discardLogs() {
+        mFullBatch = null;
+        mNetworkEvents = new ArrayList<NetworkEvent>();
+        Log.d(TAG, "Discarded all network logs");
+    }
+
+    @GuardedBy("this")
+    private void finalizeBatchAndNotifyDeviceOwnerLocked() {
         if (mNetworkEvents.size() > 0) {
             // finalize the batch and start a new one from scratch
             mFullBatch = mNetworkEvents;
             mCurrentFullBatchToken++;
             mNetworkEvents = new ArrayList<NetworkEvent>();
-            // notify DO that there's a new non-empty batch waiting
-            Bundle extras = new Bundle();
-            extras.putLong(DeviceAdminReceiver.EXTRA_NETWORK_LOGS_TOKEN, mCurrentFullBatchToken);
-            extras.putInt(DeviceAdminReceiver.EXTRA_NETWORK_LOGS_COUNT, mFullBatch.size());
-            Log.d(TAG, "Sending network logging batch broadcast to device owner, batchToken: "
-                    + mCurrentFullBatchToken);
-            mDpm.sendDeviceOwnerCommand(DeviceAdminReceiver.ACTION_NETWORK_LOGS_AVAILABLE, extras);
+            if (!mPaused) {
+                notifyDeviceOwnerLocked();
+            }
         } else {
             // don't notify the DO, since there are no events; DPC can still retrieve
-            // the last full batch
+            // the last full batch if not paused.
             Log.d(TAG, "Was about to finalize the batch, but there were no events to send to"
                     + " the DPC, the batchToken of last available batch: "
                     + mCurrentFullBatchToken);
@@ -115,10 +152,21 @@
         scheduleBatchFinalization();
     }
 
+    @GuardedBy("this")
+    private void notifyDeviceOwnerLocked() {
+        Bundle extras = new Bundle();
+        extras.putLong(DeviceAdminReceiver.EXTRA_NETWORK_LOGS_TOKEN, mCurrentFullBatchToken);
+        extras.putInt(DeviceAdminReceiver.EXTRA_NETWORK_LOGS_COUNT, mFullBatch.size());
+        Log.d(TAG, "Sending network logging batch broadcast to device owner, batchToken: "
+                + mCurrentFullBatchToken);
+        mDpm.sendDeviceOwnerCommand(DeviceAdminReceiver.ACTION_NETWORK_LOGS_AVAILABLE, extras);
+    }
+
     synchronized List<NetworkEvent> retrieveFullLogBatch(long batchToken) {
         if (batchToken != mCurrentFullBatchToken) {
             return null;
         }
+        mLastRetrievedFullBatchToken = mCurrentFullBatchToken;
         return mFullBatch;
     }
 }
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/SecurityLogMonitor.java b/services/devicepolicy/java/com/android/server/devicepolicy/SecurityLogMonitor.java
index 79702a8..18f06be 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/SecurityLogMonitor.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/SecurityLogMonitor.java
@@ -19,6 +19,7 @@
 import android.app.admin.DeviceAdminReceiver;
 import android.app.admin.SecurityLog;
 import android.app.admin.SecurityLog.SecurityEvent;
+import android.os.SystemClock;
 import android.util.Log;
 import android.util.Slog;
 
@@ -50,7 +51,7 @@
         mService = service;
     }
 
-    private static final boolean DEBUG = false;
+    private static final boolean DEBUG = false;  // STOPSHIP if true.
     private static final String TAG = "SecurityLogMonitor";
     /**
      * Each log entry can hold up to 4K bytes (but as of {@link android.os.Build.VERSION_CODES#N}
@@ -78,17 +79,25 @@
     private ArrayList<SecurityEvent> mPendingLogs = new ArrayList<SecurityEvent>();
     @GuardedBy("mLock")
     private boolean mAllowedToRetrieve = false;
-    // When DO will be allowed to retrieves the log, in milliseconds.
+
+    /**
+     * When DO will be allowed to retrieve the log, in milliseconds since boot (as per
+     * {@link SystemClock#elapsedRealtime()})
+     */
     @GuardedBy("mLock")
-    private long mNextAllowedRetrivalTimeMillis = -1;
+    private long mNextAllowedRetrievalTimeMillis = -1;
+    @GuardedBy("mLock")
+    private boolean mPaused = false;
 
     void start() {
+        Slog.i(TAG, "Starting security logging.");
         mLock.lock();
         try {
             if (mMonitorThread == null) {
                 mPendingLogs = new ArrayList<SecurityEvent>();
                 mAllowedToRetrieve = false;
-                mNextAllowedRetrivalTimeMillis = -1;
+                mNextAllowedRetrievalTimeMillis = -1;
+                mPaused = false;
 
                 mMonitorThread = new Thread(this);
                 mMonitorThread.start();
@@ -99,6 +108,7 @@
     }
 
     void stop() {
+        Slog.i(TAG, "Stopping security logging.");
         mLock.lock();
         try {
             if (mMonitorThread != null) {
@@ -111,7 +121,8 @@
                 // Reset state and clear buffer
                 mPendingLogs = new ArrayList<SecurityEvent>();
                 mAllowedToRetrieve = false;
-                mNextAllowedRetrivalTimeMillis = -1;
+                mNextAllowedRetrievalTimeMillis = -1;
+                mPaused = false;
                 mMonitorThread = null;
             }
         } finally {
@@ -120,6 +131,58 @@
     }
 
     /**
+     * If logs are being collected, keep collecting them but stop notifying the device owner that
+     * new logs are available (since they cannot be retrieved).
+     */
+    void pause() {
+        Slog.i(TAG, "Paused.");
+
+        mLock.lock();
+        mPaused = true;
+        mAllowedToRetrieve = false;
+        mLock.unlock();
+    }
+
+    /**
+     * If logs are being collected, start notifying the device owner when logs are ready to be
+     * retrieved again (if it was paused).
+     * <p>If logging is enabled and there are logs ready to be retrieved, this method will attempt
+     * to notify the device owner. Therefore calling identity should be cleared before calling it
+     * (in case the method is called from a user other than the DO's user).
+     */
+    void resume() {
+        mLock.lock();
+        try {
+            if (!mPaused) {
+                Log.d(TAG, "Attempted to resume, but logging is not paused.");
+                return;
+            }
+            mPaused = false;
+            mAllowedToRetrieve = false;
+        } finally {
+            mLock.unlock();
+        }
+
+        Slog.i(TAG, "Resumed.");
+        try {
+            notifyDeviceOwnerIfNeeded();
+        } catch (InterruptedException e) {
+            Log.w(TAG, "Thread interrupted.", e);
+        }
+    }
+
+    /**
+     * Discard all collected logs.
+     */
+    void discardLogs() {
+        mLock.lock();
+        mAllowedToRetrieve = false;
+        mPendingLogs = new ArrayList<SecurityEvent>();
+        mLock.unlock();
+        Slog.i(TAG, "Discarded all logs.");
+    }
+
+    /**
      * Returns the new batch of logs since the last call to this method. Returns null if
      * rate limit is exceeded.
      */
@@ -128,7 +191,7 @@
         try {
             if (mAllowedToRetrieve) {
                 mAllowedToRetrieve = false;
-                mNextAllowedRetrivalTimeMillis = System.currentTimeMillis()
+                mNextAllowedRetrievalTimeMillis = SystemClock.elapsedRealtime()
                         + RATE_LIMIT_INTERVAL_MILLISECONDS;
                 List<SecurityEvent> result = mPendingLogs;
                 mPendingLogs = new ArrayList<SecurityEvent>();
@@ -163,7 +226,7 @@
                     SecurityLog.readEventsSince(lastLogTimestampNanos + 1, logs);
                 }
                 if (!logs.isEmpty()) {
-                    if (DEBUG) Slog.d(TAG, "processing new logs");
+                    if (DEBUG) Slog.d(TAG, "processing new logs. Events: " + logs.size());
                     mLock.lockInterruptibly();
                     try {
                         mPendingLogs.addAll(logs);
@@ -172,6 +235,7 @@
                             mPendingLogs = new ArrayList<SecurityEvent>(mPendingLogs.subList(
                                     mPendingLogs.size() - (BUFFER_ENTRIES_MAXIMUM_LEVEL / 2),
                                     mPendingLogs.size()));
+                            Slog.i(TAG, "Pending logs buffer full. Discarding old logs.");
                         }
                     } finally {
                         mLock.unlock();
@@ -188,7 +252,7 @@
                 break;
             }
         }
-        if (DEBUG) Slog.d(TAG, "MonitorThread exit.");
+        Slog.i(TAG, "MonitorThread exit.");
     }
 
     private void notifyDeviceOwnerIfNeeded() throws InterruptedException {
@@ -196,15 +260,24 @@
         boolean allowToRetrieveNow = false;
         mLock.lockInterruptibly();
         try {
+            if (mPaused) {
+                return;
+            }
+
+            // STOPSHIP(b/34186771): If the previous notification didn't reach the DO and logs were
+            // not retrieved (e.g. the broadcast was sent before the user was unlocked), no more
+            // subsequent callbacks will be sent. We should make sure that the DO gets notified
+            // before logs are lost.
             int logSize = mPendingLogs.size();
             if (logSize >= BUFFER_ENTRIES_NOTIFICATION_LEVEL) {
                 // Allow DO to retrieve logs if too many pending logs
                 allowToRetrieveNow = true;
+                if (DEBUG) Slog.d(TAG, "Number of log entries over threshold: " + logSize);
             } else if (logSize > 0) {
-                if (mNextAllowedRetrivalTimeMillis == -1 ||
-                        System.currentTimeMillis() >= mNextAllowedRetrivalTimeMillis) {
+                if (SystemClock.elapsedRealtime() >= mNextAllowedRetrievalTimeMillis) {
                     // Rate limit reset
                     allowToRetrieveNow = true;
+                    if (DEBUG) Slog.d(TAG, "Timeout reached");
                 }
             }
             shouldNotifyDO = (!mAllowedToRetrieve) && allowToRetrieveNow;
@@ -213,7 +286,7 @@
             mLock.unlock();
         }
         if (shouldNotifyDO) {
-            if (DEBUG) Slog.d(TAG, "notify DO");
+            Slog.i(TAG, "notify DO");
             mService.sendDeviceOwnerCommand(DeviceAdminReceiver.ACTION_SECURITY_LOGS_AVAILABLE,
                     null);
         }
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 68cb0c5..08fb591 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -175,6 +175,8 @@
             "com.google.android.clockwork.bluetooth.WearBluetoothService";
     private static final String WEAR_WIFI_MEDIATOR_SERVICE_CLASS =
             "com.google.android.clockwork.wifi.WearWifiMediatorService";
+    private static final String WEAR_CELLULAR_MEDIATOR_SERVICE_CLASS =
+            "com.google.android.clockwork.cellular.WearCellularMediatorService";
     private static final String WEAR_TIME_SERVICE_CLASS =
             "com.google.android.clockwork.time.WearTimeService";
     private static final String ACCOUNT_SERVICE_CLASS =
@@ -1381,6 +1383,13 @@
             traceBeginAndSlog("StartWearWifiMediator");
             mSystemServiceManager.startService(WEAR_WIFI_MEDIATOR_SERVICE_CLASS);
             traceEnd();
+
+            if (SystemProperties.getBoolean("config.enable_cellmediator", false)) {
+                traceBeginAndSlog("StartWearCellularMediator");
+                mSystemServiceManager.startService(WEAR_CELLULAR_MEDIATOR_SERVICE_CLASS);
+                traceEnd();
+            }
+
             if (!disableNonCoreServices) {
                 traceBeginAndSlog("StartWearTimeService");
                 mSystemServiceManager.startService(WEAR_TIME_SERVICE_CLASS);
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
index e68895e0..60f4360 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
@@ -2845,7 +2845,10 @@
     public void testGetLastSecurityLogRetrievalTime() throws Exception {
         mContext.binder.callingUid = DpmMockContext.CALLER_SYSTEM_USER_UID;
         setupDeviceOwner();
-        when(mContext.userManager.getUserCount()).thenReturn(1);
+
+        // setUp() adds a secondary user for CALLER_USER_HANDLE. Remove it as otherwise the
+        // feature is disabled because there are non-affiliated secondary users.
+        mContext.removeUser(DpmMockContext.CALLER_USER_HANDLE);
         when(mContext.resources.getBoolean(R.bool.config_supportPreRebootSecurityLogs))
                 .thenReturn(true);
 
@@ -2854,6 +2857,10 @@
 
         // Enabling logging should not change the timestamp.
         dpm.setSecurityLoggingEnabled(admin1, true);
+        verify(mContext.settings)
+                .securityLogSetLoggingEnabledProperty(true);
+        when(mContext.settings.securityLogGetLoggingEnabledProperty())
+                .thenReturn(true);
         assertEquals(-1, dpm.getLastSecurityLogRetrievalTime());
 
         // Retrieving the logs should update the timestamp.
@@ -2906,7 +2913,7 @@
     public void testGetLastBugReportRequestTime() throws Exception {
         mContext.binder.callingUid = DpmMockContext.CALLER_SYSTEM_USER_UID;
         setupDeviceOwner();
-        when(mContext.userManager.getUserCount()).thenReturn(1);
+
         mContext.packageName = admin1.getPackageName();
         mContext.applicationInfo = new ApplicationInfo();
         when(mContext.resources.getColor(eq(R.color.notification_action_list), anyObject()))
@@ -2914,6 +2921,10 @@
         when(mContext.resources.getColor(eq(R.color.notification_material_background_color),
                 anyObject())).thenReturn(Color.WHITE);
 
+        // setUp() adds a secondary user for CALLER_USER_HANDLE. Remove it as otherwise the
+        // feature is disabled because there are non-affiliated secondary users.
+        mContext.removeUser(DpmMockContext.CALLER_USER_HANDLE);
+
         // No bug reports were requested so far.
         assertEquals(-1, dpm.getLastBugReportRequestTime());
 
@@ -2951,7 +2962,16 @@
     public void testGetLastNetworkLogRetrievalTime() throws Exception {
         mContext.binder.callingUid = DpmMockContext.CALLER_SYSTEM_USER_UID;
         setupDeviceOwner();
-        when(mContext.userManager.getUserCount()).thenReturn(1);
+        mContext.packageName = admin1.getPackageName();
+        mContext.applicationInfo = new ApplicationInfo();
+        when(mContext.resources.getColor(eq(R.color.notification_action_list), anyObject()))
+                .thenReturn(Color.WHITE);
+        when(mContext.resources.getColor(eq(R.color.notification_material_background_color),
+                anyObject())).thenReturn(Color.WHITE);
+
+        // setUp() adds a secondary user for CALLER_USER_HANDLE. Remove it as otherwise the
+        // feature is disabled because there are non-affiliated secondary users.
+        mContext.removeUser(DpmMockContext.CALLER_USER_HANDLE);
         when(mContext.iipConnectivityMetrics.registerNetdEventCallback(anyObject()))
                 .thenReturn(true);
 
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DpmMockContext.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DpmMockContext.java
index a1b6769..44bf547 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DpmMockContext.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DpmMockContext.java
@@ -393,6 +393,18 @@
         return dir;
     }
 
+    public void removeUser(int userId) {
+        for (int i = 0; i < mUserInfos.size(); i++) {
+            if (mUserInfos.get(i).id == userId) {
+                mUserInfos.remove(i);
+                break;
+            }
+        }
+        when(userManager.getUserInfo(eq(userId))).thenReturn(null);
+
+        when(userManager.isUserRunning(eq(new UserHandle(userId)))).thenReturn(false);
+    }
+
     private UserInfo getUserInfo(int userId) {
         for (UserInfo ui : mUserInfos) {
             if (ui.id == userId) {
diff --git a/services/usage/java/com/android/server/usage/UserUsageStatsService.java b/services/usage/java/com/android/server/usage/UserUsageStatsService.java
index ba770ef..d9f352c 100644
--- a/services/usage/java/com/android/server/usage/UserUsageStatsService.java
+++ b/services/usage/java/com/android/server/usage/UserUsageStatsService.java
@@ -188,7 +188,6 @@
                 String[] annotations = event.mContentAnnotations;
                 if (annotations != null) {
                     for (String annotation : annotations) {
-                        // TODO(kanlig): update with confidences of annotations.
                         stats.updateChooserCounts(event.mPackage, annotation, event.mAction);
                     }
                 }
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index 861a1eb..eb838fa 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -936,8 +936,10 @@
      *
      * <p>Requires Permission:
      *   {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE}
+     *
+     * @hide
      */
-    /** {@hide} */
+    @SystemApi
     public String getImei() {
         return getImei(getDefaultSim());
     }
@@ -949,8 +951,10 @@
      *   {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE}
      *
      * @param slotId of which deviceID is returned
+     *
+     * @hide
      */
-    /** {@hide} */
+    @SystemApi
     public String getImei(int slotId) {
         ITelephony telephony = getITelephony();
         if (telephony == null) return null;
diff --git a/services/tests/servicestests/src/android/net/util/BlockingSocketReaderTest.java b/tests/net/java/android/net/util/BlockingSocketReaderTest.java
similarity index 100%
rename from services/tests/servicestests/src/android/net/util/BlockingSocketReaderTest.java
rename to tests/net/java/android/net/util/BlockingSocketReaderTest.java
diff --git a/services/tests/servicestests/src/android/net/util/ConnectivityPacketSummaryTest.java b/tests/net/java/android/net/util/ConnectivityPacketSummaryTest.java
similarity index 100%
rename from services/tests/servicestests/src/android/net/util/ConnectivityPacketSummaryTest.java
rename to tests/net/java/android/net/util/ConnectivityPacketSummaryTest.java
diff --git a/tests/net/java/com/android/server/connectivity/IpConnectivityEventBuilderTest.java b/tests/net/java/com/android/server/connectivity/IpConnectivityEventBuilderTest.java
index f9cac43..415911e 100644
--- a/tests/net/java/com/android/server/connectivity/IpConnectivityEventBuilderTest.java
+++ b/tests/net/java/com/android/server/connectivity/IpConnectivityEventBuilderTest.java
@@ -58,8 +58,8 @@
         String want = joinLines(
                 "dropped_events: 0",
                 "events <",
+                "  link_layer: 0",
                 "  time_ms: 1",
-                "  transport: 0",
                 "  default_network_event <",
                 "    network_id <",
                 "      network_id: 102",
@@ -89,8 +89,8 @@
         String want = joinLines(
                 "dropped_events: 0",
                 "events <",
+                "  link_layer: 0",
                 "  time_ms: 1",
-                "  transport: 0",
                 "  dhcp_event <",
                 "    duration_ms: 192",
                 "    if_name: \"wlan0\"",
@@ -112,8 +112,8 @@
         String want = joinLines(
                 "dropped_events: 0",
                 "events <",
+                "  link_layer: 0",
                 "  time_ms: 1",
-                "  transport: 0",
                 "  dhcp_event <",
                 "    duration_ms: 0",
                 "    if_name: \"wlan0\"",
@@ -137,8 +137,8 @@
         String want = joinLines(
                 "dropped_events: 0",
                 "events <",
+                "  link_layer: 0",
                 "  time_ms: 1",
-                "  transport: 0",
                 "  dns_lookup_batch <",
                 "    event_types: 1",
                 "    event_types: 1",
@@ -185,8 +185,8 @@
         String want = joinLines(
                 "dropped_events: 0",
                 "events <",
+                "  link_layer: 0",
                 "  time_ms: 1",
-                "  transport: 0",
                 "  ip_provisioning_event <",
                 "    event_type: 1",
                 "    if_name: \"wlan0\"",
@@ -208,8 +208,8 @@
         String want = joinLines(
                 "dropped_events: 0",
                 "events <",
+                "  link_layer: 0",
                 "  time_ms: 1",
-                "  transport: 0",
                 "  ip_reachability_event <",
                 "    event_type: 512",
                 "    if_name: \"wlan0\"",
@@ -231,8 +231,8 @@
         String want = joinLines(
                 "dropped_events: 0",
                 "events <",
+                "  link_layer: 0",
                 "  time_ms: 1",
-                "  transport: 0",
                 "  network_event <",
                 "    event_type: 5",
                 "    latency_ms: 20410",
@@ -258,8 +258,8 @@
         String want = joinLines(
                 "dropped_events: 0",
                 "events <",
+                "  link_layer: 0",
                 "  time_ms: 1",
-                "  transport: 0",
                 "  validation_probe_event <",
                 "    latency_ms: 40730",
                 "    network_id <",
@@ -287,8 +287,8 @@
         String want = joinLines(
                 "dropped_events: 0",
                 "events <",
+                "  link_layer: 0",
                 "  time_ms: 1",
-                "  transport: 0",
                 "  apf_program_event <",
                 "    current_ras: 9",
                 "    drop_multicast: true",
@@ -319,8 +319,8 @@
         String want = joinLines(
                 "dropped_events: 0",
                 "events <",
+                "  link_layer: 0",
                 "  time_ms: 1",
-                "  transport: 0",
                 "  apf_statistics <",
                 "    dropped_ras: 2",
                 "    duration_ms: 45000",
@@ -351,8 +351,8 @@
         String want = joinLines(
                 "dropped_events: 0",
                 "events <",
+                "  link_layer: 0",
                 "  time_ms: 1",
-                "  transport: 0",
                 "  ra_event <",
                 "    dnssl_lifetime: -1",
                 "    prefix_preferred_lifetime: 300",
diff --git a/tests/net/java/com/android/server/connectivity/IpConnectivityMetricsTest.java b/tests/net/java/com/android/server/connectivity/IpConnectivityMetricsTest.java
index e9257fa..f56f3f8 100644
--- a/tests/net/java/com/android/server/connectivity/IpConnectivityMetricsTest.java
+++ b/tests/net/java/com/android/server/connectivity/IpConnectivityMetricsTest.java
@@ -158,16 +158,16 @@
         String want = joinLines(
                 "dropped_events: 0",
                 "events <",
+                "  link_layer: 0",
                 "  time_ms: 100",
-                "  transport: 0",
                 "  ip_reachability_event <",
                 "    event_type: 512",
                 "    if_name: \"wlan0\"",
                 "  >",
                 ">",
                 "events <",
+                "  link_layer: 0",
                 "  time_ms: 200",
-                "  transport: 0",
                 "  dhcp_event <",
                 "    duration_ms: 192",
                 "    if_name: \"wlan0\"",
@@ -175,8 +175,8 @@
                 "  >",
                 ">",
                 "events <",
+                "  link_layer: 0",
                 "  time_ms: 300",
-                "  transport: 0",
                 "  default_network_event <",
                 "    network_id <",
                 "      network_id: 102",
@@ -191,8 +191,8 @@
                 "  >",
                 ">",
                 "events <",
+                "  link_layer: 0",
                 "  time_ms: 400",
-                "  transport: 0",
                 "  ip_provisioning_event <",
                 "    event_type: 1",
                 "    if_name: \"wlan0\"",
@@ -200,8 +200,8 @@
                 "  >",
                 ">",
                 "events <",
+                "  link_layer: 0",
                 "  time_ms: 500",
-                "  transport: 0",
                 "  validation_probe_event <",
                 "    latency_ms: 40730",
                 "    network_id <",
@@ -212,8 +212,8 @@
                 "  >",
                 ">",
                 "events <",
+                "  link_layer: 0",
                 "  time_ms: 600",
-                "  transport: 0",
                 "  apf_statistics <",
                 "    dropped_ras: 2",
                 "    duration_ms: 45000",
@@ -226,8 +226,8 @@
                 "  >",
                 ">",
                 "events <",
+                "  link_layer: 0",
                 "  time_ms: 700",
-                "  transport: 0",
                 "  ra_event <",
                 "    dnssl_lifetime: -1",
                 "    prefix_preferred_lifetime: 300",
diff --git a/tests/net/java/com/android/server/connectivity/NetdEventListenerServiceTest.java b/tests/net/java/com/android/server/connectivity/NetdEventListenerServiceTest.java
index 75d2f1a..cfd5598 100644
--- a/tests/net/java/com/android/server/connectivity/NetdEventListenerServiceTest.java
+++ b/tests/net/java/com/android/server/connectivity/NetdEventListenerServiceTest.java
@@ -213,8 +213,8 @@
 
         IpConnectivityEvent got = events.get(0);
         String want = String.join("\n",
+                "link_layer: 0",
                 "time_ms: 0",
-                "transport: 0",
                 "connect_statistics <",
                 "  connect_count: 12",
                 "  errnos_counters <",
diff --git a/tests/net/java/com/android/server/connectivity/NetworkNotificationManagerTest.java b/tests/net/java/com/android/server/connectivity/NetworkNotificationManagerTest.java
index 98073ce..21c2de7 100644
--- a/tests/net/java/com/android/server/connectivity/NetworkNotificationManagerTest.java
+++ b/tests/net/java/com/android/server/connectivity/NetworkNotificationManagerTest.java
@@ -80,6 +80,7 @@
         when(mCtx.getResources()).thenReturn(mResources);
         when(mCtx.getPackageManager()).thenReturn(mPm);
         when(mCtx.getApplicationInfo()).thenReturn(new ApplicationInfo());
+        when(mNetworkInfo.getExtraInfo()).thenReturn("extra");
         when(mResources.getColor(anyInt(), any())).thenReturn(0xFF607D8B);
 
         mManager = new NetworkNotificationManager(mCtx, mTelephonyManager, mNotificationManager);
diff --git a/tests/net/java/com/android/server/connectivity/tethering/UpstreamNetworkMonitorTest.java b/tests/net/java/com/android/server/connectivity/tethering/UpstreamNetworkMonitorTest.java
new file mode 100644
index 0000000..b2a9a49
--- /dev/null
+++ b/tests/net/java/com/android/server/connectivity/tethering/UpstreamNetworkMonitorTest.java
@@ -0,0 +1,213 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.connectivity.tethering;
+
+import static android.net.NetworkCapabilities.NET_CAPABILITY_DUN;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.reset;
+
+import android.content.Context;
+import android.net.ConnectivityManager;
+import android.net.ConnectivityManager.NetworkCallback;
+import android.net.IConnectivityManager;
+import android.net.Network;
+import android.net.NetworkCapabilities;
+import android.net.NetworkRequest;
+
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.runner.RunWith;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class UpstreamNetworkMonitorTest {
+    private static final int EVENT_UNM_UPDATE = 1;
+
+    @Mock private Context mContext;
+    @Mock private IConnectivityManager mCS;
+
+    private TestConnectivityManager mCM;
+    private UpstreamNetworkMonitor mUNM;
+
+    @Before public void setUp() throws Exception {
+        MockitoAnnotations.initMocks(this);
+        reset(mContext);
+        reset(mCS);
+
+        mCM = new TestConnectivityManager(mContext, mCS);
+        mUNM = new UpstreamNetworkMonitor(null, EVENT_UNM_UPDATE, (ConnectivityManager) mCM);
+    }
+
+    @Test
+    public void testDoesNothingBeforeStarted() {
+        UpstreamNetworkMonitor unm = new UpstreamNetworkMonitor(null, null, EVENT_UNM_UPDATE);
+        assertFalse(unm.mobileNetworkRequested());
+        // Given a null Context, and therefore a null ConnectivityManager,
+        // these would cause an exception, if they actually attempted anything.
+        unm.mobileUpstreamRequiresDun(true);
+        unm.mobileUpstreamRequiresDun(false);
+    }
+
+    @Test
+    public void testDefaultNetworkIsTracked() throws Exception {
+        assertEquals(0, mCM.trackingDefault.size());
+
+        mUNM.start();
+        assertEquals(1, mCM.trackingDefault.size());
+
+        mUNM.stop();
+        assertTrue(mCM.isEmpty());
+    }
+
+    @Test
+    public void testListensForDunNetworks() throws Exception {
+        assertTrue(mCM.listening.isEmpty());
+
+        mUNM.start();
+        assertFalse(mCM.listening.isEmpty());
+        assertTrue(mCM.isListeningForDun());
+
+        mUNM.stop();
+        assertTrue(mCM.isEmpty());
+    }
+
+    @Test
+    public void testCanRequestMobileNetwork() throws Exception {
+        assertFalse(mUNM.mobileNetworkRequested());
+        assertEquals(0, mCM.requested.size());
+
+        mUNM.start();
+        assertFalse(mUNM.mobileNetworkRequested());
+        assertEquals(0, mCM.requested.size());
+
+        mUNM.mobileUpstreamRequiresDun(false);
+        assertFalse(mUNM.mobileNetworkRequested());
+        assertEquals(0, mCM.requested.size());
+
+        mUNM.registerMobileNetworkRequest();
+        assertTrue(mUNM.mobileNetworkRequested());
+        assertEquals(1, mCM.requested.size());
+        assertFalse(mCM.isDunRequested());
+
+        mUNM.stop();
+        assertFalse(mUNM.mobileNetworkRequested());
+        assertTrue(mCM.isEmpty());
+    }
+
+    @Test
+    public void testCanRequestDunNetwork() throws Exception {
+        assertFalse(mUNM.mobileNetworkRequested());
+        assertEquals(0, mCM.requested.size());
+
+        mUNM.start();
+        assertFalse(mUNM.mobileNetworkRequested());
+        assertEquals(0, mCM.requested.size());
+
+        mUNM.mobileUpstreamRequiresDun(true);
+        assertFalse(mUNM.mobileNetworkRequested());
+        assertEquals(0, mCM.requested.size());
+
+        mUNM.registerMobileNetworkRequest();
+        assertTrue(mUNM.mobileNetworkRequested());
+        assertEquals(1, mCM.requested.size());
+        assertTrue(mCM.isDunRequested());
+
+        mUNM.stop();
+        assertFalse(mUNM.mobileNetworkRequested());
+        assertTrue(mCM.isEmpty());
+    }
+
+    private static class TestConnectivityManager extends ConnectivityManager {
+        public Set<NetworkCallback> trackingDefault = new HashSet<>();
+        public Map<NetworkCallback, NetworkRequest> listening = new HashMap<>();
+        public Map<NetworkCallback, NetworkRequest> requested = new HashMap<>();
+
+        public TestConnectivityManager(Context ctx, IConnectivityManager svc) {
+            super(ctx, svc);
+        }
+
+        boolean isEmpty() {
+            return trackingDefault.isEmpty() &&
+                   listening.isEmpty() &&
+                   requested.isEmpty();
+        }
+
+        boolean isListeningForDun() {
+            for (NetworkRequest req : listening.values()) {
+                if (req.networkCapabilities.hasCapability(NET_CAPABILITY_DUN)) {
+                    return true;
+                }
+            }
+            return false;
+        }
+
+        boolean isDunRequested() {
+            for (NetworkRequest req : requested.values()) {
+                if (req.networkCapabilities.hasCapability(NET_CAPABILITY_DUN)) {
+                    return true;
+                }
+            }
+            return false;
+        }
+
+        @Override
+        public void requestNetwork(NetworkRequest req, NetworkCallback cb) {
+            assertFalse(requested.containsKey(cb));
+            requested.put(cb, req);
+        }
+
+        @Override
+        public void registerNetworkCallback(NetworkRequest req, NetworkCallback cb) {
+            assertFalse(listening.containsKey(cb));
+            listening.put(cb, req);
+        }
+
+        @Override
+        public void registerDefaultNetworkCallback(NetworkCallback cb) {
+            assertFalse(trackingDefault.contains(cb));
+            trackingDefault.add(cb);
+        }
+
+        @Override
+        public void unregisterNetworkCallback(NetworkCallback cb) {
+            if (trackingDefault.contains(cb)) {
+                trackingDefault.remove(cb);
+            } else if (listening.containsKey(cb)) {
+                listening.remove(cb);
+            } else if (requested.containsKey(cb)) {
+                requested.remove(cb);
+            }
+
+            assertFalse(trackingDefault.contains(cb));
+            assertFalse(listening.containsKey(cb));
+            assertFalse(requested.containsKey(cb));
+        }
+    }
+}
diff --git a/tools/layoutlib/bridge/src/android/animation/AnimationThread.java b/tools/layoutlib/bridge/src/android/animation/AnimationThread.java
index b10ec9f..ce2aec79 100644
--- a/tools/layoutlib/bridge/src/android/animation/AnimationThread.java
+++ b/tools/layoutlib/bridge/src/android/animation/AnimationThread.java
@@ -25,7 +25,6 @@
 
 import android.os.Handler;
 import android.os.Handler_Delegate;
-import android.os.Handler_Delegate.IHandlerCallback;
 import android.os.Message;
 
 import java.util.PriorityQueue;
diff --git a/tools/layoutlib/bridge/src/android/content/res/Resources_Delegate.java b/tools/layoutlib/bridge/src/android/content/res/Resources_Delegate.java
index c3d4cef6..e0f8e1c 100644
--- a/tools/layoutlib/bridge/src/android/content/res/Resources_Delegate.java
+++ b/tools/layoutlib/bridge/src/android/content/res/Resources_Delegate.java
@@ -21,6 +21,7 @@
 import com.android.ide.common.rendering.api.DensityBasedResourceValue;
 import com.android.ide.common.rendering.api.LayoutLog;
 import com.android.ide.common.rendering.api.LayoutlibCallback;
+import com.android.ide.common.rendering.api.PluralsResourceValue;
 import com.android.ide.common.rendering.api.RenderResources;
 import com.android.ide.common.rendering.api.ResourceValue;
 import com.android.layoutlib.bridge.Bridge;
@@ -43,6 +44,7 @@
 import android.content.res.Resources.NotFoundException;
 import android.content.res.Resources.Theme;
 import android.graphics.drawable.Drawable;
+import android.icu.text.PluralRules;
 import android.util.AttributeSet;
 import android.util.DisplayMetrics;
 import android.util.LruCache;
@@ -405,9 +407,6 @@
             rv = resources.mContext.getRenderResources().resolveResValue(rv);
             if (rv != null) {
                 return rv.getValue();
-            } else {
-                Bridge.getLog().error(LayoutLog.TAG_RESOURCES_RESOLVE,
-                        "Unable to resolve resource " + ref, null);
             }
         }
         // Not a reference.
@@ -738,6 +737,48 @@
     }
 
     @LayoutlibDelegate
+    static String getQuantityString(Resources resources, int id, int quantity) throws
+            NotFoundException {
+        Pair<String, ResourceValue> value = getResourceValue(resources, id, mPlatformResourceFlag);
+
+        if (value != null) {
+            if (value.getSecond() instanceof PluralsResourceValue) {
+                PluralsResourceValue pluralsResourceValue = (PluralsResourceValue) value.getSecond();
+                PluralRules pluralRules = PluralRules.forLocale(resources.getConfiguration().getLocales()
+                        .get(0));
+                String strValue = pluralsResourceValue.getValue(pluralRules.select(quantity));
+                if (strValue == null) {
+                    strValue = pluralsResourceValue.getValue(PluralRules.KEYWORD_OTHER);
+                }
+
+                return strValue;
+            }
+            else {
+                return value.getSecond().getValue();
+            }
+        }
+
+        // id was not found or not resolved. Throw a NotFoundException.
+        throwException(resources, id);
+
+        // this is not used since the method above always throws
+        return null;
+    }
+
+    @LayoutlibDelegate
+    static String getQuantityString(Resources resources, int id, int quantity, Object... formatArgs)
+            throws NotFoundException {
+        String raw = getQuantityString(resources, id, quantity);
+        return String.format(resources.getConfiguration().getLocales().get(0), raw, formatArgs);
+    }
+
+    @LayoutlibDelegate
+    static CharSequence getQuantityText(Resources resources, int id, int quantity) throws
+            NotFoundException {
+        return getQuantityString(resources, id, quantity);
+    }
+
+    @LayoutlibDelegate
     static void getValue(Resources resources, int id, TypedValue outValue, boolean resolveRefs)
             throws NotFoundException {
         Pair<String, ResourceValue> value = getResourceValue(resources, id, mPlatformResourceFlag);
diff --git a/tools/layoutlib/bridge/src/android/graphics/BidiRenderer.java b/tools/layoutlib/bridge/src/android/graphics/BidiRenderer.java
index 21f36ce..c6827a3 100644
--- a/tools/layoutlib/bridge/src/android/graphics/BidiRenderer.java
+++ b/tools/layoutlib/bridge/src/android/graphics/BidiRenderer.java
@@ -30,6 +30,7 @@
 import java.awt.Toolkit;
 import java.awt.font.FontRenderContext;
 import java.awt.font.GlyphVector;
+import java.awt.geom.AffineTransform;
 import java.awt.geom.Rectangle2D;
 import java.util.ArrayList;
 import java.util.LinkedList;
@@ -41,6 +42,7 @@
  */
 @SuppressWarnings("deprecation")
 public class BidiRenderer {
+    private static String JAVA_VENDOR = System.getProperty("java.vendor");
 
     private static class ScriptRun {
         int start;
@@ -221,9 +223,16 @@
             frc = mGraphics.getFontRenderContext();
         } else {
             frc = Toolkit.getDefaultToolkit().getFontMetrics(font).getFontRenderContext();
+
             // Metrics obtained this way don't have anti-aliasing set. So,
             // we create a new FontRenderContext with anti-aliasing set.
-            frc = new FontRenderContext(font.getTransform(), mPaint.isAntiAliased(), frc.usesFractionalMetrics());
+            AffineTransform transform = font.getTransform();
+            if (mPaint.isAntiAliased() &&
+                    // Workaround for http://b.android.com/211659
+                    (transform.getScaleX() <= 9.9 ||
+                    !"JetBrains s.r.o".equals(JAVA_VENDOR))) {
+                frc = new FontRenderContext(transform, true, frc.usesFractionalMetrics());
+            }
         }
         GlyphVector gv = font.layoutGlyphVector(frc, mText, start, limit, flag);
         int ng = gv.getNumGlyphs();
diff --git a/tools/layoutlib/bridge/src/android/graphics/Canvas_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/Canvas_Delegate.java
index 43a0ff5..c599e9d 100644
--- a/tools/layoutlib/bridge/src/android/graphics/Canvas_Delegate.java
+++ b/tools/layoutlib/bridge/src/android/graphics/Canvas_Delegate.java
@@ -20,24 +20,14 @@
 import com.android.layoutlib.bridge.Bridge;
 import com.android.layoutlib.bridge.impl.DelegateManager;
 import com.android.layoutlib.bridge.impl.GcSnapshot;
-import com.android.layoutlib.bridge.impl.PorterDuffUtility;
-import com.android.ninepatch.NinePatchChunk;
 import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
 
 import android.annotation.Nullable;
 import android.graphics.Bitmap.Config;
-import android.text.TextUtils;
 
-import java.awt.Color;
-import java.awt.Composite;
 import java.awt.Graphics2D;
 import java.awt.Rectangle;
-import java.awt.RenderingHints;
-import java.awt.Shape;
 import java.awt.geom.AffineTransform;
-import java.awt.geom.Arc2D;
-import java.awt.geom.Rectangle2D;
-import java.awt.image.BufferedImage;
 
 import libcore.util.NativeAllocationRegistry_Delegate;
 
@@ -401,23 +391,6 @@
     }
 
     @LayoutlibDelegate
-    public static boolean nClipRegion(long nativeCanvas,
-                                                    long nativeRegion,
-                                                    int regionOp) {
-        Canvas_Delegate canvasDelegate = Canvas_Delegate.getDelegate(nativeCanvas);
-        if (canvasDelegate == null) {
-            return true;
-        }
-
-        Region_Delegate region = Region_Delegate.getDelegate(nativeRegion);
-        if (region == null) {
-            return true;
-        }
-
-        return canvasDelegate.mSnapshot.clip(region.getJavaArea(), regionOp);
-    }
-
-    @LayoutlibDelegate
     public static void nSetDrawFilter(long nativeCanvas, long nativeFilter) {
         Canvas_Delegate canvasDelegate = Canvas_Delegate.getDelegate(nativeCanvas);
         if (canvasDelegate == null) {
diff --git a/tools/layoutlib/bridge/src/android/graphics/FontFamily_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/FontFamily_Delegate.java
index 50efc7f..d326935 100644
--- a/tools/layoutlib/bridge/src/android/graphics/FontFamily_Delegate.java
+++ b/tools/layoutlib/bridge/src/android/graphics/FontFamily_Delegate.java
@@ -248,14 +248,17 @@
     // ---- delegate methods ----
     @LayoutlibDelegate
     /*package*/ static boolean addFont(FontFamily thisFontFamily, String path, int ttcIndex) {
-        final FontFamily_Delegate delegate = getDelegate(thisFontFamily.mNativePtr);
+        if (thisFontFamily.mBuilderPtr == 0) {
+            throw new IllegalStateException("Unable to call addFont after freezing.");
+        }
+        final FontFamily_Delegate delegate = getDelegate(thisFontFamily.mBuilderPtr);
         return delegate != null && delegate.addFont(path, ttcIndex);
     }
 
     // ---- native methods ----
 
     @LayoutlibDelegate
-    /*package*/ static long nCreateFamily(String lang, int variant) {
+    /*package*/ static long nInitBuilder(String lang, int variant) {
         // TODO: support lang. This is required for japanese locale.
         FontFamily_Delegate delegate = new FontFamily_Delegate();
         // variant can be 0, 1 or 2.
@@ -270,6 +273,11 @@
     }
 
     @LayoutlibDelegate
+    /*package*/ static long nCreateFamily(long builderPtr) {
+        return builderPtr;
+    }
+
+    @LayoutlibDelegate
     /*package*/ static void nUnrefFamily(long nativePtr) {
         // Removing the java reference for the object doesn't mean that it's freed for garbage
         // collection. Typeface_Delegate may still hold a reference for it.
@@ -277,22 +285,22 @@
     }
 
     @LayoutlibDelegate
-    /*package*/ static boolean nAddFont(long nativeFamily, ByteBuffer font, int ttcIndex) {
+    /*package*/ static boolean nAddFont(long builderPtr, ByteBuffer font, int ttcIndex) {
         assert false : "The only client of this method has been overriden.";
         return false;
     }
 
     @LayoutlibDelegate
-    /*package*/ static boolean nAddFontWeightStyle(long nativeFamily, ByteBuffer font,
+    /*package*/ static boolean nAddFontWeightStyle(long builderPtr, ByteBuffer font,
             int ttcIndex, List<FontListParser.Axis> listOfAxis,
             int weight, boolean isItalic) {
         assert false : "The only client of this method has been overriden.";
         return false;
     }
 
-    static boolean addFont(long nativeFamily, final String path, final int weight,
+    static boolean addFont(long builderPtr, final String path, final int weight,
             final boolean isItalic) {
-        final FontFamily_Delegate delegate = getDelegate(nativeFamily);
+        final FontFamily_Delegate delegate = getDelegate(builderPtr);
         if (delegate != null) {
             if (sFontLocation == null) {
                 delegate.mPostInitRunnables.add(() -> delegate.addFont(path, weight, isItalic));
@@ -304,8 +312,8 @@
     }
 
     @LayoutlibDelegate
-    /*package*/ static boolean nAddFontFromAsset(long nativeFamily, AssetManager mgr, String path) {
-        FontFamily_Delegate ffd = sManager.getDelegate(nativeFamily);
+    /*package*/ static boolean nAddFontFromAsset(long builderPtr, AssetManager mgr, String path) {
+        FontFamily_Delegate ffd = sManager.getDelegate(builderPtr);
         if (ffd == null) {
             return false;
         }
diff --git a/tools/layoutlib/bridge/src/android/graphics/Typeface_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/Typeface_Delegate.java
index 5cd34f6..a554b6d 100644
--- a/tools/layoutlib/bridge/src/android/graphics/Typeface_Delegate.java
+++ b/tools/layoutlib/bridge/src/android/graphics/Typeface_Delegate.java
@@ -212,9 +212,10 @@
             Map<String, ByteBuffer> bufferForPath) {
         FontFamily fontFamily = new FontFamily(family.lang, family.variant);
         for (FontListParser.Font font : family.fonts) {
-            FontFamily_Delegate.addFont(fontFamily.mNativePtr, font.fontName, font.weight,
+            FontFamily_Delegate.addFont(fontFamily.mBuilderPtr, font.fontName, font.weight,
                     font.isItalic);
         }
+        fontFamily.freeze();
         return fontFamily;
     }
 
diff --git a/tools/layoutlib/bridge/src/android/view/PointerIcon_Delegate.java b/tools/layoutlib/bridge/src/android/view/PointerIcon_Delegate.java
new file mode 100644
index 0000000..4a5ea9b
--- /dev/null
+++ b/tools/layoutlib/bridge/src/android/view/PointerIcon_Delegate.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view;
+
+import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
+
+import android.content.Context;
+import android.content.res.Resources;
+
+public class PointerIcon_Delegate {
+
+    @LayoutlibDelegate
+    /*package*/ static void loadResource(PointerIcon icon, Context context, Resources resources,
+            int resourceId) {
+        // HACK: This bypasses the problem of having an enum resolved as a resourceId.
+        // PointerIcon would not be displayed by layoutlib anyway, so we always return the null
+        // icon.
+    }
+}
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeRenderSession.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeRenderSession.java
index 3ac1889..77b131f 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeRenderSession.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeRenderSession.java
@@ -76,6 +76,23 @@
     }
 
     @Override
+    public Result measure(long timeout) {
+        try {
+            Bridge.prepareThread();
+            mLastResult = mSession.acquire(timeout);
+            if (mLastResult.isSuccess()) {
+                mSession.invalidateRenderingSize();
+                mLastResult = mSession.measure();
+            }
+        } finally {
+            mSession.release();
+            Bridge.cleanupThread();
+        }
+
+        return mLastResult;
+    }
+
+    @Override
     public Result render(long timeout, boolean forceMeasure) {
         try {
             Bridge.prepareThread();
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java
index 1b3b563..663e56d 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java
@@ -66,7 +66,6 @@
 import android.graphics.drawable.Drawable;
 import android.hardware.display.DisplayManager;
 import android.net.Uri;
-import android.os.Build.VERSION_CODES;
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.IBinder;
@@ -104,6 +103,7 @@
 import java.util.List;
 import java.util.Map;
 
+import static android.os.Build.VERSION_CODES.JELLY_BEAN_MR1;
 import static com.android.layoutlib.bridge.android.RenderParamsFlags.FLAG_KEY_APPLICATION_PACKAGE;
 
 /**
@@ -113,6 +113,28 @@
 public final class BridgeContext extends Context {
     private static final String PREFIX_THEME_APPCOMPAT = "Theme.AppCompat";
 
+    private static final Map<String, ResourceValue> FRAMEWORK_PATCHED_VALUES = new HashMap<>(2);
+    private static final Map<String, ResourceValue> FRAMEWORK_REPLACE_VALUES = new HashMap<>(3);
+
+    static {
+        FRAMEWORK_PATCHED_VALUES.put("animateFirstView", new ResourceValue(
+                ResourceType.BOOL, "animateFirstView", "false", false));
+        FRAMEWORK_PATCHED_VALUES.put("animateLayoutChanges",
+                new ResourceValue(ResourceType.BOOL, "animateLayoutChanges", "false", false));
+
+
+        FRAMEWORK_REPLACE_VALUES.put("textEditSuggestionItemLayout",
+                new ResourceValue(ResourceType.LAYOUT, "textEditSuggestionItemLayout",
+                        "text_edit_suggestion_item", true));
+        FRAMEWORK_REPLACE_VALUES.put("textEditSuggestionContainerLayout",
+                new ResourceValue(ResourceType.LAYOUT, "textEditSuggestionContainerLayout",
+                        "text_edit_suggestion_container", true));
+        FRAMEWORK_REPLACE_VALUES.put("textEditSuggestionHighlightStyle",
+                new ResourceValue(ResourceType.STYLE, "textEditSuggestionHighlightStyle",
+                        "TextAppearance.Holo.SuggestionHighlight", true));
+
+    }
+
     /** The map adds cookies to each view so that IDE can link xml tags to views. */
     private final HashMap<View, Object> mViewKeyMap = new HashMap<>();
     /**
@@ -312,7 +334,7 @@
      * Returns the current parser at the top the of the stack.
      * @return a parser or null.
      */
-    public BridgeXmlBlockParser getCurrentParser() {
+    private BridgeXmlBlockParser getCurrentParser() {
         return mParserStack.peek();
     }
 
@@ -406,7 +428,8 @@
     }
 
     public Pair<View, Boolean> inflateView(ResourceReference resource, ViewGroup parent,
-            boolean attachToRoot, boolean skipCallbackParser) {
+            @SuppressWarnings("SameParameterValue") boolean attachToRoot,
+            boolean skipCallbackParser) {
         boolean isPlatformLayout = resource.isFramework();
 
         if (!isPlatformLayout && !skipCallbackParser) {
@@ -711,11 +734,7 @@
 
             Object key = parser.getViewCookie();
             if (key != null) {
-                defaultPropMap = mDefaultPropMaps.get(key);
-                if (defaultPropMap == null) {
-                    defaultPropMap = new PropertiesMap();
-                    mDefaultPropMaps.put(key, defaultPropMap);
-                }
+                defaultPropMap = mDefaultPropMaps.computeIfAbsent(key, k -> new PropertiesMap());
             }
 
         } else if (set instanceof BridgeLayoutParamsMapAttributes) {
@@ -909,6 +928,16 @@
                 // if there's no direct value for this attribute in the XML, we look for default
                 // values in the widget defStyle, and then in the theme.
                 if (value == null) {
+                    if (frameworkAttr) {
+                        // For some framework values, layoutlib patches the actual value in the
+                        // theme when it helps to improve the final preview. In most cases
+                        // we just disable animations.
+                        ResourceValue patchedValue = FRAMEWORK_PATCHED_VALUES.get(attrName);
+                        if (patchedValue != null) {
+                            defaultValue = patchedValue;
+                        }
+                    }
+
                     // if we found a value, we make sure this doesn't reference another value.
                     // So we resolve it.
                     if (defaultValue != null) {
@@ -916,16 +945,21 @@
                         // exist, we should log a warning and omit it.
                         String val = defaultValue.getValue();
                         if (val != null && val.startsWith(SdkConstants.PREFIX_THEME_REF)) {
-                            if (!attrName.equals(RTL_ATTRS.get(val)) ||
-                                    getApplicationInfo().targetSdkVersion <
-                                            VERSION_CODES.JELLY_BEAN_MR1) {
+                            // Because we always use the latest framework code, some resources might
+                            // fail to resolve when using old themes (they haven't been backported).
+                            // Since this is an artifact caused by us using always the latest
+                            // code, we check for some of those values and replace them here.
+                            defaultValue = FRAMEWORK_REPLACE_VALUES.get(attrName);
+
+                            if (defaultValue == null &&
+                                    (getApplicationInfo().targetSdkVersion < JELLY_BEAN_MR1 ||
+                                    !attrName.equals(RTL_ATTRS.get(val)))) {
                                 // Only log a warning if the referenced value isn't one of the RTL
                                 // attributes, or the app targets old API.
                                 Bridge.getLog().warning(LayoutLog.TAG_RESOURCES_RESOLVE_THEME_ATTR,
                                         String.format("Failed to find '%s' in current theme.", val),
                                         val);
                             }
-                            defaultValue = null;
                         }
                     }
 
@@ -1944,7 +1978,7 @@
                 Map<List<StyleResourceValue>,
                         Map<Integer, Pair<BridgeTypedArray, PropertiesMap>>>> mCache;
 
-        public TypedArrayCache() {
+        private TypedArrayCache() {
             mCache = new IdentityHashMap<>();
         }
 
@@ -1965,17 +1999,9 @@
         public void put(int[] attrs, List<StyleResourceValue> themes, int resId,
                 Pair<BridgeTypedArray, PropertiesMap> value) {
             Map<List<StyleResourceValue>, Map<Integer, Pair<BridgeTypedArray, PropertiesMap>>>
-                    cacheFromThemes = mCache.get(attrs);
-            if (cacheFromThemes == null) {
-                cacheFromThemes = new HashMap<>();
-                mCache.put(attrs, cacheFromThemes);
-            }
+                    cacheFromThemes = mCache.computeIfAbsent(attrs, k -> new HashMap<>());
             Map<Integer, Pair<BridgeTypedArray, PropertiesMap>> cacheFromResId =
-                    cacheFromThemes.get(themes);
-            if (cacheFromResId == null) {
-                cacheFromResId = new HashMap<>();
-                cacheFromThemes.put(themes, cacheFromResId);
-            }
+                    cacheFromThemes.computeIfAbsent(themes, k -> new HashMap<>());
             cacheFromResId.put(resId, value);
         }
 
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/view/WindowManagerImpl.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/view/WindowManagerImpl.java
index 3031701..d7d16cf 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/view/WindowManagerImpl.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/view/WindowManagerImpl.java
@@ -17,6 +17,7 @@
 
 import android.util.DisplayMetrics;
 import android.view.Display;
+import android.view.Display.Mode;
 import android.view.DisplayAdjustments;
 import android.view.DisplayInfo;
 import android.view.View;
@@ -33,6 +34,9 @@
         DisplayInfo info = new DisplayInfo();
         info.logicalHeight = mMetrics.heightPixels;
         info.logicalWidth = mMetrics.widthPixels;
+        info.supportedModes = new Mode[] {
+                new Mode(0, mMetrics.widthPixels, mMetrics.heightPixels, 60f)
+        };
         mDisplay = new Display(null, Display.DEFAULT_DISPLAY, info,
                 DisplayAdjustments.DEFAULT_DISPLAY_ADJUSTMENTS);
     }
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderSessionImpl.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderSessionImpl.java
index 91a783a..85fe2a4 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderSessionImpl.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderSessionImpl.java
@@ -208,7 +208,7 @@
     /**
      * Measures the the current layout if needed (see {@link #invalidateRenderingSize}).
      */
-    private void measure(@NonNull SessionParams params) {
+    private void measureLayout(@NonNull SessionParams params) {
         // only do the screen measure when needed.
         if (mMeasuredScreenWidth != -1) {
             return;
@@ -353,7 +353,7 @@
 
             setActiveToolbar(view, context, params);
 
-            measure(params);
+            measureLayout(params);
             measureView(mViewRoot, null /*measuredView*/,
                     mMeasuredScreenWidth, MeasureSpec.EXACTLY,
                     mMeasuredScreenHeight, MeasureSpec.EXACTLY);
@@ -385,13 +385,10 @@
     }
 
     /**
-     * Renders the given view hierarchy to the passed canvas and returns the result of the render
-     * operation.
-     * @param canvas an optional canvas to render the views to. If null, only the measure and
-     * layout steps will be executed.
+     * Runs a layout pass for the given view root
      */
-    private static Result render(@NonNull BridgeContext context, @NonNull ViewGroup viewRoot,
-            @Nullable Canvas canvas, int width, int height) {
+    private static void doLayout(@NonNull BridgeContext context, @NonNull ViewGroup viewRoot,
+            int width, int height) {
         // measure again with the size we need
         // This must always be done before the call to layout
         measureView(viewRoot, null /*measuredView*/,
@@ -401,7 +398,16 @@
         // now do the layout.
         viewRoot.layout(0, 0, width, height);
         handleScrolling(context, viewRoot);
+    }
 
+    /**
+     * Renders the given view hierarchy to the passed canvas and returns the result of the render
+     * operation.
+     * @param canvas an optional canvas to render the views to. If null, only the measure and
+     * layout steps will be executed.
+     */
+    private static Result renderAndBuildResult(@NonNull BridgeContext context, @NonNull ViewGroup viewRoot,
+            @Nullable Canvas canvas, int width, int height) {
         if (canvas == null) {
             return SUCCESS.createResult();
         }
@@ -428,6 +434,40 @@
      * @see RenderSession#render(long)
      */
     public Result render(boolean freshRender) {
+        return renderAndBuildResult(freshRender, false);
+    }
+
+    /**
+     * Measures the layout
+     * <p>
+     * {@link #acquire(long)} must have been called before this.
+     *
+     * @throws IllegalStateException if the current context is different than the one owned by
+     *      the scene, or if {@link #acquire(long)} was not called.
+     *
+     * @see SessionParams#getRenderingMode()
+     * @see RenderSession#render(long)
+     */
+    public Result measure() {
+        return renderAndBuildResult(false, true);
+    }
+
+    /**
+     * Renders the scene.
+     * <p>
+     * {@link #acquire(long)} must have been called before this.
+     *
+     * @param freshRender whether the render is a new one and should erase the existing bitmap (in
+     *      the case where bitmaps are reused). This is typically needed when not playing
+     *      animations.)
+     *
+     * @throws IllegalStateException if the current context is different than the one owned by
+     *      the scene, or if {@link #acquire(long)} was not called.
+     *
+     * @see SessionParams#getRenderingMode()
+     * @see RenderSession#render(long)
+     */
+    private Result renderAndBuildResult(boolean freshRender, boolean onlyMeasure) {
         checkLock();
 
         SessionParams params = getParams();
@@ -437,14 +477,15 @@
                 return ERROR_NOT_INFLATED.createResult();
             }
 
-            measure(params);
+            measureLayout(params);
 
             HardwareConfig hardwareConfig = params.getHardwareConfig();
             Result renderResult = SUCCESS.createResult();
-            if (params.isLayoutOnly()) {
+            if (onlyMeasure) {
                 // delete the canvas and image to reset them on the next full rendering
                 mImage = null;
                 mCanvas = null;
+                doLayout(getContext(), mViewRoot, mMeasuredScreenWidth, mMeasuredScreenHeight);
             } else {
                 // draw the views
                 // create the BufferedImage into which the layout will be rendered.
@@ -505,11 +546,12 @@
                     gc.dispose();
                 }
 
+                doLayout(getContext(), mViewRoot, mMeasuredScreenWidth, mMeasuredScreenHeight);
                 if (mElapsedFrameTimeNanos >= 0) {
                     long initialTime = System_Delegate.nanoTime();
                     if (!mFirstFrameExecuted) {
                         // We need to run an initial draw call to initialize the animations
-                        render(getContext(), mViewRoot, NOP_CANVAS, mMeasuredScreenWidth, mMeasuredScreenHeight);
+                        renderAndBuildResult(getContext(), mViewRoot, NOP_CANVAS, mMeasuredScreenWidth, mMeasuredScreenHeight);
 
                         // The first frame will initialize the animations
                         Choreographer_Delegate.doFrame(initialTime);
@@ -518,7 +560,7 @@
                     // Second frame will move the animations
                     Choreographer_Delegate.doFrame(initialTime + mElapsedFrameTimeNanos);
                 }
-                renderResult = render(getContext(), mViewRoot, mCanvas, mMeasuredScreenWidth,
+                renderResult = renderAndBuildResult(getContext(), mViewRoot, mCanvas, mMeasuredScreenWidth,
                         mMeasuredScreenHeight);
             }
 
diff --git a/tools/layoutlib/bridge/src/libcore/util/NativeAllocationRegistry_Delegate.java b/tools/layoutlib/bridge/src/libcore/util/NativeAllocationRegistry_Delegate.java
index 6246ec1b8..04fabc2 100644
--- a/tools/layoutlib/bridge/src/libcore/util/NativeAllocationRegistry_Delegate.java
+++ b/tools/layoutlib/bridge/src/libcore/util/NativeAllocationRegistry_Delegate.java
@@ -52,9 +52,15 @@
 
     @LayoutlibDelegate
     /*package*/ static void applyFreeFunction(long freeFunction, long nativePtr) {
-        NativeAllocationRegistry_Delegate delegate = sManager.getDelegate(freeFunction);
-        if (delegate != null) {
-            delegate.mFinalizer.free(nativePtr);
+        // This method MIGHT run in the context of the finalizer thread. If the delegate method
+        // crashes, it could bring down the VM. That's why we catch all the exceptions and ignore
+        // them.
+        try {
+            NativeAllocationRegistry_Delegate delegate = sManager.getDelegate(freeFunction);
+            if (delegate != null) {
+                delegate.mFinalizer.free(nativePtr);
+            }
+        } catch (Throwable ignore) {
         }
     }
 
diff --git a/tools/layoutlib/bridge/tests/res/testApp/MyApplication/golden/simple_activity-old-theme.png b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/golden/simple_activity-old-theme.png
new file mode 100644
index 0000000..eb431b0
--- /dev/null
+++ b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/golden/simple_activity-old-theme.png
Binary files differ
diff --git a/tools/layoutlib/bridge/tests/res/testApp/MyApplication/src/main/res/layout/allwidgets.xml b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/src/main/res/layout/allwidgets.xml
index adb58a3..05a3665 100644
--- a/tools/layoutlib/bridge/tests/res/testApp/MyApplication/src/main/res/layout/allwidgets.xml
+++ b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/src/main/res/layout/allwidgets.xml
@@ -28,7 +28,8 @@
         android:layout_alignParentStart="true"
         android:layout_below="@id/frameLayout"
         android:text="Large Text"
-        android:textAppearance="?android:attr/textAppearanceLarge" />
+        android:textAppearance="?android:attr/textAppearanceLarge"
+        android:pointerIcon="hand" />
 
     <TextView
         android:id="@id/textView3"
diff --git a/tools/layoutlib/bridge/tests/res/testApp/MyApplication/src/main/res/values/arrays.xml b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/src/main/res/values/arrays.xml
index f6e14d2..5f58d39 100644
--- a/tools/layoutlib/bridge/tests/res/testApp/MyApplication/src/main/res/values/arrays.xml
+++ b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/src/main/res/values/arrays.xml
@@ -17,6 +17,7 @@
         <!-- theme ref in android NS. value = @string/candidates_style = <u>candidates</u> -->
         <item>?android:attr/candidatesTextStyleSpans</item>
         <item>@android:string/unknownName</item>  <!-- value = Unknown -->
+        <item>?EC</item>
     </string-array>
 
     <!-- resources that the above array can refer to -->
diff --git a/tools/layoutlib/bridge/tests/res/testApp/MyApplication/src/main/res/values/styles.xml b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/src/main/res/values/styles.xml
index c8a5fec..debe33b 100644
--- a/tools/layoutlib/bridge/tests/res/testApp/MyApplication/src/main/res/values/styles.xml
+++ b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/src/main/res/values/styles.xml
@@ -3,7 +3,6 @@
     <!-- Base application theme. -->
     <style name="AppTheme" parent="android:Theme.Material.Light.DarkActionBar">
         <item name="myattr">@integer/ten</item>
-        <item name="android:animateFirstView">false</item>
     </style>
 
 </resources>
diff --git a/tools/layoutlib/bridge/tests/src/com/android/layoutlib/bridge/intensive/Main.java b/tools/layoutlib/bridge/tests/src/com/android/layoutlib/bridge/intensive/Main.java
index cdcae89..d0c04d7 100644
--- a/tools/layoutlib/bridge/tests/src/com/android/layoutlib/bridge/intensive/Main.java
+++ b/tools/layoutlib/bridge/tests/src/com/android/layoutlib/bridge/intensive/Main.java
@@ -69,6 +69,7 @@
 
 import com.google.android.collect.Lists;
 
+import static org.junit.Assert.assertArrayEquals;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNotNull;
@@ -313,6 +314,12 @@
         sBridge = new Bridge();
         sBridge.init(ConfigGenerator.loadProperties(buildProp), fontLocation,
                 ConfigGenerator.getEnumMap(attrs), getLayoutLog());
+        Bridge.getLock().lock();
+        try {
+            Bridge.setLog(getLayoutLog());
+        } finally {
+            Bridge.getLock().unlock();
+        }
     }
 
     @Before
@@ -327,6 +334,20 @@
     }
 
     @Test
+    public void testActivityOnOldTheme() throws ClassNotFoundException {
+        LayoutLibTestCallback layoutLibCallback =
+                new LayoutLibTestCallback(getLogger(), mDefaultClassLoader);
+        layoutLibCallback.initResources();
+
+        LayoutPullParser parser = createLayoutPullParser("simple_activity.xml");
+        SessionParams params = getSessionParams(parser, ConfigGenerator.NEXUS_5,
+                layoutLibCallback, "Theme.NoTitleBar", false,
+                RenderingMode.NORMAL, 22);
+
+        renderAndVerify(params, "simple_activity-old-theme.png");
+    }
+
+    @Test
     public void testTranslucentBars() throws ClassNotFoundException {
         LayoutLibTestCallback layoutLibCallback =
                 new LayoutLibTestCallback(getLogger(), mDefaultClassLoader);
@@ -555,7 +576,7 @@
 
     /** Test activity.xml */
     @Test
-    public void testScrolling() throws ClassNotFoundException {
+    public void testScrollingAndMeasure() throws ClassNotFoundException {
         // Create the layout pull parser.
         LayoutPullParser parser = createLayoutPullParser("scrolled.xml");
         // Create LayoutLibCallback.
@@ -569,7 +590,10 @@
         params.setForceNoDecor();
         params.setExtendedViewInfoMode(true);
 
-        RenderResult result = renderAndVerify(params, "scrolled.png");
+        // Do an only-measure pass
+        RenderSession session = sBridge.createSession(params);
+        session.measure();
+        RenderResult result = RenderResult.getFromSession(session);
         assertNotNull(result);
         assertNotNull(result.getResult());
         assertTrue(result.getResult().isSuccess());
@@ -586,6 +610,20 @@
         assertEquals(90, rootLayout.getChildren().get(5).getChildren().get(0).getLeft());
         assertEquals(-270, rootLayout.getChildren().get(5).getChildren().get(0).getBottom());
         assertEquals(690, rootLayout.getChildren().get(5).getChildren().get(0).getRight());
+
+        // Do a full render pass
+        parser = createLayoutPullParser("scrolled.xml");
+
+        params = getSessionParams(parser, ConfigGenerator.NEXUS_5,
+                layoutLibCallback, "Theme.Material.NoActionBar.Fullscreen", false,
+                RenderingMode.V_SCROLL, 22);
+        params.setForceNoDecor();
+        params.setExtendedViewInfoMode(true);
+
+        result = renderAndVerify(params, "scrolled.png");
+        assertNotNull(result);
+        assertNotNull(result.getResult());
+        assertTrue(result.getResult().isSuccess());
     }
 
     @Test
@@ -624,6 +662,36 @@
         assertEquals("app_name", resources.getResourceEntryName(id));
     }
 
+    @Test
+    public void testStringEscaping() throws Exception {
+        // Setup
+        // Create the layout pull parser for our resources (empty.xml can not be part of the test
+        // app as it won't compile).
+        LayoutPullParser parser = new LayoutPullParser("/empty.xml");
+        // Create LayoutLibCallback.
+        LayoutLibTestCallback layoutLibCallback =
+                new LayoutLibTestCallback(getLogger(), mDefaultClassLoader);
+        layoutLibCallback.initResources();
+        SessionParams params = getSessionParams(parser, ConfigGenerator.NEXUS_4,
+                layoutLibCallback, "AppTheme", true, RenderingMode.NORMAL, 22);
+        AssetManager assetManager = AssetManager.getSystem();
+        DisplayMetrics metrics = new DisplayMetrics();
+        Configuration configuration = RenderAction.getConfiguration(params);
+        Resources resources = new Resources(assetManager, metrics, configuration);
+        resources.mLayoutlibCallback = params.getLayoutlibCallback();
+        resources.mContext =
+                new BridgeContext(params.getProjectKey(), metrics, params.getResources(),
+                        params.getAssets(), params.getLayoutlibCallback(), configuration,
+                        params.getTargetSdkVersion(), params.isRtlSupported());
+
+        int id = resources.mLayoutlibCallback.getResourceId(ResourceType.ARRAY, "string_array");
+        String[] strings = resources.getStringArray(id);
+        assertArrayEquals(
+                new String[]{"mystring", "Hello world!", "candidates", "Unknown", "?EC"},
+                strings);
+        assertTrue(sRenderMessages.isEmpty());
+    }
+
     @NonNull
     private LayoutPullParser createLayoutPullParser(String layoutPath) {
         return new LayoutPullParser(APP_TEST_RES + "/layout/" + layoutPath);
diff --git a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/CreateInfo.java b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/CreateInfo.java
index 92fd75c..741eb27 100644
--- a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/CreateInfo.java
+++ b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/CreateInfo.java
@@ -154,6 +154,8 @@
         "android.content.res.Resources#getIntArray",
         "android.content.res.Resources#getInteger",
         "android.content.res.Resources#getLayout",
+        "android.content.res.Resources#getQuantityString",
+        "android.content.res.Resources#getQuantityText",
         "android.content.res.Resources#getResourceEntryName",
         "android.content.res.Resources#getResourceName",
         "android.content.res.Resources#getResourcePackageName",
@@ -232,6 +234,7 @@
         "android.view.RenderNode#nSetScaleY",
         "android.view.RenderNode#nGetScaleY",
         "android.view.RenderNode#nIsPivotExplicitlySet",
+        "android.view.PointerIcon#loadResource",
         "android.view.ViewGroup#drawChild",
         "com.android.internal.view.menu.MenuBuilder#createNewMenuItem",
         "com.android.internal.util.XmlUtils#convertValueToInt",
@@ -336,7 +339,8 @@
      */
     private final static String[] PROMOTED_FIELDS = new String[] {
         "android.graphics.drawable.VectorDrawable#mVectorState",
-        "android.view.Choreographer#mLastFrameTimeNanos"
+        "android.view.Choreographer#mLastFrameTimeNanos",
+        "android.graphics.FontFamily#mBuilderPtr"
     };
 
     /**