Import Android SDK Platform P [4719250]

/google/data/ro/projects/android/fetch_artifact \
    --bid 4719250 \
    --target sdk_phone_armv7-win_sdk \
    sdk-repo-linux-sources-4719250.zip

AndroidVersion.ApiLevel has been modified to appear as 28

Change-Id: I9ec0a12c9251b8449dba0d86b0cfdbcca16b0a7c
diff --git a/android/accessibilityservice/AccessibilityService.java b/android/accessibilityservice/AccessibilityService.java
index 0a4541b..829a944 100644
--- a/android/accessibilityservice/AccessibilityService.java
+++ b/android/accessibilityservice/AccessibilityService.java
@@ -537,7 +537,7 @@
      * anything behind it, then only the modal window will be reported
      * (assuming it is the top one). For convenience the returned windows
      * are ordered in a descending layer order, which is the windows that
-     * are higher in the Z-order are reported first. Since the user can always
+     * are on top are reported first. Since the user can always
      * interact with the window that has input focus by typing, the focused
      * window is always returned (even if covered by a modal window).
      * <p>
diff --git a/android/app/ActivityOptions.java b/android/app/ActivityOptions.java
index 09dcbf2..ecd99a7 100644
--- a/android/app/ActivityOptions.java
+++ b/android/app/ActivityOptions.java
@@ -1139,7 +1139,8 @@
      * {@link android.app.admin.DevicePolicyManager} can run in LockTask mode. Therefore, if
      * {@link android.app.admin.DevicePolicyManager#isLockTaskPermitted(String)} returns
      * {@code false} for the package of the target activity, a {@link SecurityException} will be
-     * thrown during {@link Context#startActivity(Intent, Bundle)}.
+     * thrown during {@link Context#startActivity(Intent, Bundle)}. This method doesn't affect
+     * activities that are already running — relaunch the activity to run in lock task mode.
      *
      * Defaults to {@code false} if not set.
      *
diff --git a/android/app/ActivityThread.java b/android/app/ActivityThread.java
index 50a4398..82c3383 100644
--- a/android/app/ActivityThread.java
+++ b/android/app/ActivityThread.java
@@ -5873,7 +5873,7 @@
         } finally {
             // If the app targets < O-MR1, or doesn't change the thread policy
             // during startup, clobber the policy to maintain behavior of b/36951662
-            if (data.appInfo.targetSdkVersion <= Build.VERSION_CODES.O
+            if (data.appInfo.targetSdkVersion < Build.VERSION_CODES.O_MR1
                     || StrictMode.getThreadPolicy().equals(writesAllowedPolicy)) {
                 StrictMode.setThreadPolicy(savedPolicy);
             }
diff --git a/android/app/ApplicationPackageManager.java b/android/app/ApplicationPackageManager.java
index a68136b..1084b42 100644
--- a/android/app/ApplicationPackageManager.java
+++ b/android/app/ApplicationPackageManager.java
@@ -2155,19 +2155,10 @@
     public String[] setPackagesSuspended(String[] packageNames, boolean suspended,
             PersistableBundle appExtras, PersistableBundle launcherExtras,
             String dialogMessage) {
-        // TODO (b/75332201): Pass in the dialogMessage and use it in the interceptor dialog
         try {
             return mPM.setPackagesSuspendedAsUser(packageNames, suspended, appExtras,
-                    launcherExtras, mContext.getOpPackageName(), mContext.getUserId());
-        } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
-        }
-    }
-
-    @Override
-    public PersistableBundle getSuspendedPackageAppExtras(String packageName) {
-        try {
-            return mPM.getSuspendedPackageAppExtras(packageName, mContext.getUserId());
+                    launcherExtras, dialogMessage, mContext.getOpPackageName(),
+                    mContext.getUserId());
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -2175,17 +2166,14 @@
 
     @Override
     public Bundle getSuspendedPackageAppExtras() {
-        final PersistableBundle extras = getSuspendedPackageAppExtras(mContext.getOpPackageName());
-        return extras != null ? new Bundle(extras.deepCopy()) : null;
-    }
-
-    @Override
-    public void setSuspendedPackageAppExtras(String packageName, PersistableBundle appExtras) {
+        final PersistableBundle extras;
         try {
-            mPM.setSuspendedPackageAppExtras(packageName, appExtras, mContext.getUserId());
+            extras = mPM.getSuspendedPackageAppExtras(mContext.getOpPackageName(),
+                    mContext.getUserId());
         } catch (RemoteException e) {
-            e.rethrowFromSystemServer();
+            throw e.rethrowFromSystemServer();
         }
+        return extras != null ? new Bundle(extras.deepCopy()) : null;
     }
 
     @Override
@@ -2199,8 +2187,12 @@
 
     /** @hide */
     @Override
-    public boolean isPackageSuspended(String packageName) {
-        return isPackageSuspendedForUser(packageName, mContext.getUserId());
+    public boolean isPackageSuspended(String packageName) throws NameNotFoundException {
+        try {
+            return isPackageSuspendedForUser(packageName, mContext.getUserId());
+        } catch (IllegalArgumentException ie) {
+            throw new NameNotFoundException(packageName);
+        }
     }
 
     @Override
diff --git a/android/app/BroadcastOptions.java b/android/app/BroadcastOptions.java
index b6cff38..69c3632 100644
--- a/android/app/BroadcastOptions.java
+++ b/android/app/BroadcastOptions.java
@@ -32,6 +32,7 @@
     private long mTemporaryAppWhitelistDuration;
     private int mMinManifestReceiverApiLevel = 0;
     private int mMaxManifestReceiverApiLevel = Build.VERSION_CODES.CUR_DEVELOPMENT;
+    private boolean mDontSendToRestrictedApps = false;
 
     /**
      * How long to temporarily put an app on the power whitelist when executing this broadcast
@@ -52,6 +53,12 @@
     static final String KEY_MAX_MANIFEST_RECEIVER_API_LEVEL
             = "android:broadcast.maxManifestReceiverApiLevel";
 
+    /**
+     * Corresponds to {@link #setMaxManifestReceiverApiLevel}.
+     */
+    static final String KEY_DONT_SEND_TO_RESTRICTED_APPS =
+            "android:broadcast.dontSendToRestrictedApps";
+
     public static BroadcastOptions makeBasic() {
         BroadcastOptions opts = new BroadcastOptions();
         return opts;
@@ -66,6 +73,7 @@
         mMinManifestReceiverApiLevel = opts.getInt(KEY_MIN_MANIFEST_RECEIVER_API_LEVEL, 0);
         mMaxManifestReceiverApiLevel = opts.getInt(KEY_MAX_MANIFEST_RECEIVER_API_LEVEL,
                 Build.VERSION_CODES.CUR_DEVELOPMENT);
+        mDontSendToRestrictedApps = opts.getBoolean(KEY_DONT_SEND_TO_RESTRICTED_APPS, false);
     }
 
     /**
@@ -123,6 +131,23 @@
     }
 
     /**
+     * Sets whether pending intent can be sent for an application with background restrictions
+     * @param dontSendToRestrictedApps if true, pending intent will not be sent for an application
+     * with background restrictions. Default value is {@code false}
+     */
+    public void setDontSendToRestrictedApps(boolean dontSendToRestrictedApps) {
+        mDontSendToRestrictedApps = dontSendToRestrictedApps;
+    }
+
+    /**
+     * @hide
+     * @return #setDontSendToRestrictedApps
+     */
+    public boolean isDontSendToRestrictedApps() {
+        return mDontSendToRestrictedApps;
+    }
+
+    /**
      * Returns the created options as a Bundle, which can be passed to
      * {@link android.content.Context#sendBroadcast(android.content.Intent)
      * Context.sendBroadcast(Intent)} and related methods.
@@ -141,6 +166,9 @@
         if (mMaxManifestReceiverApiLevel != Build.VERSION_CODES.CUR_DEVELOPMENT) {
             b.putInt(KEY_MAX_MANIFEST_RECEIVER_API_LEVEL, mMaxManifestReceiverApiLevel);
         }
+        if (mDontSendToRestrictedApps) {
+            b.putBoolean(KEY_DONT_SEND_TO_RESTRICTED_APPS, true);
+        }
         return b.isEmpty() ? null : b;
     }
 }
diff --git a/android/app/ContextImpl.java b/android/app/ContextImpl.java
index 71b88fa..9a491bc 100644
--- a/android/app/ContextImpl.java
+++ b/android/app/ContextImpl.java
@@ -16,6 +16,7 @@
 
 package android.app;
 
+import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.TestApi;
@@ -88,11 +89,12 @@
 import java.io.FilenameFilter;
 import java.io.IOException;
 import java.io.InputStream;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
 import java.nio.ByteOrder;
 import java.util.ArrayList;
 import java.util.Objects;
 import java.util.concurrent.Executor;
-import java.util.concurrent.atomic.AtomicInteger;
 
 class ReceiverRestrictedContext extends ContextWrapper {
     ReceiverRestrictedContext(Context base) {
@@ -212,13 +214,24 @@
     static final int STATE_UNINITIALIZED = 0;
     static final int STATE_INITIALIZING = 1;
     static final int STATE_READY = 2;
+    static final int STATE_NOT_FOUND = 3;
+
+    /** @hide */
+    @IntDef(prefix = { "STATE_" }, value = {
+            STATE_UNINITIALIZED,
+            STATE_INITIALIZING,
+            STATE_READY,
+            STATE_NOT_FOUND,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    @interface ServiceInitializationState {}
 
     /**
      * Initialization state for each service. Any of {@link #STATE_UNINITIALIZED},
      * {@link #STATE_INITIALIZING} or {@link #STATE_READY},
      */
-    final AtomicInteger[] mServiceInitializationStateArray =
-            SystemServiceRegistry.createServiceInitializationStateArray();
+    @ServiceInitializationState
+    final int[] mServiceInitializationStateArray = new int[mServiceCache.length];
 
     static ContextImpl getImpl(Context context) {
         Context nextContext;
diff --git a/android/app/Notification.java b/android/app/Notification.java
index 4326ee3..2b4f420 100644
--- a/android/app/Notification.java
+++ b/android/app/Notification.java
@@ -3902,7 +3902,7 @@
          * @deprecated use {@link #addPerson(Person)}
          */
         public Builder addPerson(String uri) {
-            addPerson(new Person().setUri(uri));
+            addPerson(new Person.Builder().setUri(uri).build());
             return this;
         }
 
@@ -4588,10 +4588,18 @@
                 bindHeaderChronometerAndTime(contentView);
                 bindProfileBadge(contentView);
             }
+            bindActivePermissions(contentView);
             bindExpandButton(contentView);
             mN.mUsesStandardHeader = true;
         }
 
+        private void bindActivePermissions(RemoteViews contentView) {
+            int color = isColorized() ? getPrimaryTextColor() : getSecondaryTextColor();
+            contentView.setDrawableTint(R.id.camera, false, color, PorterDuff.Mode.SRC_ATOP);
+            contentView.setDrawableTint(R.id.mic, false, color, PorterDuff.Mode.SRC_ATOP);
+            contentView.setDrawableTint(R.id.overlay, false, color, PorterDuff.Mode.SRC_ATOP);
+        }
+
         private void bindExpandButton(RemoteViews contentView) {
             int color = isColorized() ? getPrimaryTextColor() : getSecondaryTextColor();
             contentView.setDrawableTint(R.id.expand_button, false, color,
@@ -6384,7 +6392,7 @@
          * @deprecated use {@code MessagingStyle(Person)}
          */
         public MessagingStyle(@NonNull CharSequence userDisplayName) {
-            this(new Person().setName(userDisplayName));
+            this(new Person.Builder().setName(userDisplayName).build());
         }
 
         /**
@@ -6431,6 +6439,7 @@
         /**
          * @return the user to be displayed for any replies sent by the user
          */
+        @NonNull
         public Person getUser() {
             return mUser;
         }
@@ -6489,7 +6498,7 @@
          */
         public MessagingStyle addMessage(CharSequence text, long timestamp, CharSequence sender) {
             return addMessage(text, timestamp,
-                    sender == null ? null : new Person().setName(sender));
+                    sender == null ? null : new Person.Builder().setName(sender).build());
         }
 
         /**
@@ -6505,7 +6514,8 @@
          *
          * @return this object for method chaining
          */
-        public MessagingStyle addMessage(CharSequence text, long timestamp, Person sender) {
+        public MessagingStyle addMessage(@NonNull CharSequence text, long timestamp,
+                @Nullable Person sender) {
             return addMessage(new Message(text, timestamp, sender));
         }
 
@@ -6661,7 +6671,7 @@
             mUser = extras.getParcelable(EXTRA_MESSAGING_PERSON);
             if (mUser == null) {
                 CharSequence displayName = extras.getCharSequence(EXTRA_SELF_DISPLAY_NAME);
-                mUser = new Person().setName(displayName);
+                mUser = new Person.Builder().setName(displayName).build();
             }
             mConversationTitle = extras.getCharSequence(EXTRA_CONVERSATION_TITLE);
             Parcelable[] messages = extras.getParcelableArray(EXTRA_MESSAGES);
@@ -6678,7 +6688,8 @@
         public RemoteViews makeContentView(boolean increasedHeight) {
             mBuilder.mOriginalActions = mBuilder.mActions;
             mBuilder.mActions = new ArrayList<>();
-            RemoteViews remoteViews = makeMessagingView(true /* isCollapsed */);
+            RemoteViews remoteViews = makeMessagingView(true /* displayImagesAtEnd */,
+                    true /* showReplyIcon */);
             mBuilder.mActions = mBuilder.mOriginalActions;
             mBuilder.mOriginalActions = null;
             return remoteViews;
@@ -6765,11 +6776,19 @@
          */
         @Override
         public RemoteViews makeBigContentView() {
-            return makeMessagingView(false /* isCollapsed */);
+            return makeMessagingView(false /* displayImagesAtEnd */, false /* showReplyIcon */);
         }
 
+        /**
+         * Create a messaging layout.
+         *
+         * @param displayImagesAtEnd should images be displayed at the end of the content instead
+         *                           of inline.
+         * @param showReplyIcon Should the reply affordance be shown at the end of the notification
+         * @return the created remoteView.
+         */
         @NonNull
-        private RemoteViews makeMessagingView(boolean isCollapsed) {
+        private RemoteViews makeMessagingView(boolean displayImagesAtEnd, boolean showReplyIcon) {
             CharSequence conversationTitle = !TextUtils.isEmpty(super.mBigContentTitle)
                     ? super.mBigContentTitle
                     : mConversationTitle;
@@ -6780,24 +6799,24 @@
                 nameReplacement = conversationTitle;
                 conversationTitle = null;
             }
-            boolean hideLargeIcon = !isCollapsed || isOneToOne;
+            boolean hideLargeIcon = !showReplyIcon || isOneToOne;
             RemoteViews contentView = mBuilder.applyStandardTemplateWithActions(
                     mBuilder.getMessagingLayoutResource(),
                     mBuilder.mParams.reset().hasProgress(false).title(conversationTitle).text(null)
                             .hideLargeIcon(hideLargeIcon)
                             .headerTextSecondary(conversationTitle)
-                            .alwaysShowReply(isCollapsed));
+                            .alwaysShowReply(showReplyIcon));
             addExtras(mBuilder.mN.extras);
             // also update the end margin if there is an image
             int endMargin = R.dimen.notification_content_margin_end;
-            if (isCollapsed) {
+            if (showReplyIcon) {
                 endMargin = R.dimen.notification_content_plus_picture_margin_end;
             }
             contentView.setViewLayoutMarginEndDimen(R.id.notification_main_column, endMargin);
             contentView.setInt(R.id.status_bar_latest_event_content, "setLayoutColor",
                     mBuilder.resolveContrastColor());
-            contentView.setBoolean(R.id.status_bar_latest_event_content, "setIsCollapsed",
-                    isCollapsed);
+            contentView.setBoolean(R.id.status_bar_latest_event_content, "setDisplayImagesAtEnd",
+                    displayImagesAtEnd);
             contentView.setIcon(R.id.status_bar_latest_event_content, "setLargeIcon",
                     mBuilder.mN.mLargeIcon);
             contentView.setCharSequence(R.id.status_bar_latest_event_content, "setNameReplacement",
@@ -6864,7 +6883,8 @@
          */
         @Override
         public RemoteViews makeHeadsUpContentView(boolean increasedHeight) {
-            RemoteViews remoteViews = makeMessagingView(true /* isCollapsed */);
+            RemoteViews remoteViews = makeMessagingView(true /* displayImagesAtEnd */,
+                    false /* showReplyIcon */);
             remoteViews.setInt(R.id.notification_messaging, "setMaxDisplayedLines", 1);
             return remoteViews;
         }
@@ -6906,7 +6926,8 @@
              *  @deprecated use {@code Message(CharSequence, long, Person)}
              */
             public Message(CharSequence text, long timestamp, CharSequence sender){
-                this(text, timestamp, sender == null ? null : new Person().setName(sender));
+                this(text, timestamp, sender == null ? null
+                        : new Person.Builder().setName(sender).build());
             }
 
             /**
@@ -6917,13 +6938,14 @@
              * Should be <code>null</code> for messages by the current user, in which case
              * the platform will insert the user set in {@code MessagingStyle(Person)}.
              * <p>
-             * The person provided should contain an Icon, set with {@link Person#setIcon(Icon)}
-             * and also have a name provided with {@link Person#setName(CharSequence)}. If multiple
-             * users have the same name, consider providing a key with {@link Person#setKey(String)}
-             * in order to differentiate between the different users.
+             * The person provided should contain an Icon, set with
+             * {@link Person.Builder#setIcon(Icon)} and also have a name provided
+             * with {@link Person.Builder#setName(CharSequence)}. If multiple users have the same
+             * name, consider providing a key with {@link Person.Builder#setKey(String)} in order
+             * to differentiate between the different users.
              * </p>
              */
-            public Message(CharSequence text, long timestamp, @Nullable Person sender){
+            public Message(@NonNull CharSequence text, long timestamp, @Nullable Person sender) {
                 mText = text;
                 mTimestamp = timestamp;
                 mSender = sender;
@@ -7082,7 +7104,7 @@
                             // the native api instead
                             CharSequence senderName = bundle.getCharSequence(KEY_SENDER);
                             if (senderName != null) {
-                                senderPerson = new Person().setName(senderName);
+                                senderPerson = new Person.Builder().setName(senderName).build();
                             }
                         }
                         Message message = new Message(bundle.getCharSequence(KEY_TEXT),
@@ -7777,217 +7799,6 @@
         }
     }
 
-    /**
-     * A Person associated with this Notification.
-     */
-    public static final class Person implements Parcelable {
-        @Nullable private CharSequence mName;
-        @Nullable private Icon mIcon;
-        @Nullable private String mUri;
-        @Nullable private String mKey;
-        private boolean mBot;
-        private boolean mImportant;
-
-        protected Person(Parcel in) {
-            mName = in.readCharSequence();
-            if (in.readInt() != 0) {
-                mIcon = Icon.CREATOR.createFromParcel(in);
-            }
-            mUri = in.readString();
-            mKey = in.readString();
-            mImportant = in.readBoolean();
-            mBot = in.readBoolean();
-        }
-
-        /**
-         * Create a new person.
-         */
-        public Person() {
-        }
-
-        /**
-         * Give this person a name.
-         *
-         * @param name the name of this person
-         */
-        public Person setName(@Nullable CharSequence name) {
-            this.mName = name;
-            return this;
-        }
-
-        /**
-         * Add an icon for this person.
-         * <br />
-         * This is currently only used for {@link MessagingStyle} notifications and should not be
-         * provided otherwise, in order to save memory. The system will prefer this icon over any
-         * images that are resolved from the URI.
-         *
-         * @param icon the icon of the person
-         */
-        public Person setIcon(@Nullable Icon icon) {
-            this.mIcon = icon;
-            return this;
-        }
-
-        /**
-         * Set a URI associated with this person.
-         *
-         * <P>
-         * Depending on user preferences, adding a URI to a Person may allow the notification to
-         * pass through interruption filters, if this notification is of
-         * category {@link #CATEGORY_CALL} or {@link #CATEGORY_MESSAGE}.
-         * The addition of people may also cause this notification to appear more prominently in
-         * the user interface.
-         * </P>
-         *
-         * <P>
-         * The person should be specified by the {@code String} representation of a
-         * {@link android.provider.ContactsContract.Contacts#CONTENT_LOOKUP_URI}.
-         * </P>
-         *
-         * <P>The system will also attempt to resolve {@code mailto:} and {@code tel:} schema
-         * URIs.  The path part of these URIs must exist in the contacts database, in the
-         * appropriate column, or the reference will be discarded as invalid. Telephone schema
-         * URIs will be resolved by {@link android.provider.ContactsContract.PhoneLookup}.
-         * </P>
-         *
-         * @param uri a URI for the person
-         */
-        public Person setUri(@Nullable String uri) {
-            mUri = uri;
-            return this;
-        }
-
-        /**
-         * Add a key to this person in order to uniquely identify it.
-         * This is especially useful if the name doesn't uniquely identify this person or if the
-         * display name is a short handle of the actual name.
-         *
-         * <P>If no key is provided, the name serves as as the key for the purpose of
-         * identification.</P>
-         *
-         * @param key the key that uniquely identifies this person
-         */
-        public Person setKey(@Nullable String key) {
-            mKey = key;
-            return this;
-        }
-
-        /**
-         * Sets whether this is an important person. Use this method to denote users who frequently
-         * interact with the user of this device, when it is not possible to refer to the user
-         * by {@link android.provider.ContactsContract.Contacts#CONTENT_LOOKUP_URI}.
-         *
-         * @param isImportant {@code true} if this is an important person, {@code false} otherwise.
-         */
-        public Person setImportant(boolean isImportant) {
-            mImportant = isImportant;
-            return this;
-        }
-
-        /**
-         * Sets whether this person is a machine rather than a human.
-         *
-         * @param isBot {@code true}  if this person is a machine, {@code false} otherwise.
-         */
-        public Person setBot(boolean isBot) {
-            mBot = isBot;
-            return this;
-        }
-
-        /**
-         * @return the uri provided for this person or {@code null} if no Uri was provided
-         */
-        @Nullable
-        public String getUri() {
-            return mUri;
-        }
-
-        /**
-         * @return the name provided for this person or {@code null} if no name was provided
-         */
-        @Nullable
-        public CharSequence getName() {
-            return mName;
-        }
-
-        /**
-         * @return the icon provided for this person or {@code null} if no icon was provided
-         */
-        @Nullable
-        public Icon getIcon() {
-            return mIcon;
-        }
-
-        /**
-         * @return the key provided for this person or {@code null} if no key was provided
-         */
-        @Nullable
-        public String getKey() {
-            return mKey;
-        }
-
-        /**
-         * @return whether this Person is a machine.
-         */
-        public boolean isBot() {
-            return mBot;
-        }
-
-        /**
-         * @return whether this Person is important.
-         */
-        public boolean isImportant() {
-            return mImportant;
-        }
-
-        /**
-         * @return the URI associated with this person, or "name:mName" otherwise
-         *  @hide
-         */
-        public String resolveToLegacyUri() {
-            if (mUri != null) {
-                return mUri;
-            }
-            if (mName != null) {
-                return "name:" + mName;
-            }
-            return "";
-        }
-
-        @Override
-        public int describeContents() {
-            return 0;
-        }
-
-        @Override
-        public void writeToParcel(Parcel dest, @WriteFlags int flags) {
-            dest.writeCharSequence(mName);
-            if (mIcon != null) {
-                dest.writeInt(1);
-                mIcon.writeToParcel(dest, 0);
-            } else {
-                dest.writeInt(0);
-            }
-            dest.writeString(mUri);
-            dest.writeString(mKey);
-            dest.writeBoolean(mImportant);
-            dest.writeBoolean(mBot);
-        }
-
-        public static final Creator<Person> CREATOR = new Creator<Person>() {
-            @Override
-            public Person createFromParcel(Parcel in) {
-                return new Person(in);
-            }
-
-            @Override
-            public Person[] newArray(int size) {
-                return new Person[size];
-            }
-        };
-    }
-
     // When adding a new Style subclass here, don't forget to update
     // Builder.getNotificationStyleClass.
 
diff --git a/android/app/NotificationChannel.java b/android/app/NotificationChannel.java
index 4a7cf62..9e47ced 100644
--- a/android/app/NotificationChannel.java
+++ b/android/app/NotificationChannel.java
@@ -328,7 +328,8 @@
      * Group information is only used for presentation, not for behavior.
      *
      * Only modifiable before the channel is submitted to
-     * {@link NotificationManager#notify(String, int, Notification)}.
+     * {@link NotificationManager#createNotificationChannel(NotificationChannel)}, unless the
+     * channel is not currently part of a group.
      *
      * @param groupId the id of a group created by
      * {@link NotificationManager#createNotificationChannelGroup(NotificationChannelGroup)}.
@@ -341,6 +342,9 @@
      * Sets whether notifications posted to this channel can appear as application icon badges
      * in a Launcher.
      *
+     * Only modifiable before the channel is submitted to
+     * {@link NotificationManager#createNotificationChannel(NotificationChannel)}.
+     *
      * @param showBadge true if badges should be allowed to be shown.
      */
     public void setShowBadge(boolean showBadge) {
@@ -353,7 +357,7 @@
      * least {@link NotificationManager#IMPORTANCE_DEFAULT} should have a sound.
      *
      * Only modifiable before the channel is submitted to
-     * {@link NotificationManager#notify(String, int, Notification)}.
+     * {@link NotificationManager#createNotificationChannel(NotificationChannel)}.
      */
     public void setSound(Uri sound, AudioAttributes audioAttributes) {
         this.mSound = sound;
@@ -365,7 +369,7 @@
      * on devices that support that feature.
      *
      * Only modifiable before the channel is submitted to
-     * {@link NotificationManager#notify(String, int, Notification)}.
+     * {@link NotificationManager#createNotificationChannel(NotificationChannel)}.
      */
     public void enableLights(boolean lights) {
         this.mLights = lights;
@@ -376,7 +380,7 @@
      * {@link #enableLights(boolean) enabled} on this channel and the device supports that feature.
      *
      * Only modifiable before the channel is submitted to
-     * {@link NotificationManager#notify(String, int, Notification)}.
+     * {@link NotificationManager#createNotificationChannel(NotificationChannel)}.
      */
     public void setLightColor(int argb) {
         this.mLightColor = argb;
@@ -387,7 +391,7 @@
      * be set with {@link #setVibrationPattern(long[])}.
      *
      * Only modifiable before the channel is submitted to
-     * {@link NotificationManager#notify(String, int, Notification)}.
+     * {@link NotificationManager#createNotificationChannel(NotificationChannel)}.
      */
     public void enableVibration(boolean vibration) {
         this.mVibrationEnabled = vibration;
@@ -399,7 +403,7 @@
      * vibration} as well. Otherwise, vibration will be disabled.
      *
      * Only modifiable before the channel is submitted to
-     * {@link NotificationManager#notify(String, int, Notification)}.
+     * {@link NotificationManager#createNotificationChannel(NotificationChannel)}.
      */
     public void setVibrationPattern(long[] vibrationPattern) {
         this.mVibrationEnabled = vibrationPattern != null && vibrationPattern.length > 0;
@@ -407,9 +411,10 @@
     }
 
     /**
-     * Sets the level of interruption of this notification channel. Only
-     * modifiable before the channel is submitted to
-     * {@link NotificationManager#notify(String, int, Notification)}.
+     * Sets the level of interruption of this notification channel.
+     *
+     * Only modifiable before the channel is submitted to
+     * {@link NotificationManager#createNotificationChannel(NotificationChannel)}.
      *
      * @param importance the amount the user should be interrupted by
      *            notifications from this channel.
diff --git a/android/app/NotificationManager.java b/android/app/NotificationManager.java
index 46d1264..757fc64 100644
--- a/android/app/NotificationManager.java
+++ b/android/app/NotificationManager.java
@@ -1145,6 +1145,21 @@
                 SUPPRESSED_EFFECT_NOTIFICATION_LIST
         };
 
+        private static final int[] SCREEN_OFF_SUPPRESSED_EFFECTS = {
+                SUPPRESSED_EFFECT_SCREEN_OFF,
+                SUPPRESSED_EFFECT_FULL_SCREEN_INTENT,
+                SUPPRESSED_EFFECT_LIGHTS,
+                SUPPRESSED_EFFECT_AMBIENT,
+        };
+
+        private static final int[] SCREEN_ON_SUPPRESSED_EFFECTS = {
+                SUPPRESSED_EFFECT_SCREEN_ON,
+                SUPPRESSED_EFFECT_PEEK,
+                SUPPRESSED_EFFECT_STATUS_BAR,
+                SUPPRESSED_EFFECT_BADGE,
+                SUPPRESSED_EFFECT_NOTIFICATION_LIST
+        };
+
         /**
          * Visual effects to suppress for a notification that is filtered by Do Not Disturb mode.
          * Bitmask of SUPPRESSED_EFFECT_* constants.
@@ -1297,6 +1312,58 @@
             return true;
         }
 
+        /**
+         * @hide
+         */
+        public static boolean areAnyScreenOffEffectsSuppressed(int effects) {
+            for (int i = 0; i < SCREEN_OFF_SUPPRESSED_EFFECTS.length; i++) {
+                final int effect = SCREEN_OFF_SUPPRESSED_EFFECTS[i];
+                if ((effects & effect) != 0) {
+                    return true;
+                }
+            }
+            return false;
+        }
+
+        /**
+         * @hide
+         */
+        public static boolean areAnyScreenOnEffectsSuppressed(int effects) {
+            for (int i = 0; i < SCREEN_ON_SUPPRESSED_EFFECTS.length; i++) {
+                final int effect = SCREEN_ON_SUPPRESSED_EFFECTS[i];
+                if ((effects & effect) != 0) {
+                    return true;
+                }
+            }
+            return false;
+        }
+
+        /**
+         * @hide
+         */
+        public static int toggleScreenOffEffectsSuppressed(int currentEffects, boolean suppress) {
+            return toggleEffects(currentEffects, SCREEN_OFF_SUPPRESSED_EFFECTS, suppress);
+        }
+
+        /**
+         * @hide
+         */
+        public static int toggleScreenOnEffectsSuppressed(int currentEffects, boolean suppress) {
+            return toggleEffects(currentEffects, SCREEN_ON_SUPPRESSED_EFFECTS, suppress);
+        }
+
+        private static int toggleEffects(int currentEffects, int[] effects, boolean suppress) {
+            for (int i = 0; i < effects.length; i++) {
+                final int effect = effects[i];
+                if (suppress) {
+                    currentEffects |= effect;
+                } else {
+                    currentEffects &= ~effect;
+                }
+            }
+            return currentEffects;
+        }
+
         public static String suppressedEffectsToString(int effects) {
             if (effects <= 0) return "";
             final StringBuilder sb = new StringBuilder();
diff --git a/android/app/Person.java b/android/app/Person.java
new file mode 100644
index 0000000..3884a8d
--- /dev/null
+++ b/android/app/Person.java
@@ -0,0 +1,270 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.graphics.drawable.Icon;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * Provides an immutable reference to an entity that appears repeatedly on different surfaces of the
+ * platform. For example, this could represent the sender of a message.
+ */
+public final class Person implements Parcelable {
+
+    @Nullable private CharSequence mName;
+    @Nullable private Icon mIcon;
+    @Nullable private String mUri;
+    @Nullable private String mKey;
+    private boolean mIsBot;
+    private boolean mIsImportant;
+
+    private Person(Parcel in) {
+        mName = in.readCharSequence();
+        if (in.readInt() != 0) {
+            mIcon = Icon.CREATOR.createFromParcel(in);
+        }
+        mUri = in.readString();
+        mKey = in.readString();
+        mIsImportant = in.readBoolean();
+        mIsBot = in.readBoolean();
+    }
+
+    private Person(Builder builder) {
+        mName = builder.mName;
+        mIcon = builder.mIcon;
+        mUri = builder.mUri;
+        mKey = builder.mKey;
+        mIsBot = builder.mIsBot;
+        mIsImportant = builder.mIsImportant;
+    }
+
+    /** Creates and returns a new {@link Builder} initialized with this Person's data. */
+    public Builder toBuilder() {
+        return new Builder(this);
+    }
+
+    /**
+     * @return the uri provided for this person or {@code null} if no Uri was provided.
+     */
+    @Nullable
+    public String getUri() {
+        return mUri;
+    }
+
+    /**
+     * @return the name provided for this person or {@code null} if no name was provided.
+     */
+    @Nullable
+    public CharSequence getName() {
+        return mName;
+    }
+
+    /**
+     * @return the icon provided for this person or {@code null} if no icon was provided.
+     */
+    @Nullable
+    public Icon getIcon() {
+        return mIcon;
+    }
+
+    /**
+     * @return the key provided for this person or {@code null} if no key was provided.
+     */
+    @Nullable
+    public String getKey() {
+        return mKey;
+    }
+
+    /**
+     * @return whether this Person is a machine.
+     */
+    public boolean isBot() {
+        return mIsBot;
+    }
+
+    /**
+     * @return whether this Person is important.
+     */
+    public boolean isImportant() {
+        return mIsImportant;
+    }
+
+    /**
+     * @return the URI associated with this person, or "name:mName" otherwise
+     *  @hide
+     */
+    public String resolveToLegacyUri() {
+        if (mUri != null) {
+            return mUri;
+        }
+        if (mName != null) {
+            return "name:" + mName;
+        }
+        return "";
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, @WriteFlags int flags) {
+        dest.writeCharSequence(mName);
+        if (mIcon != null) {
+            dest.writeInt(1);
+            mIcon.writeToParcel(dest, 0);
+        } else {
+            dest.writeInt(0);
+        }
+        dest.writeString(mUri);
+        dest.writeString(mKey);
+        dest.writeBoolean(mIsImportant);
+        dest.writeBoolean(mIsBot);
+    }
+
+    /** Builder for the immutable {@link Person} class. */
+    public static class Builder {
+        @Nullable private CharSequence mName;
+        @Nullable private Icon mIcon;
+        @Nullable private String mUri;
+        @Nullable private String mKey;
+        private boolean mIsBot;
+        private boolean mIsImportant;
+
+        /** Creates a new, empty {@link Builder}. */
+        public Builder() {
+        }
+
+        private Builder(Person person) {
+            mName = person.mName;
+            mIcon = person.mIcon;
+            mUri = person.mUri;
+            mKey = person.mKey;
+            mIsBot = person.mIsBot;
+            mIsImportant = person.mIsImportant;
+        }
+
+        /**
+         * Give this person a name.
+         *
+         * @param name the name of this person.
+         */
+        @NonNull
+        public Person.Builder setName(@Nullable CharSequence name) {
+            this.mName = name;
+            return this;
+        }
+
+        /**
+         * Add an icon for this person.
+         * <br />
+         * The system will prefer this icon over any images that are resolved from the URI.
+         *
+         * @param icon the icon of the person.
+         */
+        @NonNull
+        public Person.Builder setIcon(@Nullable Icon icon) {
+            this.mIcon = icon;
+            return this;
+        }
+
+        /**
+         * Set a URI associated with this person.
+         *
+         * <P>
+         * The person should be specified by the {@code String} representation of a
+         * {@link android.provider.ContactsContract.Contacts#CONTENT_LOOKUP_URI}.
+         * </P>
+         *
+         * <P>The system will also attempt to resolve {@code mailto:} and {@code tel:} schema
+         * URIs. The path part of these URIs must exist in the contacts database, in the
+         * appropriate column, or the reference will be discarded as invalid. Telephone schema
+         * URIs will be resolved by {@link android.provider.ContactsContract.PhoneLookup}.
+         * </P>
+         *
+         * @param uri a URI for the person.
+         */
+        @NonNull
+        public Person.Builder setUri(@Nullable String uri) {
+            mUri = uri;
+            return this;
+        }
+
+        /**
+         * Add a key to this person in order to uniquely identify it.
+         * This is especially useful if the name doesn't uniquely identify this person or if the
+         * display name is a short handle of the actual name.
+         *
+         * <P>If no key is provided, the name serves as the key for the purpose of
+         * identification.</P>
+         *
+         * @param key the key that uniquely identifies this person.
+         */
+        @NonNull
+        public Person.Builder setKey(@Nullable String key) {
+            mKey = key;
+            return this;
+        }
+
+        /**
+         * Sets whether this is an important person. Use this method to denote users who frequently
+         * interact with the user of this device when {@link #setUri(String)} isn't provided with
+         * {@link android.provider.ContactsContract.Contacts#CONTENT_LOOKUP_URI}, and instead with
+         * the {@code mailto:} or {@code tel:} schemas.
+         *
+         * @param isImportant {@code true} if this is an important person, {@code false} otherwise.
+         */
+        @NonNull
+        public Person.Builder setImportant(boolean isImportant) {
+            mIsImportant = isImportant;
+            return this;
+        }
+
+        /**
+         * Sets whether this person is a machine rather than a human.
+         *
+         * @param isBot {@code true} if this person is a machine, {@code false} otherwise.
+         */
+        @NonNull
+        public Person.Builder setBot(boolean isBot) {
+            mIsBot = isBot;
+            return this;
+        }
+
+        /** Creates and returns the {@link Person} this builder represents. */
+        @NonNull
+        public Person build() {
+            return new Person(this);
+        }
+    }
+
+    public static final Creator<Person> CREATOR = new Creator<Person>() {
+        @Override
+        public Person createFromParcel(Parcel in) {
+            return new Person(in);
+        }
+
+        @Override
+        public Person[] newArray(int size) {
+            return new Person[size];
+        }
+    };
+}
diff --git a/android/app/RemoteAction.java b/android/app/RemoteAction.java
index 47741c0..c174665 100644
--- a/android/app/RemoteAction.java
+++ b/android/app/RemoteAction.java
@@ -122,6 +122,7 @@
     public RemoteAction clone() {
         RemoteAction action = new RemoteAction(mIcon, mTitle, mContentDescription, mActionIntent);
         action.setEnabled(mEnabled);
+        action.setShouldShowIcon(mShouldShowIcon);
         return action;
     }
 
diff --git a/android/app/StatsManager.java b/android/app/StatsManager.java
index 4a6fa8c..8783d94 100644
--- a/android/app/StatsManager.java
+++ b/android/app/StatsManager.java
@@ -23,6 +23,7 @@
 import android.os.IStatsManager;
 import android.os.RemoteException;
 import android.os.ServiceManager;
+import android.util.AndroidException;
 import android.util.Slog;
 
 /**
@@ -82,55 +83,74 @@
     }
 
     /**
-     * Clients can send a configuration and simultaneously registers the name of a broadcast
-     * receiver that listens for when it should request data.
+     * Adds the given configuration and associates it with the given configKey. If a config with the
+     * given configKey already exists for the caller's uid, it is replaced with the new one.
      *
      * @param configKey An arbitrary integer that allows clients to track the configuration.
-     * @param config    Wire-encoded StatsDConfig proto that specifies metrics (and all
+     * @param config    Wire-encoded StatsdConfig proto that specifies metrics (and all
      *                  dependencies eg, conditions and matchers).
-     * @return true if successful
+     * @throws StatsUnavailableException if unsuccessful due to failing to connect to stats service
+     * @throws IllegalArgumentException if config is not a wire-encoded StatsdConfig proto
      */
     @RequiresPermission(Manifest.permission.DUMP)
-    public boolean addConfiguration(long configKey, byte[] config) {
+    public void addConfig(long configKey, byte[] config) throws StatsUnavailableException {
         synchronized (this) {
             try {
                 IStatsManager service = getIStatsManagerLocked();
-                if (service == null) {
-                    Slog.e(TAG, "Failed to find statsd when adding configuration");
-                    return false;
-                }
-                return service.addConfiguration(configKey, config);
+                service.addConfiguration(configKey, config); // can throw IllegalArgumentException
             } catch (RemoteException e) {
                 Slog.e(TAG, "Failed to connect to statsd when adding configuration");
-                return false;
+                throw new StatsUnavailableException("could not connect", e);
             }
         }
     }
 
     /**
+     * TODO: Temporary for backwards compatibility. Remove.
+     */
+    @RequiresPermission(Manifest.permission.DUMP)
+    public boolean addConfiguration(long configKey, byte[] config) {
+        try {
+            addConfig(configKey, config);
+            return true;
+        } catch (StatsUnavailableException | IllegalArgumentException e) {
+            return false;
+        }
+    }
+
+    /**
      * Remove a configuration from logging.
      *
      * @param configKey Configuration key to remove.
-     * @return true if successful
+     * @throws StatsUnavailableException if unsuccessful due to failing to connect to stats service
      */
     @RequiresPermission(Manifest.permission.DUMP)
-    public boolean removeConfiguration(long configKey) {
+    public void removeConfig(long configKey) throws StatsUnavailableException {
         synchronized (this) {
             try {
                 IStatsManager service = getIStatsManagerLocked();
-                if (service == null) {
-                    Slog.e(TAG, "Failed to find statsd when removing configuration");
-                    return false;
-                }
-                return service.removeConfiguration(configKey);
+                service.removeConfiguration(configKey);
             } catch (RemoteException e) {
                 Slog.e(TAG, "Failed to connect to statsd when removing configuration");
-                return false;
+                throw new StatsUnavailableException("could not connect", e);
             }
         }
     }
 
     /**
+     * TODO: Temporary for backwards compatibility. Remove.
+     */
+    @RequiresPermission(Manifest.permission.DUMP)
+    public boolean removeConfiguration(long configKey) {
+        try {
+            removeConfig(configKey);
+            return true;
+        } catch (StatsUnavailableException e) {
+            return false;
+        }
+    }
+
+    /**
      * Set the PendingIntent to be used when broadcasting subscriber information to the given
      * subscriberId within the given config.
      * <p>
@@ -150,123 +170,165 @@
      * {@link #EXTRA_STATS_DIMENSIONS_VALUE}.
      * <p>
      * This function can only be called by the owner (uid) of the config. It must be called each
-     * time statsd starts. The config must have been added first (via addConfiguration()).
+     * time statsd starts. The config must have been added first (via {@link #addConfig}).
      *
-     * @param configKey     The integer naming the config to which this subscriber is attached.
-     * @param subscriberId  ID of the subscriber, as used in the config.
      * @param pendingIntent the PendingIntent to use when broadcasting info to the subscriber
      *                      associated with the given subscriberId. May be null, in which case
      *                      it undoes any previous setting of this subscriberId.
-     * @return true if successful
+     * @param configKey     The integer naming the config to which this subscriber is attached.
+     * @param subscriberId  ID of the subscriber, as used in the config.
+     * @throws StatsUnavailableException if unsuccessful due to failing to connect to stats service
+     */
+    @RequiresPermission(Manifest.permission.DUMP)
+    public void setBroadcastSubscriber(
+            PendingIntent pendingIntent, long configKey, long subscriberId)
+            throws StatsUnavailableException {
+        synchronized (this) {
+            try {
+                IStatsManager service = getIStatsManagerLocked();
+                if (pendingIntent != null) {
+                    // Extracts IIntentSender from the PendingIntent and turns it into an IBinder.
+                    IBinder intentSender = pendingIntent.getTarget().asBinder();
+                    service.setBroadcastSubscriber(configKey, subscriberId, intentSender);
+                } else {
+                    service.unsetBroadcastSubscriber(configKey, subscriberId);
+                }
+            } catch (RemoteException e) {
+                Slog.e(TAG, "Failed to connect to statsd when adding broadcast subscriber", e);
+                throw new StatsUnavailableException("could not connect", e);
+            }
+        }
+    }
+
+    /**
+     * TODO: Temporary for backwards compatibility. Remove.
      */
     @RequiresPermission(Manifest.permission.DUMP)
     public boolean setBroadcastSubscriber(
             long configKey, long subscriberId, PendingIntent pendingIntent) {
-        synchronized (this) {
-            try {
-                IStatsManager service = getIStatsManagerLocked();
-                if (service == null) {
-                    Slog.e(TAG, "Failed to find statsd when adding broadcast subscriber");
-                    return false;
-                }
-                if (pendingIntent != null) {
-                    // Extracts IIntentSender from the PendingIntent and turns it into an IBinder.
-                    IBinder intentSender = pendingIntent.getTarget().asBinder();
-                    return service.setBroadcastSubscriber(configKey, subscriberId, intentSender);
-                } else {
-                    return service.unsetBroadcastSubscriber(configKey, subscriberId);
-                }
-            } catch (RemoteException e) {
-                Slog.e(TAG, "Failed to connect to statsd when adding broadcast subscriber", e);
-                return false;
-            }
+        try {
+            setBroadcastSubscriber(pendingIntent, configKey, subscriberId);
+            return true;
+        } catch (StatsUnavailableException e) {
+            return false;
         }
     }
 
     /**
      * Registers the operation that is called to retrieve the metrics data. This must be called
-     * each time statsd starts. The config must have been added first (via addConfiguration(),
-     * although addConfiguration could have been called on a previous boot). This operation allows
+     * each time statsd starts. The config must have been added first (via {@link #addConfig},
+     * although addConfig could have been called on a previous boot). This operation allows
      * statsd to send metrics data whenever statsd determines that the metrics in memory are
-     * approaching the memory limits. The fetch operation should call {@link #getData} to fetch the
-     * data, which also deletes the retrieved metrics from statsd's memory.
+     * approaching the memory limits. The fetch operation should call {@link #getReports} to fetch
+     * the data, which also deletes the retrieved metrics from statsd's memory.
      *
-     * @param configKey     The integer naming the config to which this operation is attached.
      * @param pendingIntent the PendingIntent to use when broadcasting info to the subscriber
      *                      associated with the given subscriberId. May be null, in which case
      *                      it removes any associated pending intent with this configKey.
-     * @return true if successful
+     * @param configKey     The integer naming the config to which this operation is attached.
+     * @throws StatsUnavailableException if unsuccessful due to failing to connect to stats service
      */
     @RequiresPermission(Manifest.permission.DUMP)
-    public boolean setDataFetchOperation(long configKey, PendingIntent pendingIntent) {
+    public void setFetchReportsOperation(PendingIntent pendingIntent, long configKey)
+            throws StatsUnavailableException {
         synchronized (this) {
             try {
                 IStatsManager service = getIStatsManagerLocked();
-                if (service == null) {
-                    Slog.e(TAG, "Failed to find statsd when registering data listener.");
-                    return false;
-                }
                 if (pendingIntent == null) {
-                    return service.removeDataFetchOperation(configKey);
+                    service.removeDataFetchOperation(configKey);
                 } else {
                     // Extracts IIntentSender from the PendingIntent and turns it into an IBinder.
                     IBinder intentSender = pendingIntent.getTarget().asBinder();
-                    return service.setDataFetchOperation(configKey, intentSender);
+                    service.setDataFetchOperation(configKey, intentSender);
                 }
 
             } catch (RemoteException e) {
                 Slog.e(TAG, "Failed to connect to statsd when registering data listener.");
-                return false;
+                throw new StatsUnavailableException("could not connect", e);
             }
         }
     }
 
     /**
-     * Clients can request data with a binder call. This getter is destructive and also clears
-     * the retrieved metrics from statsd memory.
-     *
-     * @param configKey Configuration key to retrieve data from.
-     * @return Serialized ConfigMetricsReportList proto. Returns null on failure (eg, if statsd
-     * crashed).
+     * TODO: Temporary for backwards compatibility. Remove.
      */
     @RequiresPermission(Manifest.permission.DUMP)
-    public @Nullable byte[] getData(long configKey) {
+    public boolean setDataFetchOperation(long configKey, PendingIntent pendingIntent) {
+        try {
+            setFetchReportsOperation(pendingIntent, configKey);
+            return true;
+        } catch (StatsUnavailableException e) {
+            return false;
+        }
+    }
+
+    /**
+     * Request the data collected for the given configKey.
+     * This getter is destructive - it also clears the retrieved metrics from statsd's memory.
+     *
+     * @param configKey Configuration key to retrieve data from.
+     * @return Serialized ConfigMetricsReportList proto.
+     * @throws StatsUnavailableException if unsuccessful due to failing to connect to stats service
+     */
+    @RequiresPermission(Manifest.permission.DUMP)
+    public byte[] getReports(long configKey) throws StatsUnavailableException {
         synchronized (this) {
             try {
                 IStatsManager service = getIStatsManagerLocked();
-                if (service == null) {
-                    Slog.e(TAG, "Failed to find statsd when getting data");
-                    return null;
-                }
                 return service.getData(configKey);
             } catch (RemoteException e) {
                 Slog.e(TAG, "Failed to connect to statsd when getting data");
-                return null;
+                throw new StatsUnavailableException("could not connect", e);
+            }
+        }
+    }
+
+    /**
+     * TODO: Temporary for backwards compatibility. Remove.
+     */
+    @RequiresPermission(Manifest.permission.DUMP)
+    public @Nullable byte[] getData(long configKey) {
+        try {
+            return getReports(configKey);
+        } catch (StatsUnavailableException e) {
+            return null;
+        }
+    }
+
+    /**
+     * Clients can request metadata for statsd. Will contain stats across all configurations but not
+     * the actual metrics themselves (metrics must be collected via {@link #getReports(long)}.
+     * This getter is not destructive and will not reset any metrics/counters.
+     *
+     * @return Serialized StatsdStatsReport proto.
+     * @throws StatsUnavailableException if unsuccessful due to failing to connect to stats service
+     */
+    @RequiresPermission(Manifest.permission.DUMP)
+    public byte[] getStatsMetadata() throws StatsUnavailableException {
+        synchronized (this) {
+            try {
+                IStatsManager service = getIStatsManagerLocked();
+                return service.getMetadata();
+            } catch (RemoteException e) {
+                Slog.e(TAG, "Failed to connect to statsd when getting metadata");
+                throw new StatsUnavailableException("could not connect", e);
             }
         }
     }
 
     /**
      * Clients can request metadata for statsd. Will contain stats across all configurations but not
-     * the actual metrics themselves (metrics must be collected via {@link #getData(String)}.
+     * the actual metrics themselves (metrics must be collected via {@link #getReports(long)}.
      * This getter is not destructive and will not reset any metrics/counters.
      *
      * @return Serialized StatsdStatsReport proto. Returns null on failure (eg, if statsd crashed).
      */
     @RequiresPermission(Manifest.permission.DUMP)
     public @Nullable byte[] getMetadata() {
-        synchronized (this) {
-            try {
-                IStatsManager service = getIStatsManagerLocked();
-                if (service == null) {
-                    Slog.e(TAG, "Failed to find statsd when getting metadata");
-                    return null;
-                }
-                return service.getMetadata();
-            } catch (RemoteException e) {
-                Slog.e(TAG, "Failed to connect to statsd when getting metadata");
-                return null;
-            }
+        try {
+            return getStatsMetadata();
+        } catch (StatsUnavailableException e) {
+            return null;
         }
     }
 
@@ -279,14 +341,33 @@
         }
     }
 
-    private IStatsManager getIStatsManagerLocked() throws RemoteException {
+    private IStatsManager getIStatsManagerLocked() throws StatsUnavailableException {
         if (mService != null) {
             return mService;
         }
         mService = IStatsManager.Stub.asInterface(ServiceManager.getService("stats"));
-        if (mService != null) {
+        if (mService == null) {
+            throw new StatsUnavailableException("could not be found");
+        }
+        try {
             mService.asBinder().linkToDeath(new StatsdDeathRecipient(), 0);
+        } catch (RemoteException e) {
+            throw new StatsUnavailableException("could not connect when linkToDeath", e);
         }
         return mService;
     }
+
+    /**
+     * Exception thrown when communication with the stats service fails (eg if it is not available).
+     * This might be thrown early during boot before the stats service has started or if it crashed.
+     */
+    public static class StatsUnavailableException extends AndroidException {
+        public StatsUnavailableException(String reason) {
+            super("Failed to connect to statsd: " + reason);
+        }
+
+        public StatsUnavailableException(String reason, Throwable e) {
+            super("Failed to connect to statsd: " + reason, e);
+        }
+    }
 }
diff --git a/android/app/SystemServiceRegistry.java b/android/app/SystemServiceRegistry.java
index 1776eac..246d4a3 100644
--- a/android/app/SystemServiceRegistry.java
+++ b/android/app/SystemServiceRegistry.java
@@ -18,6 +18,7 @@
 
 import android.accounts.AccountManager;
 import android.accounts.IAccountManager;
+import android.app.ContextImpl.ServiceInitializationState;
 import android.app.admin.DevicePolicyManager;
 import android.app.admin.IDevicePolicyManager;
 import android.app.job.IJobScheduler;
@@ -104,10 +105,12 @@
 import android.os.BatteryManager;
 import android.os.BatteryStats;
 import android.os.Build;
+import android.os.DeviceIdleManager;
 import android.os.DropBoxManager;
 import android.os.HardwarePropertiesManager;
 import android.os.IBatteryPropertiesRegistrar;
 import android.os.IBinder;
+import android.os.IDeviceIdleController;
 import android.os.IHardwarePropertiesManager;
 import android.os.IPowerManager;
 import android.os.IRecoverySystem;
@@ -160,7 +163,6 @@
 import com.android.internal.policy.PhoneLayoutInflater;
 
 import java.util.HashMap;
-import java.util.concurrent.atomic.AtomicInteger;
 
 /**
  * Manages all of the system services that can be returned by {@link Context#getSystemService}.
@@ -279,12 +281,12 @@
             }});
 
         registerService(Context.IPSEC_SERVICE, IpSecManager.class,
-                new StaticServiceFetcher<IpSecManager>() {
+                new CachedServiceFetcher<IpSecManager>() {
             @Override
-            public IpSecManager createService() {
+            public IpSecManager createService(ContextImpl ctx) throws ServiceNotFoundException {
                 IBinder b = ServiceManager.getService(Context.IPSEC_SERVICE);
                 IIpSecService service = IIpSecService.Stub.asInterface(b);
-                return new IpSecManager(service);
+                return new IpSecManager(ctx, service);
             }});
 
         registerService(Context.COUNTRY_DETECTOR, CountryDetector.class,
@@ -984,6 +986,17 @@
                                 ctx.mMainThread.getHandler());
                     }
             });
+
+        registerService(Context.DEVICE_IDLE_CONTROLLER, DeviceIdleManager.class,
+                new CachedServiceFetcher<DeviceIdleManager>() {
+                    @Override
+                    public DeviceIdleManager createService(ContextImpl ctx)
+                            throws ServiceNotFoundException {
+                        IDeviceIdleController service = IDeviceIdleController.Stub.asInterface(
+                                ServiceManager.getServiceOrThrow(
+                                        Context.DEVICE_IDLE_CONTROLLER));
+                        return new DeviceIdleManager(ctx.getOuterContext(), service);
+                    }});
     }
 
     /**
@@ -993,10 +1006,6 @@
         return new Object[sServiceCacheSize];
     }
 
-    public static AtomicInteger[] createServiceInitializationStateArray() {
-        return new AtomicInteger[sServiceCacheSize];
-    }
-
     /**
      * Gets a system service from a given context.
      */
@@ -1037,7 +1046,10 @@
     static abstract class CachedServiceFetcher<T> implements ServiceFetcher<T> {
         private final int mCacheIndex;
 
-        public CachedServiceFetcher() {
+        CachedServiceFetcher() {
+            // Note this class must be instantiated only by the static initializer of the
+            // outer class (SystemServiceRegistry), which already does the synchronization,
+            // so bare access to sServiceCacheSize is okay here.
             mCacheIndex = sServiceCacheSize++;
         }
 
@@ -1045,95 +1057,73 @@
         @SuppressWarnings("unchecked")
         public final T getService(ContextImpl ctx) {
             final Object[] cache = ctx.mServiceCache;
+            final int[] gates = ctx.mServiceInitializationStateArray;
 
-            // Fast path. If it's already cached, just return it.
-            Object service = cache[mCacheIndex];
-            if (service != null) {
-                return (T) service;
-            }
+            for (;;) {
+                boolean doInitialize = false;
+                synchronized (cache) {
+                    // Return it if we already have a cached instance.
+                    T service = (T) cache[mCacheIndex];
+                    if (service != null || gates[mCacheIndex] == ContextImpl.STATE_NOT_FOUND) {
+                        return service;
+                    }
 
-            // Slow path.
-            final AtomicInteger[] gates = ctx.mServiceInitializationStateArray;
-            final AtomicInteger gate;
+                    // If we get here, there's no cached instance.
 
-            synchronized (cache) {
-                // See if it's cached or not again, with the lock held this time.
-                service = cache[mCacheIndex];
-                if (service != null) {
-                    return (T) service;
+                    // Grr... if gate is STATE_READY, then this means we initialized the service
+                    // once but someone cleared it.
+                    // We start over from STATE_UNINITIALIZED.
+                    if (gates[mCacheIndex] == ContextImpl.STATE_READY) {
+                        gates[mCacheIndex] = ContextImpl.STATE_UNINITIALIZED;
+                    }
+
+                    // It's possible for multiple threads to get here at the same time, so
+                    // use the "gate" to make sure only the first thread will call createService().
+
+                    // At this point, the gate must be either UNINITIALIZED or INITIALIZING.
+                    if (gates[mCacheIndex] == ContextImpl.STATE_UNINITIALIZED) {
+                        doInitialize = true;
+                        gates[mCacheIndex] = ContextImpl.STATE_INITIALIZING;
+                    }
                 }
 
-                // Not initialized yet. Create an atomic boolean to control which thread should
-                // instantiate the service.
-                if (gates[mCacheIndex] != null) {
-                    gate = gates[mCacheIndex];
-                } else {
-                    gate = new AtomicInteger(ContextImpl.STATE_UNINITIALIZED);
-                    gates[mCacheIndex] = gate;
-                }
-            }
+                if (doInitialize) {
+                    // Only the first thread gets here.
 
-            // Not cached yet.
-            //
-            // Note multiple threads can reach here for the same service on the same context
-            // concurrently.
-            //
-            // Now we're going to instantiate the service, but do so without the cache held;
-            // otherwise it could deadlock. (b/71882178)
-            //
-            // However we still don't want to instantiate the same service multiple times, so
-            // use the atomic integer to ensure only one thread will call createService().
-
-            if (gate.compareAndSet(
-                    ContextImpl.STATE_UNINITIALIZED, ContextImpl.STATE_INITIALIZING)) {
-                try {
-                    // This thread is the first one to get here. Instantiate the service
-                    // *without* the cache lock held.
+                    T service = null;
+                    @ServiceInitializationState int newState = ContextImpl.STATE_NOT_FOUND;
                     try {
+                        // This thread is the first one to get here. Instantiate the service
+                        // *without* the cache lock held.
                         service = createService(ctx);
+                        newState = ContextImpl.STATE_READY;
 
-                        synchronized (cache) {
-                            cache[mCacheIndex] = service;
-                        }
                     } catch (ServiceNotFoundException e) {
                         onServiceNotFound(e);
-                    }
-                } finally {
-                    // Tell the all other threads that the cache is ready now.
-                    // (But it's still be null in case of ServiceNotFoundException.)
-                    synchronized (gate) {
-                        gate.set(ContextImpl.STATE_READY);
-                        gate.notifyAll();
-                    }
-                }
-                return (T) service;
-            }
-            // Other threads will wait on the gate lock.
-            synchronized (gate) {
-                boolean interrupted = false;
 
-                // Note: We check whether "state == STATE_READY", not
-                // "cache[mCacheIndex] != null", because "cache[mCacheIndex] == null"
-                // is still a valid outcome in the ServiceNotFoundException case.
-                while (gate.get() != ContextImpl.STATE_READY) {
-                    try {
-                        gate.wait();
-                    } catch (InterruptedException e) {
-                        Log.w(TAG,  "getService() interrupted");
-                        interrupted = true;
+                    } finally {
+                        synchronized (cache) {
+                            cache[mCacheIndex] = service;
+                            gates[mCacheIndex] = newState;
+                            cache.notifyAll();
+                        }
+                    }
+                    return service;
+                }
+                // The other threads will wait for the first thread to call notifyAll(),
+                // and go back to the top and retry.
+                synchronized (cache) {
+                    while (gates[mCacheIndex] < ContextImpl.STATE_READY) {
+                        try {
+                            cache.wait();
+                        } catch (InterruptedException e) {
+                            Log.w(TAG, "getService() interrupted");
+                            Thread.currentThread().interrupt();
+                            return null;
+                        }
                     }
                 }
-                if (interrupted) {
-                    Thread.currentThread().interrupt();
-                }
             }
-            // Now the first thread has initialized it.
-            // It may still be null if ServiceNotFoundException was thrown, but that shouldn't
-            // happen, so we'll just return null here in that case.
-            synchronized (cache) {
-                service = cache[mCacheIndex];
-            }
-            return (T) service;
         }
 
         public abstract T createService(ContextImpl ctx) throws ServiceNotFoundException;
diff --git a/android/app/UiAutomation.java b/android/app/UiAutomation.java
index bd4933a..c03340e 100644
--- a/android/app/UiAutomation.java
+++ b/android/app/UiAutomation.java
@@ -580,6 +580,8 @@
         // Execute the command *without* the lock being held.
         command.run();
 
+        List<AccessibilityEvent> receivedEvents = new ArrayList<>();
+
         // Acquire the lock and wait for the event.
         try {
             // Wait for the event.
@@ -600,14 +602,14 @@
                     if (filter.accept(event)) {
                         return event;
                     }
-                    event.recycle();
+                    receivedEvents.add(event);
                 }
                 // Check if timed out and if not wait.
                 final long elapsedTimeMillis = SystemClock.uptimeMillis() - startTimeMillis;
                 final long remainingTimeMillis = timeoutMillis - elapsedTimeMillis;
                 if (remainingTimeMillis <= 0) {
                     throw new TimeoutException("Expected event not received within: "
-                            + timeoutMillis + " ms.");
+                            + timeoutMillis + " ms among: " + receivedEvents);
                 }
                 synchronized (mLock) {
                     if (mEventQueue.isEmpty()) {
@@ -620,6 +622,11 @@
                 }
             }
         } finally {
+            int size = receivedEvents.size();
+            for (int i = 0; i < size; i++) {
+                receivedEvents.get(i).recycle();
+            }
+
             synchronized (mLock) {
                 mWaitingForEventDelivery = false;
                 mEventQueue.clear();
diff --git a/android/app/WallpaperManager.java b/android/app/WallpaperManager.java
index 465340f..6c2fb2d 100644
--- a/android/app/WallpaperManager.java
+++ b/android/app/WallpaperManager.java
@@ -401,7 +401,8 @@
                 }
             }
             synchronized (this) {
-                if (mCachedWallpaper != null && mCachedWallpaperUserId == userId) {
+                if (mCachedWallpaper != null && mCachedWallpaperUserId == userId
+                        && !mCachedWallpaper.isRecycled()) {
                     return mCachedWallpaper;
                 }
                 mCachedWallpaper = null;
@@ -412,7 +413,7 @@
                 } catch (OutOfMemoryError e) {
                     Log.w(TAG, "Out of memory loading the current wallpaper: " + e);
                 } catch (SecurityException e) {
-                    if (context.getApplicationInfo().targetSdkVersion <= Build.VERSION_CODES.O) {
+                    if (context.getApplicationInfo().targetSdkVersion < Build.VERSION_CODES.O_MR1) {
                         Log.w(TAG, "No permission to access wallpaper, suppressing"
                                 + " exception to avoid crashing legacy app.");
                     } else {
@@ -976,7 +977,7 @@
             } catch (RemoteException e) {
                 throw e.rethrowFromSystemServer();
             } catch (SecurityException e) {
-                if (mContext.getApplicationInfo().targetSdkVersion <= Build.VERSION_CODES.O) {
+                if (mContext.getApplicationInfo().targetSdkVersion < Build.VERSION_CODES.O_MR1) {
                     Log.w(TAG, "No permission to access wallpaper, suppressing"
                             + " exception to avoid crashing legacy app.");
                     return null;
diff --git a/android/app/admin/DevicePolicyManager.java b/android/app/admin/DevicePolicyManager.java
index b64aae5..c491dcc 100644
--- a/android/app/admin/DevicePolicyManager.java
+++ b/android/app/admin/DevicePolicyManager.java
@@ -1169,10 +1169,18 @@
      * Constant to indicate the feature of mandatory backups. Used as argument to
      * {@link #createAdminSupportIntent(String)}.
      * @see #setMandatoryBackupTransport(ComponentName, ComponentName)
+     * @hide
      */
     public static final String POLICY_MANDATORY_BACKUPS = "policy_mandatory_backups";
 
     /**
+     * Constant to indicate the feature of suspending app. Use it as the value of
+     * {@link #EXTRA_RESTRICTION}.
+     * @hide
+     */
+    public static final String POLICY_SUSPEND_PACKAGES = "policy_suspend_packages";
+
+    /**
      * A String indicating a specific restricted feature. Can be a user restriction from the
      * {@link UserManager}, e.g. {@link UserManager#DISALLOW_ADJUST_VOLUME}, or one of the values
      * {@link #POLICY_DISABLE_CAMERA}, {@link #POLICY_DISABLE_SCREEN_CAPTURE} or
@@ -4211,6 +4219,15 @@
         return null;
     }
 
+    /**
+     * Returns {@code true} if the device supports attestation of device identifiers in addition
+     * to key attestation.
+     * @return {@code true} if Device ID attestation is supported.
+     */
+    public boolean isDeviceIdAttestationSupported() {
+        PackageManager pm = mContext.getPackageManager();
+        return pm.hasSystemFeature(PackageManager.FEATURE_DEVICE_ID_ATTESTATION);
+    }
 
     /**
      * Called by a device or profile owner, or delegated certificate installer, to associate
@@ -6182,6 +6199,7 @@
      * @hide
      */
      @SystemApi
+     @RequiresPermission(android.Manifest.permission.MANAGE_USERS)
      public @Nullable List<String> getPermittedAccessibilityServices(int userId) {
         throwIfParentInstance("getPermittedAccessibilityServices");
         if (mService != null) {
@@ -6826,8 +6844,7 @@
      * @param restriction Indicates for which feature the dialog should be displayed. Can be a
      *            user restriction from {@link UserManager}, e.g.
      *            {@link UserManager#DISALLOW_ADJUST_VOLUME}, or one of the constants
-     *            {@link #POLICY_DISABLE_CAMERA}, {@link #POLICY_DISABLE_SCREEN_CAPTURE} or
-     *            {@link #POLICY_MANDATORY_BACKUPS}.
+     *            {@link #POLICY_DISABLE_CAMERA}, {@link #POLICY_DISABLE_SCREEN_CAPTURE}.
      * @return Intent An intent to be used to start the dialog-activity if the restriction is
      *            set by an admin, or null if the restriction does not exist or no admin set it.
      */
@@ -8774,13 +8791,6 @@
      *
      * <p> Backup service is off by default when device owner is present.
      *
-     * <p> If backups are made mandatory by specifying a non-null mandatory backup transport using
-     * the {@link DevicePolicyManager#setMandatoryBackupTransport} method, the backup service is
-     * automatically enabled.
-     *
-     * <p> If the backup service is disabled using this method after the mandatory backup transport
-     * has been set, the mandatory backup transport is cleared.
-     *
      * @param admin Which {@link DeviceAdminReceiver} this request is associated with.
      * @param enabled {@code true} to enable the backup service, {@code false} to disable it.
      * @throws SecurityException if {@code admin} is not a device owner.
@@ -8818,6 +8828,8 @@
      * <p>Only device owner can call this method.
      * <p>If backups were disabled and a non-null backup transport {@link ComponentName} is
      * specified, backups will be enabled.
+     * <p> If the backup service is disabled after the mandatory backup transport has been set, the
+     * mandatory backup transport is cleared.
      *
      * <p>NOTE: The method shouldn't be called on the main thread.
      *
@@ -8825,6 +8837,7 @@
      * @param backupTransportComponent The backup transport layer to be used for mandatory backups.
      * @return {@code true} if the backup transport was successfully set; {@code false} otherwise.
      * @throws SecurityException if {@code admin} is not a device owner.
+     * @hide
      */
     @WorkerThread
     public boolean setMandatoryBackupTransport(
@@ -8844,6 +8857,7 @@
      *
      * @return a {@link ComponentName} of the backup transport layer to be used if backups are
      *         mandatory or {@code null} if backups are not mandatory.
+     * @hide
      */
     public ComponentName getMandatoryBackupTransport() {
         throwIfParentInstance("getMandatoryBackupTransport");
diff --git a/android/app/admin/FreezeInterval.java b/android/app/admin/FreezePeriod.java
similarity index 74%
rename from android/app/admin/FreezeInterval.java
rename to android/app/admin/FreezePeriod.java
index de5e21a..657f017 100644
--- a/android/app/admin/FreezeInterval.java
+++ b/android/app/admin/FreezePeriod.java
@@ -20,49 +20,88 @@
 import android.util.Pair;
 
 import java.time.LocalDate;
+import java.time.MonthDay;
 import java.time.format.DateTimeFormatter;
 import java.util.ArrayList;
 import java.util.List;
 
 /**
- * An interval representing one freeze period which repeats annually. We use the number of days
- * since the start of (non-leap) year to define the start and end dates of an interval, both
- * inclusive. If the end date is smaller than the start date, the interval is considered wrapped
- * around the year-end. As far as an interval is concerned, February 29th should be treated as
- * if it were February 28th: so an interval starting or ending on February 28th are not
- * distinguishable from an interval on February 29th. When calulating interval length or
- * distance between two dates, February 29th is also disregarded.
+ * A class that represents one freeze period which repeats <em>annually</em>. A freeze period has
+ * two {@link java.time#MonthDay} values that define the start and end dates of the period, both
+ * inclusive. If the end date is earlier than the start date, the period is considered wrapped
+ * around the year-end. As far as freeze period is concerned, leap year is disregarded and February
+ * 29th should be treated as if it were February 28th: so a freeze starting or ending on February
+ * 28th is identical to a freeze starting or ending on February 29th. When calulating the length of
+ * a freeze or the distance bewteen two freee periods, February 29th is also ignored.
  *
  * @see SystemUpdatePolicy#setFreezePeriods
- * @hide
  */
-public class FreezeInterval {
-    private static final String TAG = "FreezeInterval";
+public class FreezePeriod {
+    private static final String TAG = "FreezePeriod";
 
     private static final int DUMMY_YEAR = 2001;
     static final int DAYS_IN_YEAR = 365; // 365 since DUMMY_YEAR is not a leap year
 
-    final int mStartDay; // [1,365]
-    final int mEndDay; // [1,365]
+    private final MonthDay mStart;
+    private final MonthDay mEnd;
 
-    FreezeInterval(int startDay, int endDay) {
-        if (startDay < 1 || startDay > 365 || endDay < 1 || endDay > 365) {
-            throw new RuntimeException("Bad dates for Interval: " + startDay + "," + endDay);
-        }
-        mStartDay = startDay;
-        mEndDay = endDay;
+    /*
+     * Start and end dates represented by number of days since the beginning of the year.
+     * They are internal representations of mStart and mEnd with normalized Leap year days
+     * (Feb 29 == Feb 28 == 59th day of year). All internal calclations are based on
+     * these two values so that leap year days are disregarded.
+     */
+    private final int mStartDay; // [1, 365]
+    private final int mEndDay; // [1, 365]
+
+    /**
+     * Creates a freeze period by its start and end dates. If the end date is earlier than the start
+     * date, the freeze period is considered wrapping year-end.
+     */
+    public FreezePeriod(MonthDay start, MonthDay end) {
+        mStart = start;
+        mStartDay = mStart.atYear(DUMMY_YEAR).getDayOfYear();
+        mEnd = end;
+        mEndDay = mEnd.atYear(DUMMY_YEAR).getDayOfYear();
     }
 
+    /**
+     * Returns the start date (inclusive) of this freeze period.
+     */
+    public MonthDay getStart() {
+        return mStart;
+    }
+
+    /**
+     * Returns the end date (inclusive) of this freeze period.
+     */
+    public MonthDay getEnd() {
+        return mEnd;
+    }
+
+    /**
+     * @hide
+     */
+    private FreezePeriod(int startDay, int endDay) {
+        mStartDay = startDay;
+        mStart = dayOfYearToMonthDay(startDay);
+        mEndDay = endDay;
+        mEnd = dayOfYearToMonthDay(endDay);
+    }
+
+    /** @hide */
     int getLength() {
         return getEffectiveEndDay() - mStartDay + 1;
     }
 
+    /** @hide */
     boolean isWrapped() {
         return mEndDay < mStartDay;
     }
 
     /**
      * Returns the effective end day, taking wrapping around year-end into consideration
+     * @hide
      */
     int getEffectiveEndDay() {
         if (!isWrapped()) {
@@ -72,6 +111,7 @@
         }
     }
 
+    /** @hide */
     boolean contains(LocalDate localDate) {
         final int daysOfYear = dayOfYearDisregardLeapYear(localDate);
         if (!isWrapped()) {
@@ -84,6 +124,7 @@
         }
     }
 
+    /** @hide */
     boolean after(LocalDate localDate) {
         return mStartDay > dayOfYearDisregardLeapYear(localDate);
     }
@@ -95,6 +136,7 @@
      * include now, the returned dates represents the next future interval.
      * The result will always have the same month and dayOfMonth value as the non-instantiated
      * interval itself.
+     * @hide
      */
     Pair<LocalDate, LocalDate> toCurrentOrFutureRealDates(LocalDate now) {
         final int nowDays = dayOfYearDisregardLeapYear(now);
@@ -138,14 +180,24 @@
                 + LocalDate.ofYearDay(DUMMY_YEAR, mEndDay).format(formatter);
     }
 
-    // Treat the supplied date as in a non-leap year and return its day of year.
-    static int dayOfYearDisregardLeapYear(LocalDate date) {
+    /** @hide */
+    private static MonthDay dayOfYearToMonthDay(int dayOfYear) {
+        LocalDate date = LocalDate.ofYearDay(DUMMY_YEAR, dayOfYear);
+        return MonthDay.of(date.getMonth(), date.getDayOfMonth());
+    }
+
+    /**
+     * Treat the supplied date as in a non-leap year and return its day of year.
+     * @hide
+     */
+    private static int dayOfYearDisregardLeapYear(LocalDate date) {
         return date.withYear(DUMMY_YEAR).getDayOfYear();
     }
 
     /**
      * Compute the number of days between first (inclusive) and second (exclusive),
      * treating all years in between as non-leap.
+     * @hide
      */
     public static int distanceWithoutLeapYear(LocalDate first, LocalDate second) {
         return dayOfYearDisregardLeapYear(first) - dayOfYearDisregardLeapYear(second)
@@ -165,16 +217,16 @@
      *     3. At most one wrapped Interval remains, and it will be at the end of the list
      * @hide
      */
-    protected static List<FreezeInterval> canonicalizeIntervals(List<FreezeInterval> intervals) {
+    static List<FreezePeriod> canonicalizePeriods(List<FreezePeriod> intervals) {
         boolean[] taken = new boolean[DAYS_IN_YEAR];
         // First convert the intervals into flat array
-        for (FreezeInterval interval : intervals) {
+        for (FreezePeriod interval : intervals) {
             for (int i = interval.mStartDay; i <= interval.getEffectiveEndDay(); i++) {
                 taken[(i - 1) % DAYS_IN_YEAR] = true;
             }
         }
         // Then reconstruct intervals from the array
-        List<FreezeInterval> result = new ArrayList<>();
+        List<FreezePeriod> result = new ArrayList<>();
         int i = 0;
         while (i < DAYS_IN_YEAR) {
             if (!taken[i]) {
@@ -183,14 +235,14 @@
             }
             final int intervalStart = i + 1;
             while (i < DAYS_IN_YEAR && taken[i]) i++;
-            result.add(new FreezeInterval(intervalStart, i));
+            result.add(new FreezePeriod(intervalStart, i));
         }
         // Check if the last entry can be merged to the first entry to become one single
         // wrapped interval
         final int lastIndex = result.size() - 1;
         if (lastIndex > 0 && result.get(lastIndex).mEndDay == DAYS_IN_YEAR
                 && result.get(0).mStartDay == 1) {
-            FreezeInterval wrappedInterval = new FreezeInterval(result.get(lastIndex).mStartDay,
+            FreezePeriod wrappedInterval = new FreezePeriod(result.get(lastIndex).mStartDay,
                     result.get(0).mEndDay);
             result.set(lastIndex, wrappedInterval);
             result.remove(0);
@@ -207,18 +259,18 @@
      *
      * @hide
      */
-    protected static void validatePeriods(List<FreezeInterval> periods) {
-        List<FreezeInterval> allPeriods = FreezeInterval.canonicalizeIntervals(periods);
+    static void validatePeriods(List<FreezePeriod> periods) {
+        List<FreezePeriod> allPeriods = FreezePeriod.canonicalizePeriods(periods);
         if (allPeriods.size() != periods.size()) {
             throw SystemUpdatePolicy.ValidationFailedException.duplicateOrOverlapPeriods();
         }
         for (int i = 0; i < allPeriods.size(); i++) {
-            FreezeInterval current = allPeriods.get(i);
+            FreezePeriod current = allPeriods.get(i);
             if (current.getLength() > SystemUpdatePolicy.FREEZE_PERIOD_MAX_LENGTH) {
                 throw SystemUpdatePolicy.ValidationFailedException.freezePeriodTooLong("Freeze "
                         + "period " + current + " is too long: " + current.getLength() + " days");
             }
-            FreezeInterval previous = i > 0 ? allPeriods.get(i - 1)
+            FreezePeriod previous = i > 0 ? allPeriods.get(i - 1)
                     : allPeriods.get(allPeriods.size() - 1);
             if (previous != current) {
                 final int separation;
@@ -247,7 +299,7 @@
      *
      * @hide
      */
-    protected static void validateAgainstPreviousFreezePeriod(List<FreezeInterval> periods,
+    static void validateAgainstPreviousFreezePeriod(List<FreezePeriod> periods,
             LocalDate prevPeriodStart, LocalDate prevPeriodEnd, LocalDate now) {
         if (periods.size() == 0 || prevPeriodStart == null || prevPeriodEnd == null) {
             return;
@@ -258,14 +310,14 @@
             // Clock was adjusted backwards. We can continue execution though, the separation
             // and length validation below still works under this condition.
         }
-        List<FreezeInterval> allPeriods = FreezeInterval.canonicalizeIntervals(periods);
+        List<FreezePeriod> allPeriods = FreezePeriod.canonicalizePeriods(periods);
         // Given current time now, find the freeze period that's either current, or the one
         // that's immediately afterwards. For the later case, it might be after the year-end,
         // but this can only happen if there is only one freeze period.
-        FreezeInterval curOrNextFreezePeriod = allPeriods.get(0);
-        for (FreezeInterval interval : allPeriods) {
+        FreezePeriod curOrNextFreezePeriod = allPeriods.get(0);
+        for (FreezePeriod interval : allPeriods) {
             if (interval.contains(now)
-                    || interval.mStartDay > FreezeInterval.dayOfYearDisregardLeapYear(now)) {
+                    || interval.mStartDay > FreezePeriod.dayOfYearDisregardLeapYear(now)) {
                 curOrNextFreezePeriod = interval;
                 break;
             }
@@ -282,7 +334,7 @@
         // Now validate [prevPeriodStart, prevPeriodEnd] against curOrNextFreezeDates
         final String periodsDescription = "Prev: " + prevPeriodStart + "," + prevPeriodEnd
                 + "; cur: " + curOrNextFreezeDates.first + "," + curOrNextFreezeDates.second;
-        long separation = FreezeInterval.distanceWithoutLeapYear(curOrNextFreezeDates.first,
+        long separation = FreezePeriod.distanceWithoutLeapYear(curOrNextFreezeDates.first,
                 prevPeriodEnd) - 1;
         if (separation > 0) {
             // Two intervals do not overlap, check separation
@@ -292,7 +344,7 @@
             }
         } else {
             // Two intervals overlap, check combined length
-            long length = FreezeInterval.distanceWithoutLeapYear(curOrNextFreezeDates.second,
+            long length = FreezePeriod.distanceWithoutLeapYear(curOrNextFreezeDates.second,
                     prevPeriodStart) + 1;
             if (length > SystemUpdatePolicy.FREEZE_PERIOD_MAX_LENGTH) {
                 throw ValidationFailedException.combinedPeriodTooLong("Combined freeze period "
diff --git a/android/app/admin/SystemUpdatePolicy.java b/android/app/admin/SystemUpdatePolicy.java
index 47b3a81..20eef6c 100644
--- a/android/app/admin/SystemUpdatePolicy.java
+++ b/android/app/admin/SystemUpdatePolicy.java
@@ -38,9 +38,11 @@
 import java.time.LocalDate;
 import java.time.LocalDateTime;
 import java.time.LocalTime;
+import java.time.MonthDay;
 import java.time.ZoneId;
 import java.util.ArrayList;
 import java.util.Calendar;
+import java.util.Collections;
 import java.util.List;
 import java.util.concurrent.TimeUnit;
 import java.util.stream.Collectors;
@@ -51,7 +53,7 @@
  * @see DevicePolicyManager#setSystemUpdatePolicy
  * @see DevicePolicyManager#getSystemUpdatePolicy
  */
-public class SystemUpdatePolicy implements Parcelable {
+public final class SystemUpdatePolicy implements Parcelable {
     private static final String TAG = "SystemUpdatePolicy";
 
     /** @hide */
@@ -163,6 +165,7 @@
                 ERROR_NEW_FREEZE_PERIOD_TOO_CLOSE,
                 ERROR_COMBINED_FREEZE_PERIOD_TOO_LONG,
                 ERROR_COMBINED_FREEZE_PERIOD_TOO_CLOSE,
+                ERROR_UNKNOWN,
         })
         @Retention(RetentionPolicy.SOURCE)
         @interface ValidationFailureType {}
@@ -171,33 +174,38 @@
         public static final int ERROR_NONE = 0;
 
         /**
+         * Validation failed with unknown error.
+         */
+        public static final int ERROR_UNKNOWN = 1;
+
+        /**
          * The freeze periods contains duplicates, periods that overlap with each
          * other or periods whose start and end joins.
          */
-        public static final int ERROR_DUPLICATE_OR_OVERLAP = 1;
+        public static final int ERROR_DUPLICATE_OR_OVERLAP = 2;
 
         /**
          * There exists at least one freeze period whose length exceeds 90 days.
          */
-        public static final int ERROR_NEW_FREEZE_PERIOD_TOO_LONG = 2;
+        public static final int ERROR_NEW_FREEZE_PERIOD_TOO_LONG = 3;
 
         /**
          * There exists some freeze period which starts within 60 days of the preceding period's
          * end time.
          */
-        public static final int ERROR_NEW_FREEZE_PERIOD_TOO_CLOSE = 3;
+        public static final int ERROR_NEW_FREEZE_PERIOD_TOO_CLOSE = 4;
 
         /**
          * The device has been in a freeze period and when combining with the new freeze period
          * to be set, it will result in the total freeze period being longer than 90 days.
          */
-        public static final int ERROR_COMBINED_FREEZE_PERIOD_TOO_LONG = 4;
+        public static final int ERROR_COMBINED_FREEZE_PERIOD_TOO_LONG = 5;
 
         /**
          * The device has been in a freeze period and some new freeze period to be set is less
          * than 60 days from the end of the last freeze period the device went through.
          */
-        public static final int ERROR_COMBINED_FREEZE_PERIOD_TOO_CLOSE = 5;
+        public static final int ERROR_COMBINED_FREEZE_PERIOD_TOO_CLOSE = 6;
 
         @ValidationFailureType
         private final int mErrorCode;
@@ -272,7 +280,7 @@
     private int mMaintenanceWindowStart;
     private int mMaintenanceWindowEnd;
 
-    private final ArrayList<FreezeInterval> mFreezePeriods;
+    private final ArrayList<FreezePeriod> mFreezePeriods;
 
     private SystemUpdatePolicy() {
         mPolicyType = TYPE_UNKNOWN;
@@ -444,12 +452,10 @@
      *         requirement set above
      * @return this instance
      */
-    public SystemUpdatePolicy setFreezePeriods(List<Pair<Integer, Integer>> freezePeriods) {
-        List<FreezeInterval> newPeriods = freezePeriods.stream().map(
-                p -> new FreezeInterval(p.first, p.second)).collect(Collectors.toList());
-        FreezeInterval.validatePeriods(newPeriods);
+    public SystemUpdatePolicy setFreezePeriods(List<FreezePeriod> freezePeriods) {
+        FreezePeriod.validatePeriods(freezePeriods);
         mFreezePeriods.clear();
-        mFreezePeriods.addAll(newPeriods);
+        mFreezePeriods.addAll(freezePeriods);
         return this;
     }
 
@@ -458,12 +464,8 @@
      *
      * @return the list of freeze periods, or an empty list if none was set.
      */
-    public List<Pair<Integer, Integer>> getFreezePeriods() {
-        List<Pair<Integer, Integer>> result = new ArrayList<>(mFreezePeriods.size());
-        for (FreezeInterval interval : mFreezePeriods) {
-            result.add(new Pair<>(interval.mStartDay, interval.mEndDay));
-        }
-        return result;
+    public List<FreezePeriod> getFreezePeriods() {
+        return Collections.unmodifiableList(mFreezePeriods);
     }
 
     /**
@@ -472,7 +474,7 @@
      * @hide
      */
     public Pair<LocalDate, LocalDate> getCurrentFreezePeriod(LocalDate now) {
-        for (FreezeInterval interval : mFreezePeriods) {
+        for (FreezePeriod interval : mFreezePeriods) {
             if (interval.contains(now)) {
                 return interval.toCurrentOrFutureRealDates(now);
             }
@@ -485,10 +487,10 @@
      * is not within a freeze period.
      */
     private long timeUntilNextFreezePeriod(long now) {
-        List<FreezeInterval> sortedPeriods = FreezeInterval.canonicalizeIntervals(mFreezePeriods);
+        List<FreezePeriod> sortedPeriods = FreezePeriod.canonicalizePeriods(mFreezePeriods);
         LocalDate nowDate = millisToDate(now);
         LocalDate nextFreezeStart = null;
-        for (FreezeInterval interval : sortedPeriods) {
+        for (FreezePeriod interval : sortedPeriods) {
             if (interval.after(nowDate)) {
                 nextFreezeStart = interval.toCurrentOrFutureRealDates(nowDate).first;
                 break;
@@ -506,13 +508,13 @@
 
     /** @hide */
     public void validateFreezePeriods() {
-        FreezeInterval.validatePeriods(mFreezePeriods);
+        FreezePeriod.validatePeriods(mFreezePeriods);
     }
 
     /** @hide */
     public void validateAgainstPreviousFreezePeriod(LocalDate prevPeriodStart,
             LocalDate prevPeriodEnd, LocalDate now) {
-        FreezeInterval.validateAgainstPreviousFreezePeriod(mFreezePeriods, prevPeriodStart,
+        FreezePeriod.validateAgainstPreviousFreezePeriod(mFreezePeriods, prevPeriodStart,
                 prevPeriodEnd, now);
     }
 
@@ -521,10 +523,10 @@
      * updates and how long this action is valid for, given the current system update policy. Its
      * action could be one of the following
      * <ul>
-     * <li> {@code TYPE_INSTALL_AUTOMATIC} system updates should be installed immedately and without
-     * user intervention as soon as they become available.
-     * <li> {@code TYPE_POSTPONE} system updates should be postponed for a maximum of 30 days
-     * <li> {@code TYPE_PAUSE} system updates should be postponed indefinitely until further notice
+     * <li> {@link #TYPE_INSTALL_AUTOMATIC} system updates should be installed immedately and
+     * without user intervention as soon as they become available.
+     * <li> {@link #TYPE_POSTPONE} system updates should be postponed for a maximum of 30 days
+     * <li> {@link #TYPE_PAUSE} system updates should be postponed indefinitely until further notice
      * </ul>
      *
      * The effective time measures how long this installation option is valid for from the queried
@@ -535,18 +537,38 @@
      */
     @SystemApi
     public static class InstallationOption {
+        /** @hide */
+        @IntDef(prefix = { "TYPE_" }, value = {
+                TYPE_INSTALL_AUTOMATIC,
+                TYPE_PAUSE,
+                TYPE_POSTPONE
+        })
+        @Retention(RetentionPolicy.SOURCE)
+        @interface InstallationOptionType {}
+
+        @InstallationOptionType
         private final int mType;
         private long mEffectiveTime;
 
-        InstallationOption(int type, long effectiveTime) {
+        InstallationOption(@InstallationOptionType int type, long effectiveTime) {
             this.mType = type;
             this.mEffectiveTime = effectiveTime;
         }
 
-        public int getType() {
+        /**
+         * Returns the type of the current installation option, could be one of
+         * {@link #TYPE_INSTALL_AUTOMATIC}, {@link #TYPE_POSTPONE} and {@link #TYPE_PAUSE}.
+         * @return type of installation option.
+         */
+        public @InstallationOptionType int getType() {
             return mType;
         }
 
+        /**
+         * Returns how long the current installation option in effective for, starting from the time
+         * of query.
+         * @return the effective time in milliseconds.
+         */
         public long getEffectiveTime() {
             return mEffectiveTime;
         }
@@ -667,9 +689,11 @@
         int freezeCount = mFreezePeriods.size();
         dest.writeInt(freezeCount);
         for (int i = 0; i < freezeCount; i++) {
-            FreezeInterval interval = mFreezePeriods.get(i);
-            dest.writeInt(interval.mStartDay);
-            dest.writeInt(interval.mEndDay);
+            FreezePeriod interval = mFreezePeriods.get(i);
+            dest.writeInt(interval.getStart().getMonthValue());
+            dest.writeInt(interval.getStart().getDayOfMonth());
+            dest.writeInt(interval.getEnd().getMonthValue());
+            dest.writeInt(interval.getEnd().getDayOfMonth());
         }
     }
 
@@ -686,8 +710,9 @@
                     int freezeCount = source.readInt();
                     policy.mFreezePeriods.ensureCapacity(freezeCount);
                     for (int i = 0; i < freezeCount; i++) {
-                        policy.mFreezePeriods.add(
-                                new FreezeInterval(source.readInt(), source.readInt()));
+                        MonthDay start = MonthDay.of(source.readInt(), source.readInt());
+                        MonthDay end = MonthDay.of(source.readInt(), source.readInt());
+                        policy.mFreezePeriods.add(new FreezePeriod(start, end));
                     }
                     return policy;
                 }
@@ -730,9 +755,9 @@
                     if (!parser.getName().equals(KEY_FREEZE_TAG)) {
                         continue;
                     }
-                    policy.mFreezePeriods.add(new FreezeInterval(
-                            Integer.parseInt(parser.getAttributeValue(null, KEY_FREEZE_START)),
-                            Integer.parseInt(parser.getAttributeValue(null, KEY_FREEZE_END))));
+                    policy.mFreezePeriods.add(new FreezePeriod(
+                            MonthDay.parse(parser.getAttributeValue(null, KEY_FREEZE_START)),
+                            MonthDay.parse(parser.getAttributeValue(null, KEY_FREEZE_END))));
                 }
                 return policy;
             }
@@ -751,10 +776,10 @@
         out.attribute(null, KEY_INSTALL_WINDOW_START, Integer.toString(mMaintenanceWindowStart));
         out.attribute(null, KEY_INSTALL_WINDOW_END, Integer.toString(mMaintenanceWindowEnd));
         for (int i = 0; i < mFreezePeriods.size(); i++) {
-            FreezeInterval interval = mFreezePeriods.get(i);
+            FreezePeriod interval = mFreezePeriods.get(i);
             out.startTag(null, KEY_FREEZE_TAG);
-            out.attribute(null, KEY_FREEZE_START, Integer.toString(interval.mStartDay));
-            out.attribute(null, KEY_FREEZE_END, Integer.toString(interval.mEndDay));
+            out.attribute(null, KEY_FREEZE_START, interval.getStart().toString());
+            out.attribute(null, KEY_FREEZE_END, interval.getEnd().toString());
             out.endTag(null, KEY_FREEZE_TAG);
         }
     }
diff --git a/android/app/slice/Slice.java b/android/app/slice/Slice.java
index bf3398a..4336f18 100644
--- a/android/app/slice/Slice.java
+++ b/android/app/slice/Slice.java
@@ -21,19 +21,13 @@
 import android.annotation.StringDef;
 import android.app.PendingIntent;
 import android.app.RemoteInput;
-import android.content.ContentResolver;
-import android.content.Context;
-import android.content.IContentProvider;
-import android.content.Intent;
 import android.graphics.drawable.Icon;
 import android.net.Uri;
 import android.os.Bundle;
 import android.os.Parcel;
 import android.os.Parcelable;
-import android.os.RemoteException;
 
 import com.android.internal.util.ArrayUtils;
-import com.android.internal.util.Preconditions;
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
@@ -575,45 +569,4 @@
         }
         return sb.toString();
     }
-
-    /**
-     * @deprecated TO BE REMOVED.
-     */
-    @Deprecated
-    public static @Nullable Slice bindSlice(ContentResolver resolver,
-            @NonNull Uri uri, @NonNull List<SliceSpec> supportedSpecs) {
-        Preconditions.checkNotNull(uri, "uri");
-        IContentProvider provider = resolver.acquireProvider(uri);
-        if (provider == null) {
-            throw new IllegalArgumentException("Unknown URI " + uri);
-        }
-        try {
-            Bundle extras = new Bundle();
-            extras.putParcelable(SliceProvider.EXTRA_BIND_URI, uri);
-            extras.putParcelableArrayList(SliceProvider.EXTRA_SUPPORTED_SPECS,
-                    new ArrayList<>(supportedSpecs));
-            final Bundle res = provider.call(resolver.getPackageName(), SliceProvider.METHOD_SLICE,
-                    null, extras);
-            Bundle.setDefusable(res, true);
-            if (res == null) {
-                return null;
-            }
-            return res.getParcelable(SliceProvider.EXTRA_SLICE);
-        } catch (RemoteException e) {
-            // Arbitrary and not worth documenting, as Activity
-            // Manager will kill this process shortly anyway.
-            return null;
-        } finally {
-            resolver.releaseProvider(provider);
-        }
-    }
-
-    /**
-     * @deprecated TO BE REMOVED.
-     */
-    @Deprecated
-    public static @Nullable Slice bindSlice(Context context, @NonNull Intent intent,
-            @NonNull List<SliceSpec> supportedSpecs) {
-        return context.getSystemService(SliceManager.class).bindSlice(intent, supportedSpecs);
-    }
 }
diff --git a/android/app/slice/SliceManager.java b/android/app/slice/SliceManager.java
index 0285e9f..ad49437 100644
--- a/android/app/slice/SliceManager.java
+++ b/android/app/slice/SliceManager.java
@@ -16,6 +16,8 @@
 
 package android.app.slice;
 
+import static android.content.pm.PackageManager.PERMISSION_DENIED;
+
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.SdkConstant;
@@ -38,6 +40,7 @@
 import android.os.ServiceManager;
 import android.os.ServiceManager.ServiceNotFoundException;
 import android.os.UserHandle;
+import android.util.ArraySet;
 import android.util.Log;
 
 import com.android.internal.util.Preconditions;
@@ -47,6 +50,7 @@
 import java.util.Collection;
 import java.util.Collections;
 import java.util.List;
+import java.util.Set;
 
 /**
  * Class to handle interactions with {@link Slice}s.
@@ -101,22 +105,6 @@
     private final IBinder mToken = new Binder();
 
     /**
-     * Permission denied.
-     * @hide
-     */
-    public static final int PERMISSION_DENIED = -1;
-    /**
-     * Permission granted.
-     * @hide
-     */
-    public static final int PERMISSION_GRANTED = 0;
-    /**
-     * Permission just granted by the user, and should be granted uri permission as well.
-     * @hide
-     */
-    public static final int PERMISSION_USER_GRANTED = 1;
-
-    /**
      * @hide
      */
     public SliceManager(Context context, Handler handler) throws ServiceNotFoundException {
@@ -140,7 +128,7 @@
      * @see Intent#ACTION_ASSIST
      * @see Intent#CATEGORY_HOME
      */
-    public void pinSlice(@NonNull Uri uri, @NonNull List<SliceSpec> specs) {
+    public void pinSlice(@NonNull Uri uri, @NonNull Set<SliceSpec> specs) {
         try {
             mService.pinSlice(mContext.getPackageName(), uri,
                     specs.toArray(new SliceSpec[specs.size()]), mToken);
@@ -150,6 +138,14 @@
     }
 
     /**
+     * @deprecated TO BE REMOVED
+     */
+    @Deprecated
+    public void pinSlice(@NonNull Uri uri, @NonNull List<SliceSpec> specs) {
+        pinSlice(uri, new ArraySet<>(specs));
+    }
+
+    /**
      * Remove a pin for a slice.
      * <p>
      * If the slice has no other pins/callbacks then the slice will be unpinned.
@@ -189,9 +185,10 @@
      * into account all clients and returns only specs supported by all.
      * @see SliceSpec
      */
-    public @NonNull List<SliceSpec> getPinnedSpecs(Uri uri) {
+    public @NonNull Set<SliceSpec> getPinnedSpecs(Uri uri) {
         try {
-            return Arrays.asList(mService.getPinnedSpecs(uri, mContext.getPackageName()));
+            return new ArraySet<>(Arrays.asList(mService.getPinnedSpecs(uri,
+                    mContext.getPackageName())));
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -240,7 +237,7 @@
      * @return The Slice provided by the app or null if none is given.
      * @see Slice
      */
-    public @Nullable Slice bindSlice(@NonNull Uri uri, @NonNull List<SliceSpec> supportedSpecs) {
+    public @Nullable Slice bindSlice(@NonNull Uri uri, @NonNull Set<SliceSpec> supportedSpecs) {
         Preconditions.checkNotNull(uri, "uri");
         ContentResolver resolver = mContext.getContentResolver();
         try (ContentProviderClient provider = resolver.acquireContentProviderClient(uri)) {
@@ -265,6 +262,14 @@
     }
 
     /**
+     * @deprecated TO BE REMOVED
+     */
+    @Deprecated
+    public @Nullable Slice bindSlice(@NonNull Uri uri, @NonNull List<SliceSpec> supportedSpecs) {
+        return bindSlice(uri, new ArraySet<>(supportedSpecs));
+    }
+
+    /**
      * Turns a slice intent into a slice uri. Expects an explicit intent.
      * <p>
      * This goes through a several stage resolution process to determine if any slice
@@ -272,12 +277,12 @@
      * <ol>
      *  <li> If the intent contains data that {@link ContentResolver#getType} is
      *  {@link SliceProvider#SLICE_TYPE} then the data will be returned.</li>
-     *  <li>If the intent with {@link #CATEGORY_SLICE} added resolves to a provider, then
-     *  the provider will be asked to {@link SliceProvider#onMapIntentToUri} and that result
-     *  will be returned.</li>
-     *  <li>Lastly, if the intent explicitly points at an activity, and that activity has
+     *  <li>If the intent explicitly points at an activity, and that activity has
      *  meta-data for key {@link #SLICE_METADATA_KEY}, then the Uri specified there will be
      *  returned.</li>
+     *  <li>Lastly, if the intent with {@link #CATEGORY_SLICE} added resolves to a provider, then
+     *  the provider will be asked to {@link SliceProvider#onMapIntentToUri} and that result
+     *  will be returned.</li>
      *  <li>If no slice is found, then {@code null} is returned.</li>
      * </ol>
      * @param intent The intent associated with a slice.
@@ -287,37 +292,12 @@
      * @see Intent
      */
     public @Nullable Uri mapIntentToUri(@NonNull Intent intent) {
-        Preconditions.checkNotNull(intent, "intent");
-        Preconditions.checkArgument(intent.getComponent() != null || intent.getPackage() != null
-                || intent.getData() != null,
-                "Slice intent must be explicit %s", intent);
         ContentResolver resolver = mContext.getContentResolver();
-
-        // Check if the intent has data for the slice uri on it and use that
-        final Uri intentData = intent.getData();
-        if (intentData != null && SliceProvider.SLICE_TYPE.equals(resolver.getType(intentData))) {
-            return intentData;
-        }
+        final Uri staticUri = resolveStatic(intent, resolver);
+        if (staticUri != null) return staticUri;
         // Otherwise ask the app
-        Intent queryIntent = new Intent(intent);
-        if (!queryIntent.hasCategory(CATEGORY_SLICE)) {
-            queryIntent.addCategory(CATEGORY_SLICE);
-        }
-        List<ResolveInfo> providers =
-                mContext.getPackageManager().queryIntentContentProviders(queryIntent, 0);
-        if (providers == null || providers.isEmpty()) {
-            // There are no providers, see if this activity has a direct link.
-            ResolveInfo resolve = mContext.getPackageManager().resolveActivity(intent,
-                    PackageManager.GET_META_DATA);
-            if (resolve != null && resolve.activityInfo != null
-                    && resolve.activityInfo.metaData != null
-                    && resolve.activityInfo.metaData.containsKey(SLICE_METADATA_KEY)) {
-                return Uri.parse(
-                        resolve.activityInfo.metaData.getString(SLICE_METADATA_KEY));
-            }
-            return null;
-        }
-        String authority = providers.get(0).providerInfo.authority;
+        String authority = getAuthority(intent);
+        if (authority == null) return null;
         Uri uri = new Uri.Builder().scheme(ContentResolver.SCHEME_CONTENT)
                 .authority(authority).build();
         try (ContentProviderClient provider = resolver.acquireContentProviderClient(uri)) {
@@ -338,10 +318,43 @@
         }
     }
 
+    private String getAuthority(Intent intent) {
+        Intent queryIntent = new Intent(intent);
+        if (!queryIntent.hasCategory(CATEGORY_SLICE)) {
+            queryIntent.addCategory(CATEGORY_SLICE);
+        }
+        List<ResolveInfo> providers =
+                mContext.getPackageManager().queryIntentContentProviders(queryIntent, 0);
+        return providers != null && !providers.isEmpty() ? providers.get(0).providerInfo.authority
+                : null;
+    }
+
+    private Uri resolveStatic(@NonNull Intent intent, ContentResolver resolver) {
+        Preconditions.checkNotNull(intent, "intent");
+        Preconditions.checkArgument(intent.getComponent() != null || intent.getPackage() != null
+                || intent.getData() != null,
+                "Slice intent must be explicit %s", intent);
+
+        // Check if the intent has data for the slice uri on it and use that
+        final Uri intentData = intent.getData();
+        if (intentData != null && SliceProvider.SLICE_TYPE.equals(resolver.getType(intentData))) {
+            return intentData;
+        }
+        // There are no providers, see if this activity has a direct link.
+        ResolveInfo resolve = mContext.getPackageManager().resolveActivity(intent,
+                PackageManager.GET_META_DATA);
+        if (resolve != null && resolve.activityInfo != null
+                && resolve.activityInfo.metaData != null
+                && resolve.activityInfo.metaData.containsKey(SLICE_METADATA_KEY)) {
+            return Uri.parse(
+                    resolve.activityInfo.metaData.getString(SLICE_METADATA_KEY));
+        }
+        return null;
+    }
+
     /**
-     * Turns a slice intent into slice content. Expects an explicit intent. If there is no
-     * {@link android.content.ContentProvider} associated with the given intent this will throw
-     * {@link IllegalArgumentException}.
+     * Turns a slice intent into slice content. Is a shortcut to perform the action
+     * of both {@link #mapIntentToUri(Intent)} and {@link #bindSlice(Uri, List)} at once.
      *
      * @param intent The intent associated with a slice.
      * @param supportedSpecs List of supported specs.
@@ -351,34 +364,17 @@
      * @see Intent
      */
     public @Nullable Slice bindSlice(@NonNull Intent intent,
-            @NonNull List<SliceSpec> supportedSpecs) {
+            @NonNull Set<SliceSpec> supportedSpecs) {
         Preconditions.checkNotNull(intent, "intent");
         Preconditions.checkArgument(intent.getComponent() != null || intent.getPackage() != null
                 || intent.getData() != null,
                 "Slice intent must be explicit %s", intent);
         ContentResolver resolver = mContext.getContentResolver();
-
-        // Check if the intent has data for the slice uri on it and use that
-        final Uri intentData = intent.getData();
-        if (intentData != null && SliceProvider.SLICE_TYPE.equals(resolver.getType(intentData))) {
-            return bindSlice(intentData, supportedSpecs);
-        }
+        final Uri staticUri = resolveStatic(intent, resolver);
+        if (staticUri != null) return bindSlice(staticUri, supportedSpecs);
         // Otherwise ask the app
-        List<ResolveInfo> providers =
-                mContext.getPackageManager().queryIntentContentProviders(intent, 0);
-        if (providers == null || providers.isEmpty()) {
-            // There are no providers, see if this activity has a direct link.
-            ResolveInfo resolve = mContext.getPackageManager().resolveActivity(intent,
-                    PackageManager.GET_META_DATA);
-            if (resolve != null && resolve.activityInfo != null
-                    && resolve.activityInfo.metaData != null
-                    && resolve.activityInfo.metaData.containsKey(SLICE_METADATA_KEY)) {
-                return bindSlice(Uri.parse(resolve.activityInfo.metaData
-                        .getString(SLICE_METADATA_KEY)), supportedSpecs);
-            }
-            return null;
-        }
-        String authority = providers.get(0).providerInfo.authority;
+        String authority = getAuthority(intent);
+        if (authority == null) return null;
         Uri uri = new Uri.Builder().scheme(ContentResolver.SCHEME_CONTENT)
                 .authority(authority).build();
         try (ContentProviderClient provider = resolver.acquireContentProviderClient(uri)) {
@@ -387,8 +383,6 @@
             }
             Bundle extras = new Bundle();
             extras.putParcelable(SliceProvider.EXTRA_INTENT, intent);
-            extras.putParcelableArrayList(SliceProvider.EXTRA_SUPPORTED_SPECS,
-                    new ArrayList<>(supportedSpecs));
             final Bundle res = provider.call(SliceProvider.METHOD_MAP_INTENT, null, extras);
             if (res == null) {
                 return null;
@@ -402,6 +396,16 @@
     }
 
     /**
+     * @deprecated TO BE REMOVED.
+     */
+    @Deprecated
+    @Nullable
+    public Slice bindSlice(@NonNull Intent intent,
+            @NonNull List<SliceSpec> supportedSpecs) {
+        return bindSlice(intent, new ArraySet<>(supportedSpecs));
+    }
+
+    /**
      * Determine whether a particular process and user ID has been granted
      * permission to access a specific slice URI.
      *
@@ -417,9 +421,11 @@
      * @see #grantSlicePermission(String, Uri)
      */
     public @PermissionResult int checkSlicePermission(@NonNull Uri uri, int pid, int uid) {
-        // TODO: Switch off Uri permissions.
-        return mContext.checkUriPermission(uri, pid, uid,
-                Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
+        try {
+            return mService.checkSlicePermission(uri, null, pid, uid, null);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
     }
 
     /**
@@ -431,11 +437,11 @@
      * @see #revokeSlicePermission
      */
     public void grantSlicePermission(@NonNull String toPackage, @NonNull Uri uri) {
-        // TODO: Switch off Uri permissions.
-        mContext.grantUriPermission(toPackage, uri,
-                Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION
-                        | Intent.FLAG_GRANT_WRITE_URI_PERMISSION
-                        | Intent.FLAG_GRANT_PREFIX_URI_PERMISSION);
+        try {
+            mService.grantSlicePermission(mContext.getPackageName(), toPackage, uri);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
     }
 
     /**
@@ -453,11 +459,11 @@
      * @see #grantSlicePermission
      */
     public void revokeSlicePermission(@NonNull String toPackage, @NonNull Uri uri) {
-        // TODO: Switch off Uri permissions.
-        mContext.revokeUriPermission(toPackage, uri,
-                Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION
-                        | Intent.FLAG_GRANT_WRITE_URI_PERMISSION
-                        | Intent.FLAG_GRANT_PREFIX_URI_PERMISSION);
+        try {
+            mService.revokeSlicePermission(mContext.getPackageName(), toPackage, uri);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
     }
 
     /**
@@ -478,16 +484,6 @@
                 throw new SecurityException("User " + uid + " does not have slice permission for "
                         + uri + ".");
             }
-            if (result == PERMISSION_USER_GRANTED) {
-                // We just had a user grant of this permission and need to grant this to the app
-                // permanently.
-                mContext.grantUriPermission(pkg, uri.buildUpon().path("").build(),
-                        Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION
-                                | Intent.FLAG_GRANT_WRITE_URI_PERMISSION
-                                | Intent.FLAG_GRANT_PREFIX_URI_PERMISSION);
-                // Notify a change has happened because we just granted a permission.
-                mContext.getContentResolver().notifyChange(uri, null);
-            }
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
diff --git a/android/app/slice/SliceProvider.java b/android/app/slice/SliceProvider.java
index fe5742d..d369272 100644
--- a/android/app/slice/SliceProvider.java
+++ b/android/app/slice/SliceProvider.java
@@ -44,6 +44,7 @@
 import java.util.Collection;
 import java.util.Collections;
 import java.util.List;
+import java.util.Set;
 
 /**
  * A SliceProvider allows an app to provide content to be displayed in system spaces. This content
@@ -197,6 +198,14 @@
      * @see {@link Slice}.
      * @see {@link Slice#HINT_PARTIAL}
      */
+    public Slice onBindSlice(Uri sliceUri, Set<SliceSpec> supportedSpecs) {
+        return onBindSlice(sliceUri, new ArrayList<>(supportedSpecs));
+    }
+
+    /**
+     * @deprecated TO BE REMOVED
+     */
+    @Deprecated
     public Slice onBindSlice(Uri sliceUri, List<SliceSpec> supportedSpecs) {
         return null;
     }
diff --git a/android/app/usage/NetworkStats.java b/android/app/usage/NetworkStats.java
index 7252f02..216a4a0 100644
--- a/android/app/usage/NetworkStats.java
+++ b/android/app/usage/NetworkStats.java
@@ -237,20 +237,26 @@
                 DEFAULT_NETWORK_YES
         })
         @Retention(RetentionPolicy.SOURCE)
-        public @interface DefaultNetwork {}
+        public @interface DefaultNetworkStatus {}
 
         /**
-         * Combined usage for this network regardless of whether it was the active default network.
+         * Combined usage for this network regardless of default network status.
          */
         public static final int DEFAULT_NETWORK_ALL = -1;
 
         /**
-         * Usage that occurs while this network is not the active default network.
+         * Usage that occurs while this network is not a default network.
+         *
+         * <p>This implies that the app responsible for this usage requested that it occur on a
+         * specific network different from the one(s) the system would have selected for it.
          */
         public static final int DEFAULT_NETWORK_NO = 0x1;
 
         /**
-         * Usage that occurs while this network is the active default network.
+         * Usage that occurs while this network is a default network.
+         *
+         * <p>This implies that the app either did not select a specific network for this usage,
+         * or it selected a network that the system could have selected for app traffic.
          */
         public static final int DEFAULT_NETWORK_YES = 0x2;
 
@@ -262,7 +268,7 @@
         private int mUid;
         private int mTag;
         private int mState;
-        private int mDefaultNetwork;
+        private int mDefaultNetworkStatus;
         private int mMetered;
         private int mRoaming;
         private long mBeginTimeStamp;
@@ -323,8 +329,9 @@
             return 0;
         }
 
-        private static @DefaultNetwork int convertDefaultNetwork(int defaultNetwork) {
-            switch (defaultNetwork) {
+        private static @DefaultNetworkStatus int convertDefaultNetworkStatus(
+                int defaultNetworkStatus) {
+            switch (defaultNetworkStatus) {
                 case android.net.NetworkStats.DEFAULT_NETWORK_ALL : return DEFAULT_NETWORK_ALL;
                 case android.net.NetworkStats.DEFAULT_NETWORK_NO: return DEFAULT_NETWORK_NO;
                 case android.net.NetworkStats.DEFAULT_NETWORK_YES: return DEFAULT_NETWORK_YES;
@@ -397,18 +404,15 @@
         }
 
         /**
-         * Default network state. One of the following values:<p/>
+         * Default network status. One of the following values:<p/>
          * <ul>
          * <li>{@link #DEFAULT_NETWORK_ALL}</li>
          * <li>{@link #DEFAULT_NETWORK_NO}</li>
          * <li>{@link #DEFAULT_NETWORK_YES}</li>
          * </ul>
-         * <p>Indicates whether the network usage occurred on the system default network for this
-         * type of traffic, or whether the application chose to send this traffic on a network that
-         * was not the one selected by the system.
          */
-        public @DefaultNetwork int getDefaultNetwork() {
-            return mDefaultNetwork;
+        public @DefaultNetworkStatus int getDefaultNetworkStatus() {
+            return mDefaultNetworkStatus;
         }
 
         /**
@@ -605,7 +609,7 @@
         bucketOut.mUid = Bucket.convertUid(mRecycledSummaryEntry.uid);
         bucketOut.mTag = Bucket.convertTag(mRecycledSummaryEntry.tag);
         bucketOut.mState = Bucket.convertState(mRecycledSummaryEntry.set);
-        bucketOut.mDefaultNetwork = Bucket.convertDefaultNetwork(
+        bucketOut.mDefaultNetworkStatus = Bucket.convertDefaultNetworkStatus(
                 mRecycledSummaryEntry.defaultNetwork);
         bucketOut.mMetered = Bucket.convertMetered(mRecycledSummaryEntry.metered);
         bucketOut.mRoaming = Bucket.convertRoaming(mRecycledSummaryEntry.roaming);
@@ -657,7 +661,7 @@
                 bucketOut.mUid = Bucket.convertUid(getUid());
                 bucketOut.mTag = Bucket.convertTag(mTag);
                 bucketOut.mState = mState;
-                bucketOut.mDefaultNetwork = Bucket.DEFAULT_NETWORK_ALL;
+                bucketOut.mDefaultNetworkStatus = Bucket.DEFAULT_NETWORK_ALL;
                 bucketOut.mMetered = Bucket.METERED_ALL;
                 bucketOut.mRoaming = Bucket.ROAMING_ALL;
                 bucketOut.mBeginTimeStamp = mRecycledHistoryEntry.bucketStart;
diff --git a/android/app/usage/NetworkStatsManager.java b/android/app/usage/NetworkStatsManager.java
index b2fe958..0b21196 100644
--- a/android/app/usage/NetworkStatsManager.java
+++ b/android/app/usage/NetworkStatsManager.java
@@ -35,6 +35,7 @@
 import android.os.RemoteException;
 import android.os.ServiceManager;
 import android.os.ServiceManager.ServiceNotFoundException;
+import android.util.DataUnit;
 import android.util.Log;
 
 import com.android.internal.annotations.VisibleForTesting;
@@ -95,6 +96,15 @@
     /** @hide */
     public static final int CALLBACK_RELEASED = 1;
 
+    /**
+     * Minimum data usage threshold for registering usage callbacks.
+     *
+     * Requests registered with a threshold lower than this will only be triggered once this minimum
+     * is reached.
+     * @hide
+     */
+    public static final long MIN_THRESHOLD_BYTES = DataUnit.MEBIBYTES.toBytes(2);
+
     private final Context mContext;
     private final INetworkStatsService mService;
 
@@ -305,6 +315,8 @@
      *            {@link java.lang.System#currentTimeMillis}.
      * @param uid UID of app
      * @param tag TAG of interest. Use {@link NetworkStats.Bucket#TAG_NONE} for no tags.
+     * @param state state of interest. Use {@link NetworkStats.Bucket#STATE_ALL} to aggregate
+     *            traffic from all states.
      * @return Statistics object or null if an error happened during statistics collection.
      * @throws SecurityException if permissions are insufficient to read network statistics.
      */
diff --git a/android/app/usage/TimeSparseArray.java b/android/app/usage/TimeSparseArray.java
index 9ef88e4..4ec0e9e 100644
--- a/android/app/usage/TimeSparseArray.java
+++ b/android/app/usage/TimeSparseArray.java
@@ -88,7 +88,7 @@
                 key++;
                 keyIndex++;
             }
-            if (key >= origKey + 10) {
+            if (key >= origKey + 100) {
                 Slog.w(TAG, "Value " + value + " supposed to be inserted at " + origKey
                         + " displaced to " + key);
             }
diff --git a/android/app/usage/UsageEvents.java b/android/app/usage/UsageEvents.java
index 84f57a3..503ca6c 100644
--- a/android/app/usage/UsageEvents.java
+++ b/android/app/usage/UsageEvents.java
@@ -111,7 +111,7 @@
 
         /**
          * An event type denoting a change in App Standby Bucket. The new bucket can be
-         * retrieved by calling {@link #getStandbyBucket()}.
+         * retrieved by calling {@link #getAppStandbyBucket()}.
          *
          * @see UsageStatsManager#getAppStandbyBucket()
          */
@@ -326,13 +326,23 @@
          * Returns the standby bucket of the app, if the event is of type
          * {@link #STANDBY_BUCKET_CHANGED}, otherwise returns 0.
          * @return the standby bucket associated with the event.
-         *
+         * @hide
          */
         public int getStandbyBucket() {
             return (mBucketAndReason & 0xFFFF0000) >>> 16;
         }
 
         /**
+         * Returns the standby bucket of the app, if the event is of type
+         * {@link #STANDBY_BUCKET_CHANGED}, otherwise returns 0.
+         * @return the standby bucket associated with the event.
+         *
+         */
+        public int getAppStandbyBucket() {
+            return (mBucketAndReason & 0xFFFF0000) >>> 16;
+        }
+
+        /**
          * Returns the reason for the bucketing, if the event is of type
          * {@link #STANDBY_BUCKET_CHANGED}, otherwise returns 0. Reason values include
          * the main reason which is one of REASON_MAIN_*, OR'ed with REASON_SUB_*, if there
diff --git a/android/appwidget/AppWidgetHost.java b/android/appwidget/AppWidgetHost.java
index 37360ba..49cc498 100644
--- a/android/appwidget/AppWidgetHost.java
+++ b/android/appwidget/AppWidgetHost.java
@@ -37,6 +37,7 @@
 import android.widget.RemoteViews;
 import android.widget.RemoteViews.OnClickHandler;
 
+import com.android.internal.R;
 import com.android.internal.appwidget.IAppWidgetHost;
 import com.android.internal.appwidget.IAppWidgetService;
 
@@ -171,8 +172,9 @@
                 return;
             }
             sServiceInitialized = true;
-            if (!context.getPackageManager().hasSystemFeature(
-                    PackageManager.FEATURE_APP_WIDGETS)) {
+            PackageManager packageManager = context.getPackageManager();
+            if (!packageManager.hasSystemFeature(PackageManager.FEATURE_APP_WIDGETS)
+                    && !context.getResources().getBoolean(R.bool.config_enableAppWidgetService)) {
                 return;
             }
             IBinder b = ServiceManager.getService(Context.APPWIDGET_SERVICE);
diff --git a/android/bluetooth/BluetoothHearingAid.java b/android/bluetooth/BluetoothHearingAid.java
index 8f8083e..159e165 100644
--- a/android/bluetooth/BluetoothHearingAid.java
+++ b/android/bluetooth/BluetoothHearingAid.java
@@ -421,29 +421,29 @@
     }
 
     /**
-     * Check whether the device is active.
+     * Get the connected physical Hearing Aid devices that are active
      *
      * <p>Requires {@link android.Manifest.permission#BLUETOOTH}
      * permission.
      *
-     * @return the connected device that is active or null if no device
-     * is active
+     * @return the list of active devices. The first element is the left active
+     * device; the second element is the right active device. If either or both side
+     * is not active, it will be null on that position. Returns empty list on error.
      * @hide
      */
     @RequiresPermission(Manifest.permission.BLUETOOTH)
-    public boolean isActiveDevice(@Nullable BluetoothDevice device) {
-        if (VDBG) log("isActiveDevice()");
+    public List<BluetoothDevice> getActiveDevices() {
+        if (VDBG) log("getActiveDevices()");
         try {
             mServiceLock.readLock().lock();
-            if (mService != null && isEnabled()
-                    && ((device == null) || isValidDevice(device))) {
-                return mService.isActiveDevice(device);
+            if (mService != null && isEnabled()) {
+                return mService.getActiveDevices();
             }
             if (mService == null) Log.w(TAG, "Proxy not attached to service");
-            return false;
+            return new ArrayList<>();
         } catch (RemoteException e) {
             Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
-            return false;
+            return new ArrayList<>();
         } finally {
             mServiceLock.readLock().unlock();
         }
diff --git a/android/bluetooth/BluetoothHidDevice.java b/android/bluetooth/BluetoothHidDevice.java
index af99bf7..3bc8544 100644
--- a/android/bluetooth/BluetoothHidDevice.java
+++ b/android/bluetooth/BluetoothHidDevice.java
@@ -701,6 +701,28 @@
     }
 
     /**
+     * Gets the application name of the current HidDeviceService user.
+     *
+     * @return the current user name, or empty string if cannot get the name
+     * {@hide}
+     */
+    public String getUserAppName() {
+        final IBluetoothHidDevice service = mService;
+
+        if (service != null) {
+            try {
+                return service.getUserAppName();
+            } catch (RemoteException e) {
+                Log.e(TAG, e.toString());
+            }
+        } else {
+            Log.w(TAG, "Proxy not attached to service");
+        }
+
+        return "";
+    }
+
+    /**
      * Initiates connection to host which is currently paired with this device. If the application
      * is not registered, #connect(BluetoothDevice) will fail. The connection state should be
      * tracked by the application by handling callback from Callback#onConnectionStateChanged. The
diff --git a/android/content/ContentResolver.java b/android/content/ContentResolver.java
index 9f3df37..f7908b6 100644
--- a/android/content/ContentResolver.java
+++ b/android/content/ContentResolver.java
@@ -51,7 +51,6 @@
 import android.util.EventLog;
 import android.util.Log;
 
-import com.android.internal.util.ArrayUtils;
 import com.android.internal.util.MimeIconUtils;
 import com.android.internal.util.Preconditions;
 
@@ -602,6 +601,8 @@
             try {
                 return provider.getType(url);
             } catch (RemoteException e) {
+                // Arbitrary and not worth documenting, as Activity
+                // Manager will kill this process shortly anyway.
                 return null;
             } catch (java.lang.Exception e) {
                 Log.w(TAG, "Failed to get type for: " + url + " (" + e.getMessage() + ")");
@@ -620,9 +621,7 @@
                     ContentProvider.getUriWithoutUserId(url), resolveUserId(url));
             return type;
         } catch (RemoteException e) {
-            // Arbitrary and not worth documenting, as Activity
-            // Manager will kill this process shortly anyway.
-            return null;
+            throw e.rethrowFromSystemServer();
         } catch (java.lang.Exception e) {
             Log.w(TAG, "Failed to get type for: " + url + " (" + e.getMessage() + ")");
             return null;
@@ -1964,6 +1963,7 @@
             getContentService().registerContentObserver(uri, notifyForDescendents,
                     observer.getContentObserver(), userHandle, mTargetSdkVersion);
         } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
         }
     }
 
@@ -1982,6 +1982,7 @@
                         contentObserver);
             }
         } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
         }
     }
 
@@ -2089,6 +2090,7 @@
                     syncToNetwork ? NOTIFY_SYNC_TO_NETWORK : 0,
                     userHandle, mTargetSdkVersion);
         } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
         }
     }
 
@@ -2105,6 +2107,7 @@
                     observer != null && observer.deliverSelfNotifications(), flags,
                     userHandle, mTargetSdkVersion);
         } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
         }
     }
 
@@ -2126,6 +2129,7 @@
                     ContentProvider.getUriWithoutUserId(uri), modeFlags, /* toPackage= */ null,
                     resolveUserId(uri));
         } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
         }
     }
 
@@ -2141,6 +2145,7 @@
                     ContentProvider.getUriWithoutUserId(uri), modeFlags, toPackage,
                     resolveUserId(uri));
         } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
         }
     }
 
@@ -2160,6 +2165,7 @@
                     ContentProvider.getUriWithoutUserId(uri), modeFlags, /* toPackage= */ null,
                     resolveUserId(uri));
         } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
         }
     }
 
@@ -2178,7 +2184,7 @@
             return ActivityManager.getService()
                     .getPersistedUriPermissions(mPackageName, true).getList();
         } catch (RemoteException e) {
-            throw new RuntimeException("Activity manager has died", e);
+            throw e.rethrowFromSystemServer();
         }
     }
 
@@ -2194,7 +2200,7 @@
             return ActivityManager.getService()
                     .getPersistedUriPermissions(mPackageName, false).getList();
         } catch (RemoteException e) {
-            throw new RuntimeException("Activity manager has died", e);
+            throw e.rethrowFromSystemServer();
         }
     }
 
@@ -2273,7 +2279,7 @@
         try {
             getContentService().syncAsUser(request, userId);
         } catch(RemoteException e) {
-            // Shouldn't happen.
+            throw e.rethrowFromSystemServer();
         }
     }
 
@@ -2285,7 +2291,7 @@
         try {
             getContentService().sync(request);
         } catch(RemoteException e) {
-            // Shouldn't happen.
+            throw e.rethrowFromSystemServer();
         }
     }
 
@@ -2349,6 +2355,7 @@
         try {
             getContentService().cancelSync(account, authority, null);
         } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
         }
     }
 
@@ -2360,6 +2367,7 @@
         try {
             getContentService().cancelSyncAsUser(account, authority, null, userId);
         } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
         }
     }
 
@@ -2371,7 +2379,7 @@
         try {
             return getContentService().getSyncAdapterTypes();
         } catch (RemoteException e) {
-            throw new RuntimeException("the ContentService should always be reachable", e);
+            throw e.rethrowFromSystemServer();
         }
     }
 
@@ -2383,7 +2391,7 @@
         try {
             return getContentService().getSyncAdapterTypesAsUser(userId);
         } catch (RemoteException e) {
-            throw new RuntimeException("the ContentService should always be reachable", e);
+            throw e.rethrowFromSystemServer();
         }
     }
 
@@ -2397,8 +2405,8 @@
         try {
             return getContentService().getSyncAdapterPackagesForAuthorityAsUser(authority, userId);
         } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
         }
-        return ArrayUtils.emptyArray(String.class);
     }
 
     /**
@@ -2414,7 +2422,7 @@
         try {
             return getContentService().getSyncAutomatically(account, authority);
         } catch (RemoteException e) {
-            throw new RuntimeException("the ContentService should always be reachable", e);
+            throw e.rethrowFromSystemServer();
         }
     }
 
@@ -2427,7 +2435,7 @@
         try {
             return getContentService().getSyncAutomaticallyAsUser(account, authority, userId);
         } catch (RemoteException e) {
-            throw new RuntimeException("the ContentService should always be reachable", e);
+            throw e.rethrowFromSystemServer();
         }
     }
 
@@ -2453,8 +2461,7 @@
         try {
             getContentService().setSyncAutomaticallyAsUser(account, authority, sync, userId);
         } catch (RemoteException e) {
-            // exception ignored; if this is thrown then it means the runtime is in the midst of
-            // being restarted
+            throw e.rethrowFromSystemServer();
         }
     }
 
@@ -2500,8 +2507,7 @@
         try {
              getContentService().addPeriodicSync(account, authority, extras, pollFrequency);
         } catch (RemoteException e) {
-            // exception ignored; if this is thrown then it means the runtime is in the midst of
-            // being restarted
+            throw e.rethrowFromSystemServer();
         }
     }
 
@@ -2540,7 +2546,7 @@
         try {
             getContentService().removePeriodicSync(account, authority, extras);
         } catch (RemoteException e) {
-            throw new RuntimeException("the ContentService should always be reachable", e);
+            throw e.rethrowFromSystemServer();
         }
     }
 
@@ -2564,8 +2570,7 @@
         try {
             getContentService().cancelRequest(request);
         } catch (RemoteException e) {
-            // exception ignored; if this is thrown then it means the runtime is in the midst of
-            // being restarted
+            throw e.rethrowFromSystemServer();
         }
     }
 
@@ -2582,7 +2587,7 @@
         try {
             return getContentService().getPeriodicSyncs(account, authority, null);
         } catch (RemoteException e) {
-            throw new RuntimeException("the ContentService should always be reachable", e);
+            throw e.rethrowFromSystemServer();
         }
     }
 
@@ -2596,7 +2601,7 @@
         try {
             return getContentService().getIsSyncable(account, authority);
         } catch (RemoteException e) {
-            throw new RuntimeException("the ContentService should always be reachable", e);
+            throw e.rethrowFromSystemServer();
         }
     }
 
@@ -2609,7 +2614,7 @@
         try {
             return getContentService().getIsSyncableAsUser(account, authority, userId);
         } catch (RemoteException e) {
-            throw new RuntimeException("the ContentService should always be reachable", e);
+            throw e.rethrowFromSystemServer();
         }
     }
 
@@ -2623,8 +2628,7 @@
         try {
             getContentService().setIsSyncable(account, authority, syncable);
         } catch (RemoteException e) {
-            // exception ignored; if this is thrown then it means the runtime is in the midst of
-            // being restarted
+            throw e.rethrowFromSystemServer();
         }
     }
 
@@ -2640,7 +2644,7 @@
         try {
             return getContentService().getMasterSyncAutomatically();
         } catch (RemoteException e) {
-            throw new RuntimeException("the ContentService should always be reachable", e);
+            throw e.rethrowFromSystemServer();
         }
     }
 
@@ -2652,7 +2656,7 @@
         try {
             return getContentService().getMasterSyncAutomaticallyAsUser(userId);
         } catch (RemoteException e) {
-            throw new RuntimeException("the ContentService should always be reachable", e);
+            throw e.rethrowFromSystemServer();
         }
     }
 
@@ -2676,8 +2680,7 @@
         try {
             getContentService().setMasterSyncAutomaticallyAsUser(sync, userId);
         } catch (RemoteException e) {
-            // exception ignored; if this is thrown then it means the runtime is in the midst of
-            // being restarted
+            throw e.rethrowFromSystemServer();
         }
     }
 
@@ -2701,7 +2704,7 @@
         try {
             return getContentService().isSyncActive(account, authority, null);
         } catch (RemoteException e) {
-            throw new RuntimeException("the ContentService should always be reachable", e);
+            throw e.rethrowFromSystemServer();
         }
     }
 
@@ -2727,7 +2730,7 @@
             }
             return syncs.get(0);
         } catch (RemoteException e) {
-            throw new RuntimeException("the ContentService should always be reachable", e);
+            throw e.rethrowFromSystemServer();
         }
     }
 
@@ -2744,7 +2747,7 @@
         try {
             return getContentService().getCurrentSyncs();
         } catch (RemoteException e) {
-            throw new RuntimeException("the ContentService should always be reachable", e);
+            throw e.rethrowFromSystemServer();
         }
     }
 
@@ -2756,7 +2759,7 @@
         try {
             return getContentService().getCurrentSyncsAsUser(userId);
         } catch (RemoteException e) {
-            throw new RuntimeException("the ContentService should always be reachable", e);
+            throw e.rethrowFromSystemServer();
         }
     }
 
@@ -2771,7 +2774,7 @@
         try {
             return getContentService().getSyncStatus(account, authority, null);
         } catch (RemoteException e) {
-            throw new RuntimeException("the ContentService should always be reachable", e);
+            throw e.rethrowFromSystemServer();
         }
     }
 
@@ -2784,7 +2787,7 @@
         try {
             return getContentService().getSyncStatusAsUser(account, authority, null, userId);
         } catch (RemoteException e) {
-            throw new RuntimeException("the ContentService should always be reachable", e);
+            throw e.rethrowFromSystemServer();
         }
     }
 
@@ -2809,7 +2812,7 @@
         try {
             return getContentService().isSyncPendingAsUser(account, authority, null, userId);
         } catch (RemoteException e) {
-            throw new RuntimeException("the ContentService should always be reachable", e);
+            throw e.rethrowFromSystemServer();
         }
     }
 
@@ -2841,7 +2844,7 @@
             getContentService().addStatusChangeListener(mask, observer);
             return observer;
         } catch (RemoteException e) {
-            throw new RuntimeException("the ContentService should always be reachable", e);
+            throw e.rethrowFromSystemServer();
         }
     }
 
@@ -2856,8 +2859,7 @@
         try {
             getContentService().removeStatusChangeListener((ISyncStatusObserver.Stub) handle);
         } catch (RemoteException e) {
-            // exception ignored; if this is thrown then it means the runtime is in the midst of
-            // being restarted
+            throw e.rethrowFromSystemServer();
         }
     }
 
@@ -3027,9 +3029,7 @@
             return sContentService;
         }
         IBinder b = ServiceManager.getService(CONTENT_SERVICE_NAME);
-        if (false) Log.v("ContentService", "default service binder = " + b);
         sContentService = IContentService.Stub.asInterface(b);
-        if (false) Log.v("ContentService", "default service = " + sContentService);
         return sContentService;
     }
 
@@ -3038,7 +3038,7 @@
         return mPackageName;
     }
 
-    private static IContentService sContentService;
+    private static volatile IContentService sContentService;
     private final Context mContext;
 
     final String mPackageName;
diff --git a/android/content/Context.java b/android/content/Context.java
index 920056a..ede7ee4 100644
--- a/android/content/Context.java
+++ b/android/content/Context.java
@@ -3780,7 +3780,7 @@
     public static final String DROPBOX_SERVICE = "dropbox";
 
     /**
-     * System service name for the DeviceIdleController.  There is no Java API for this.
+     * System service name for the DeviceIdleManager.
      * @see #getSystemService(String)
      * @hide
      */
diff --git a/android/content/Intent.java b/android/content/Intent.java
index 000912c..f608fcb 100644
--- a/android/content/Intent.java
+++ b/android/content/Intent.java
@@ -40,6 +40,7 @@
 import android.os.IBinder;
 import android.os.Parcel;
 import android.os.Parcelable;
+import android.os.PersistableBundle;
 import android.os.Process;
 import android.os.ResultReceiver;
 import android.os.ShellCommand;
@@ -1814,8 +1815,12 @@
     public static final String EXTRA_PACKAGE_NAME = "android.intent.extra.PACKAGE_NAME";
 
     /**
-     * Intent extra: A {@link Bundle} of extras for a package being suspended. Will be sent with
-     * {@link #ACTION_MY_PACKAGE_SUSPENDED}.
+     * Intent extra: A {@link Bundle} of extras for a package being suspended. Will be sent as an
+     * extra with {@link #ACTION_MY_PACKAGE_SUSPENDED}.
+     *
+     * <p>The contents of this {@link Bundle} are a contract between the suspended app and the
+     * suspending app, i.e. any app with the permission {@code android.permission.SUSPEND_APPS}.
+     * This is meant to enable the suspended app to better handle the state of being suspended.
      *
      * @see #ACTION_MY_PACKAGE_SUSPENDED
      * @see #ACTION_MY_PACKAGE_UNSUSPENDED
@@ -2282,6 +2287,34 @@
     public static final String ACTION_MY_PACKAGE_SUSPENDED = "android.intent.action.MY_PACKAGE_SUSPENDED";
 
     /**
+     * Activity Action: Started to show more details about why an application was suspended.
+     *
+     * <p>Whenever the system detects an activity launch for a suspended app, it shows a dialog to
+     * the user to inform them of the state and present them an affordance to start this activity
+     * action to show more details about the reason for suspension.
+     *
+     * <p>Apps holding {@link android.Manifest.permission#SUSPEND_APPS} must declare an activity
+     * handling this intent and protect it with
+     * {@link android.Manifest.permission#SEND_SHOW_SUSPENDED_APP_DETAILS}.
+     *
+     * <p>Includes an extra {@link #EXTRA_PACKAGE_NAME} which is the name of the suspended package.
+     *
+     * <p class="note">This is a protected intent that can only be sent
+     * by the system.
+     *
+     * @see PackageManager#setPackagesSuspended(String[], boolean, PersistableBundle,
+     * PersistableBundle, String)
+     * @see PackageManager#isPackageSuspended()
+     * @see #ACTION_PACKAGES_SUSPENDED
+     *
+     * @hide
+     */
+    @SystemApi
+    @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+    public static final String ACTION_SHOW_SUSPENDED_APP_DETAILS =
+            "android.intent.action.SHOW_SUSPENDED_APP_DETAILS";
+
+    /**
      * Broadcast Action: Sent to a package that has been unsuspended.
      *
      * <p class="note">This is a protected intent that can only be sent
@@ -6788,6 +6821,9 @@
                 case "--activity-task-on-home":
                     intent.addFlags(Intent.FLAG_ACTIVITY_TASK_ON_HOME);
                     break;
+                case "--activity-match-external":
+                    intent.addFlags(Intent.FLAG_ACTIVITY_MATCH_EXTERNAL);
+                    break;
                 case "--receiver-registered-only":
                     intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
                     break;
@@ -6924,7 +6960,7 @@
                 "    [--activity-no-user-action] [--activity-previous-is-top]",
                 "    [--activity-reorder-to-front] [--activity-reset-task-if-needed]",
                 "    [--activity-single-top] [--activity-clear-task]",
-                "    [--activity-task-on-home]",
+                "    [--activity-task-on-home] [--activity-match-external]",
                 "    [--receiver-registered-only] [--receiver-replace-pending]",
                 "    [--receiver-foreground] [--receiver-no-abort]",
                 "    [--receiver-include-background]",
diff --git a/android/content/QuickViewConstants.java b/android/content/QuickViewConstants.java
index a25513d..132d43f 100644
--- a/android/content/QuickViewConstants.java
+++ b/android/content/QuickViewConstants.java
@@ -45,8 +45,8 @@
      * Feature to delete an individual document. Quick viewer implementations must use
      * Storage Access Framework to both verify delete permission and to delete content.
      *
-     * @see DocumentsContract#Document#FLAG_SUPPORTS_DELETE
-     * @see DocumentsContract#deleteDocument(ContentResolver resolver, Uri documentUri)
+     * @see DocumentsContract.Document#FLAG_SUPPORTS_DELETE
+     * @see DocumentsContract#deleteDocument(ContentResolver, Uri)
      */
     public static final String FEATURE_DELETE = "android:delete";
 
diff --git a/android/content/pm/ApplicationInfo.java b/android/content/pm/ApplicationInfo.java
index e85058d..d65e051 100644
--- a/android/content/pm/ApplicationInfo.java
+++ b/android/content/pm/ApplicationInfo.java
@@ -26,6 +26,7 @@
 import android.content.pm.PackageManager.NameNotFoundException;
 import android.content.res.Resources;
 import android.graphics.drawable.Drawable;
+import android.os.Build;
 import android.os.Environment;
 import android.os.Parcel;
 import android.os.Parcelable;
@@ -590,26 +591,33 @@
     public static final int PRIVATE_FLAG_VIRTUAL_PRELOAD = 1 << 16;
 
     /**
-     * Value for {@linl #privateFlags}: whether this app is pre-installed on the
+     * Value for {@link #privateFlags}: whether this app is pre-installed on the
      * OEM partition of the system image.
      * @hide
      */
     public static final int PRIVATE_FLAG_OEM = 1 << 17;
 
     /**
-     * Value for {@linl #privateFlags}: whether this app is pre-installed on the
+     * Value for {@link #privateFlags}: whether this app is pre-installed on the
      * vendor partition of the system image.
      * @hide
      */
     public static final int PRIVATE_FLAG_VENDOR = 1 << 18;
 
     /**
-     * Value for {@linl #privateFlags}: whether this app is pre-installed on the
+     * Value for {@link #privateFlags}: whether this app is pre-installed on the
      * product partition of the system image.
      * @hide
      */
     public static final int PRIVATE_FLAG_PRODUCT = 1 << 19;
 
+    /**
+     * Value for {@link #privateFlags}: whether this app is signed with the
+     * platform key.
+     * @hide
+     */
+    public static final int PRIVATE_FLAG_SIGNED_WITH_PLATFORM_KEY = 1 << 20;
+
     /** @hide */
     @IntDef(flag = true, prefix = { "PRIVATE_FLAG_" }, value = {
             PRIVATE_FLAG_ACTIVITIES_RESIZE_MODE_RESIZEABLE,
@@ -629,6 +637,7 @@
             PRIVATE_FLAG_PRIVILEGED,
             PRIVATE_FLAG_PRODUCT,
             PRIVATE_FLAG_REQUIRED_FOR_SYSTEM_USER,
+            PRIVATE_FLAG_SIGNED_WITH_PLATFORM_KEY,
             PRIVATE_FLAG_STATIC_SHARED_LIBRARY,
             PRIVATE_FLAG_VENDOR,
             PRIVATE_FLAG_VIRTUAL_PRELOAD,
@@ -904,7 +913,17 @@
      * The app's declared version code.
      * @hide
      */
-    public long versionCode;
+    public long longVersionCode;
+
+    /**
+     * An integer representation of the app's declared version code. This is being left in place as
+     * some apps were using reflection to access it before the move to long in
+     * {@link android.os.Build.VERSION_CODES#P}
+     * @deprecated Use {@link #longVersionCode} instead.
+     * @hide
+     */
+    @Deprecated
+    public int versionCode;
 
     /**
      * The user-visible SDK version (ex. 26) of the framework against which the application claims
@@ -1114,11 +1133,12 @@
      */
     public static final int HIDDEN_API_ENFORCEMENT_NONE = 0;
     /**
-     * Light grey list enforcement, the strictest option. Enforces the light grey, dark grey and
-     * black lists.
+     * No API enforcement, but enable the detection logic and warnings. Observed behaviour is the
+     * same as {@link #HIDDEN_API_ENFORCEMENT_NONE} but you may see warnings in the log when APIs
+     * are accessed.
      * @hide
      * */
-    public static final int HIDDEN_API_ENFORCEMENT_ALL_LISTS = 1;
+    public static final int HIDDEN_API_ENFORCEMENT_JUST_WARN = 1;
     /**
      * Dark grey list enforcement. Enforces the dark grey and black lists
      * @hide
@@ -1140,14 +1160,15 @@
     @IntDef(prefix = { "HIDDEN_API_ENFORCEMENT_" }, value = {
             HIDDEN_API_ENFORCEMENT_DEFAULT,
             HIDDEN_API_ENFORCEMENT_NONE,
-            HIDDEN_API_ENFORCEMENT_ALL_LISTS,
+            HIDDEN_API_ENFORCEMENT_JUST_WARN,
             HIDDEN_API_ENFORCEMENT_DARK_GREY_AND_BLACK,
             HIDDEN_API_ENFORCEMENT_BLACK,
     })
     @Retention(RetentionPolicy.SOURCE)
     public @interface HiddenApiEnforcementPolicy {}
 
-    private boolean isValidHiddenApiEnforcementPolicy(int policy) {
+    /** @hide */
+    public static boolean isValidHiddenApiEnforcementPolicy(int policy) {
         return policy >= HIDDEN_API_ENFORCEMENT_DEFAULT && policy <= HIDDEN_API_ENFORCEMENT_MAX;
     }
 
@@ -1214,7 +1235,7 @@
         pw.println(prefix + "enabled=" + enabled
                 + " minSdkVersion=" + minSdkVersion
                 + " targetSdkVersion=" + targetSdkVersion
-                + " versionCode=" + versionCode
+                + " versionCode=" + longVersionCode
                 + " targetSandboxVersion=" + targetSandboxVersion);
         if ((dumpFlags & DUMP_FLAG_DETAILS) != 0) {
             if (manageSpaceActivityName != null) {
@@ -1287,7 +1308,7 @@
         proto.write(ApplicationInfoProto.Version.ENABLED, enabled);
         proto.write(ApplicationInfoProto.Version.MIN_SDK_VERSION, minSdkVersion);
         proto.write(ApplicationInfoProto.Version.TARGET_SDK_VERSION, targetSdkVersion);
-        proto.write(ApplicationInfoProto.Version.VERSION_CODE, versionCode);
+        proto.write(ApplicationInfoProto.Version.VERSION_CODE, longVersionCode);
         proto.write(ApplicationInfoProto.Version.TARGET_SANDBOX_VERSION, targetSandboxVersion);
         proto.end(versionToken);
 
@@ -1421,7 +1442,7 @@
         uid = orig.uid;
         minSdkVersion = orig.minSdkVersion;
         targetSdkVersion = orig.targetSdkVersion;
-        versionCode = orig.versionCode;
+        setVersionCode(orig.longVersionCode);
         enabled = orig.enabled;
         enabledSetting = orig.enabledSetting;
         installLocation = orig.installLocation;
@@ -1495,7 +1516,7 @@
         dest.writeInt(uid);
         dest.writeInt(minSdkVersion);
         dest.writeInt(targetSdkVersion);
-        dest.writeLong(versionCode);
+        dest.writeLong(longVersionCode);
         dest.writeInt(enabled ? 1 : 0);
         dest.writeInt(enabledSetting);
         dest.writeInt(installLocation);
@@ -1566,7 +1587,7 @@
         uid = source.readInt();
         minSdkVersion = source.readInt();
         targetSdkVersion = source.readInt();
-        versionCode = source.readLong();
+        setVersionCode(source.readLong());
         enabled = source.readInt() != 0;
         enabledSetting = source.readInt();
         installLocation = source.readInt();
@@ -1658,17 +1679,26 @@
         return SystemConfig.getInstance().getHiddenApiWhitelistedApps().contains(packageName);
     }
 
+    private boolean isAllowedToUseHiddenApis() {
+        return isSignedWithPlatformKey()
+            || (isPackageWhitelistedForHiddenApis() && (isSystemApp() || isUpdatedSystemApp()));
+    }
+
     /**
      * @hide
      */
     public @HiddenApiEnforcementPolicy int getHiddenApiEnforcementPolicy() {
+        if (isAllowedToUseHiddenApis()) {
+            return HIDDEN_API_ENFORCEMENT_NONE;
+        }
         if (mHiddenApiPolicy != HIDDEN_API_ENFORCEMENT_DEFAULT) {
             return mHiddenApiPolicy;
         }
-        if (isPackageWhitelistedForHiddenApis() && (isSystemApp() || isUpdatedSystemApp())) {
-            return HIDDEN_API_ENFORCEMENT_NONE;
+        if (targetSdkVersion < Build.VERSION_CODES.P) {
+            return HIDDEN_API_ENFORCEMENT_BLACK;
+        } else {
+            return HIDDEN_API_ENFORCEMENT_DARK_GREY_AND_BLACK;
         }
-        return HIDDEN_API_ENFORCEMENT_BLACK;
     }
 
     /**
@@ -1682,6 +1712,39 @@
     }
 
     /**
+     * Updates the hidden API enforcement policy for this app from the given values, if appropriate.
+     *
+     * This will have no effect if this app is not subject to hidden API enforcement, i.e. if it
+     * is on the package whitelist.
+     *
+     * @param policyPreP configured policy for pre-P apps, or {@link
+     *        #HIDDEN_API_ENFORCEMENT_DEFAULT} if nothing configured.
+     * @param policyP configured policy for apps targeting P or later, or {@link
+     *        #HIDDEN_API_ENFORCEMENT_DEFAULT} if nothing configured.
+     * @hide
+     */
+    public void maybeUpdateHiddenApiEnforcementPolicy(
+            @HiddenApiEnforcementPolicy int policyPreP, @HiddenApiEnforcementPolicy int policyP) {
+        if (isPackageWhitelistedForHiddenApis()) {
+            return;
+        }
+        if (targetSdkVersion < Build.VERSION_CODES.P) {
+            setHiddenApiEnforcementPolicy(policyPreP);
+        } else if (targetSdkVersion >= Build.VERSION_CODES.P) {
+            setHiddenApiEnforcementPolicy(policyP);
+        }
+
+    }
+
+    /**
+     * @hide
+     */
+    public void setVersionCode(long newVersionCode) {
+        longVersionCode = newVersionCode;
+        versionCode = (int) newVersionCode;
+    }
+
+    /**
      * @hide
      */
     @Override
@@ -1758,6 +1821,11 @@
     }
 
     /** @hide */
+    public boolean isSignedWithPlatformKey() {
+        return (privateFlags & ApplicationInfo.PRIVATE_FLAG_SIGNED_WITH_PLATFORM_KEY) != 0;
+    }
+
+    /** @hide */
     @TestApi
     public boolean isPrivilegedApp() {
         return (privateFlags & ApplicationInfo.PRIVATE_FLAG_PRIVILEGED) != 0;
diff --git a/android/content/pm/LauncherApps.java b/android/content/pm/LauncherApps.java
index 9aace2e..8223363 100644
--- a/android/content/pm/LauncherApps.java
+++ b/android/content/pm/LauncherApps.java
@@ -212,7 +212,7 @@
          * an applicaton.
          *
          * <p>Note: On devices running {@link android.os.Build.VERSION_CODES#P Android P} or higher,
-         * any apps that override {@link #onPackagesSuspended(String[], Bundle, UserHandle)} will
+         * any apps that override {@link #onPackagesSuspended(String[], UserHandle, Bundle)} will
          * not receive this callback.
          *
          * @param packageNames The names of the packages that have just been
@@ -226,15 +226,20 @@
          * Indicates that one or more packages have been suspended. A device administrator or an app
          * with {@code android.permission.SUSPEND_APPS} can do this.
          *
-         * @param packageNames The names of the packages that have just been suspended.
-         * @param launcherExtras A {@link Bundle} of extras for the launcher.
-         * @param user the user for which the given packages were suspended.
+         * <p>A suspending app with the permission {@code android.permission.SUSPEND_APPS} can
+         * optionally provide a {@link Bundle} of extra information that it deems helpful for the
+         * launcher to handle the suspended state of these packages. The contents of this
+         * {@link Bundle} supposed to be a contract between the suspending app and the launcher.
          *
+         * @param packageNames The names of the packages that have just been suspended.
+         * @param user the user for which the given packages were suspended.
+         * @param launcherExtras A {@link Bundle} of extras for the launcher, if provided to the
+         *                      system, {@code null} otherwise.
          * @see PackageManager#isPackageSuspended()
          * @see #getSuspendedPackageLauncherExtras(String, UserHandle)
          */
-        public void onPackagesSuspended(String[] packageNames, @Nullable Bundle launcherExtras,
-                UserHandle user) {
+        public void onPackagesSuspended(String[] packageNames, UserHandle user,
+                @Nullable Bundle launcherExtras) {
             onPackagesSuspended(packageNames, user);
         }
 
@@ -662,6 +667,9 @@
      * {@code PackageManager#setPackagesSuspended(String[], boolean, PersistableBundle,
      * PersistableBundle, String)}.
      *
+     * <p>The contents of this {@link Bundle} are supposed to be a contract between the suspending
+     * app and the launcher.
+     *
      * <p>Note: This just returns whatever extras were provided to the system, <em>which might
      * even be {@code null}.</em>
      *
@@ -670,7 +678,7 @@
      * @return A {@link Bundle} of launcher extras. Or {@code null} if the package is not currently
      *         suspended.
      *
-     * @see Callback#onPackagesSuspended(String[], Bundle, UserHandle)
+     * @see Callback#onPackagesSuspended(String[], UserHandle, Bundle)
      * @see PackageManager#isPackageSuspended()
      */
     public @Nullable Bundle getSuspendedPackageLauncherExtras(String packageName, UserHandle user) {
@@ -1298,8 +1306,8 @@
                     mCallback.onPackagesUnavailable(info.packageNames, info.user, info.replacing);
                     break;
                 case MSG_SUSPENDED:
-                    mCallback.onPackagesSuspended(info.packageNames, info.launcherExtras,
-                            info.user);
+                    mCallback.onPackagesSuspended(info.packageNames, info.user, info.launcherExtras
+                    );
                     break;
                 case MSG_UNSUSPENDED:
                     mCallback.onPackagesUnsuspended(info.packageNames, info.user);
diff --git a/android/content/pm/PackageInfo.java b/android/content/pm/PackageInfo.java
index 627ceb7..5f9f8f1 100644
--- a/android/content/pm/PackageInfo.java
+++ b/android/content/pm/PackageInfo.java
@@ -244,7 +244,7 @@
      * the first position to be the same across updates.
      *
      * <strong>Deprecated</strong> This has been replaced by the
-     * {@link PackageInfo#signingCertificateHistory} field, which takes into
+     * {@link PackageInfo#signingInfo} field, which takes into
      * account signing certificate rotation.  For backwards compatibility in
      * the event of signing certificate rotation, this will return the oldest
      * reported signing certificate, so that an application will appear to
@@ -256,29 +256,15 @@
     public Signature[] signatures;
 
     /**
-     * Array of all signatures arrays read from the package file, potentially
+     * Signing information read from the package file, potentially
      * including past signing certificates no longer used after signing
-     * certificate rotation.  Though signing certificate rotation is only
-     * available for apps with a single signing certificate, this provides an
-     * array of arrays so that packages signed with multiple signing
-     * certificates can still return all signers.  This is only filled in if
+     * certificate rotation.  This is only filled in if
      * the flag {@link PackageManager#GET_SIGNING_CERTIFICATES} was set.
      *
-     * A package must be singed with at least one certificate, which is at
-     * position zero in the array.  An application may be signed by multiple
-     * certificates, which would be in the array at position zero in an
-     * indeterminate order.  A package may also have a history of certificates
-     * due to signing certificate rotation.  In this case, the array will be
-     * populated by a series of single-entry arrays corresponding to a signing
-     * certificate of the package.
-     *
-     * <strong>Note:</strong> Signature ordering is not guaranteed to be
-     * stable which means that a package signed with certificates A and B is
-     * equivalent to being signed with certificates B and A. This means that
-     * in case multiple signatures are reported you cannot assume the one at
-     * the first position will be the same across updates.
+     * Use this field instead of the deprecated {@code signatures} field.
+     * See {@link SigningInfo} for more information on its contents.
      */
-    public Signature[][] signingCertificateHistory;
+    public SigningInfo signingInfo;
 
     /**
      * Application specified preferred configuration
@@ -476,17 +462,11 @@
         dest.writeBoolean(mOverlayIsStatic);
         dest.writeInt(compileSdkVersion);
         dest.writeString(compileSdkVersionCodename);
-        writeSigningCertificateHistoryToParcel(dest, parcelableFlags);
-    }
-
-    private void writeSigningCertificateHistoryToParcel(Parcel dest, int parcelableFlags) {
-        if (signingCertificateHistory != null) {
-            dest.writeInt(signingCertificateHistory.length);
-            for (int i = 0; i < signingCertificateHistory.length; i++) {
-                dest.writeTypedArray(signingCertificateHistory[i], parcelableFlags);
-            }
+        if (signingInfo != null) {
+            dest.writeInt(1);
+            signingInfo.writeToParcel(dest, parcelableFlags);
         } else {
-            dest.writeInt(-1);
+            dest.writeInt(0);
         }
     }
 
@@ -544,7 +524,10 @@
         mOverlayIsStatic = source.readBoolean();
         compileSdkVersion = source.readInt();
         compileSdkVersionCodename = source.readString();
-        readSigningCertificateHistoryFromParcel(source);
+        int hasSigningInfo = source.readInt();
+        if (hasSigningInfo != 0) {
+            signingInfo = SigningInfo.CREATOR.createFromParcel(source);
+        }
 
         // The component lists were flattened with the redundant ApplicationInfo
         // instances omitted.  Distribute the canonical one here as appropriate.
@@ -556,16 +539,6 @@
         }
     }
 
-    private void readSigningCertificateHistoryFromParcel(Parcel source) {
-        int len = source.readInt();
-        if (len != -1) {
-            signingCertificateHistory = new Signature[len][];
-            for (int i = 0; i < len; i++) {
-                signingCertificateHistory[i] = source.createTypedArray(Signature.CREATOR);
-            }
-        }
-    }
-
     private void propagateApplicationInfo(ApplicationInfo appInfo, ComponentInfo[] components) {
         if (components != null) {
             for (ComponentInfo ci : components) {
diff --git a/android/content/pm/PackageManager.java b/android/content/pm/PackageManager.java
index 491f0af..9d3b53f 100644
--- a/android/content/pm/PackageManager.java
+++ b/android/content/pm/PackageManager.java
@@ -2629,6 +2629,17 @@
             "android.hardware.strongbox_keystore";
 
     /**
+     * Feature for {@link #getSystemAvailableFeatures} and {@link #hasSystemFeature}:
+     * The device has a Keymaster implementation that supports Device ID attestation.
+     *
+     * @see DevicePolicyManager#isDeviceIdAttestationSupported
+     * @hide
+     */
+    @SdkConstant(SdkConstantType.FEATURE)
+    public static final String FEATURE_DEVICE_ID_ATTESTATION =
+            "android.software.device_id_attestation";
+
+    /**
      * Action to external storage service to clean out removed apps.
      * @hide
      */
@@ -3941,6 +3952,7 @@
      *
      * @hide
      */
+    @TestApi
     public abstract @NonNull String getServicesSystemSharedLibraryPackageName();
 
     /**
@@ -3950,6 +3962,7 @@
      *
      * @hide
      */
+    @TestApi
     public abstract @NonNull String getSharedSystemSharedLibraryPackageName();
 
     /**
@@ -5558,7 +5571,8 @@
      * @param packageName The name of the package to get the suspended status of.
      * @param userId The user id.
      * @return {@code true} if the package is suspended or {@code false} if the package is not
-     * suspended or could not be found.
+     * suspended.
+     * @throws IllegalArgumentException if the package was not found.
      * @hide
      */
     public abstract boolean isPackageSuspendedForUser(String packageName, int userId);
@@ -5567,12 +5581,13 @@
      * Query if an app is currently suspended.
      *
      * @return {@code true} if the given package is suspended, {@code false} otherwise
+     * @throws NameNotFoundException if the package could not be found.
      *
      * @see #setPackagesSuspended(String[], boolean, PersistableBundle, PersistableBundle, String)
      * @hide
      */
     @SystemApi
-    public boolean isPackageSuspended(String packageName) {
+    public boolean isPackageSuspended(String packageName) throws NameNotFoundException {
         throw new UnsupportedOperationException("isPackageSuspended not implemented");
     }
 
@@ -5603,51 +5618,16 @@
     }
 
     /**
-     * Retrieve the {@link PersistableBundle} that was passed as {@code appExtras} when the given
-     * package was suspended.
+     * Returns a {@link Bundle} of extras that was meant to be sent to the calling app when it was
+     * suspended. An app with the permission {@code android.permission.SUSPEND_APPS} can supply this
+     * to the system at the time of suspending an app.
      *
-     * <p> The caller must hold permission {@link Manifest.permission#SUSPEND_APPS} to use this
-     * api.</p>
+     * <p>This is the same {@link Bundle} that is sent along with the broadcast
+     * {@link Intent#ACTION_MY_PACKAGE_SUSPENDED}, whenever the app is suspended. The contents of
+     * this {@link Bundle} are a contract between the suspended app and the suspending app.
      *
-     * @param packageName The package to retrieve extras for.
-     * @return The {@code appExtras} for the suspended package.
-     *
-     * @see #setPackagesSuspended(String[], boolean, PersistableBundle, PersistableBundle, String)
-     * @hide
-     */
-    @SystemApi
-    @RequiresPermission(Manifest.permission.SUSPEND_APPS)
-    public @Nullable PersistableBundle getSuspendedPackageAppExtras(String packageName) {
-        throw new UnsupportedOperationException("getSuspendedPackageAppExtras not implemented");
-    }
-
-    /**
-     * Set the app extras for a suspended package. This method can be used to update the appExtras
-     * for a package that was earlier suspended using
-     * {@link #setPackagesSuspended(String[], boolean, PersistableBundle, PersistableBundle,
-     * String)}
-     * Does nothing if the given package is not already in a suspended state.
-     *
-     * @param packageName The package for which the appExtras need to be updated
-     * @param appExtras The new appExtras for the given package
-     *
-     * @see #setPackagesSuspended(String[], boolean, PersistableBundle, PersistableBundle, String)
-     *
-     * @hide
-     */
-    @SystemApi
-    @RequiresPermission(Manifest.permission.SUSPEND_APPS)
-    public void setSuspendedPackageAppExtras(String packageName,
-            @Nullable PersistableBundle appExtras) {
-        throw new UnsupportedOperationException("setSuspendedPackageAppExtras not implemented");
-    }
-
-    /**
-     * Returns any extra information supplied as {@code appExtras} to the system when the calling
-     * app was suspended.
-     *
-     * <p>Note: If no extras were supplied to the system, this method will return {@code null}, even
-     * when the calling app has been suspended.</p>
+     * <p>Note: These extras are optional, so if no extras were supplied to the system, this method
+     * will return {@code null}, even when the calling app has been suspended.
      *
      * @return A {@link Bundle} containing the extras for the app, or {@code null} if the
      * package is not currently suspended.
@@ -5655,6 +5635,7 @@
      * @see #isPackageSuspended()
      * @see Intent#ACTION_MY_PACKAGE_UNSUSPENDED
      * @see Intent#ACTION_MY_PACKAGE_SUSPENDED
+     * @see Intent#EXTRA_SUSPENDED_PACKAGE_EXTRAS
      */
     public @Nullable Bundle getSuspendedPackageAppExtras() {
         throw new UnsupportedOperationException("getSuspendedPackageAppExtras not implemented");
@@ -6118,7 +6099,9 @@
      * signed.  This should be used instead of {@code getPackageInfo} with {@code GET_SIGNATURES}
      * since it takes into account the possibility of signing certificate rotation, except in the
      * case of packages that are signed by multiple certificates, for which signing certificate
-     * rotation is not supported.
+     * rotation is not supported.  This method is analogous to using {@code getPackageInfo} with
+     * {@code GET_SIGNING_CERTIFICATES} and then searching through the resulting {@code
+     * signingCertificateHistory} field to see if the desired certificate is present.
      *
      * @param packageName package whose signing certificates to check
      * @param certificate signing certificate for which to search
@@ -6132,13 +6115,19 @@
     }
 
     /**
-     * Searches the set of signing certificates by which the given uid has proven to have been
-     * signed.  This should be used instead of {@code getPackageInfo} with {@code GET_SIGNATURES}
+     * Searches the set of signing certificates by which the package(s) for the given uid has proven
+     * to have been signed.  For multiple packages sharing the same uid, this will return the
+     * signing certificates found in the signing history of the "newest" package, where "newest"
+     * indicates the package with the newest signing certificate in the shared uid group.  This
+     * method should be used instead of {@code getPackageInfo} with {@code GET_SIGNATURES}
      * since it takes into account the possibility of signing certificate rotation, except in the
      * case of packages that are signed by multiple certificates, for which signing certificate
-     * rotation is not supported.
+     * rotation is not supported. This method is analogous to using {@code getPackagesForUid}
+     * followed by {@code getPackageInfo} with {@code GET_SIGNING_CERTIFICATES}, selecting the
+     * {@code PackageInfo} of the newest-signed bpackage , and finally searching through the
+     * resulting {@code signingCertificateHistory} field to see if the desired certificate is there.
      *
-     * @param uid package whose signing certificates to check
+     * @param uid uid whose signing certificates to check
      * @param certificate signing certificate for which to search
      * @param type representation of the {@code certificate}
      * @return true if this package was or is signed by exactly the certificate {@code certificate}
diff --git a/android/content/pm/PackageManagerInternal.java b/android/content/pm/PackageManagerInternal.java
index c9b78c0..a9d0911 100644
--- a/android/content/pm/PackageManagerInternal.java
+++ b/android/content/pm/PackageManagerInternal.java
@@ -191,10 +191,10 @@
     /**
      * Retrieve launcher extras for a suspended package provided to the system in
      * {@link PackageManager#setPackagesSuspended(String[], boolean, PersistableBundle,
-     * PersistableBundle, String)}
+     * PersistableBundle, String)}.
      *
      * @param packageName The package for which to return launcher extras.
-     * @param userId The user for which to check,
+     * @param userId The user for which to check.
      * @return The launcher extras.
      *
      * @see PackageManager#setPackagesSuspended(String[], boolean, PersistableBundle,
@@ -205,6 +205,38 @@
             int userId);
 
     /**
+     * Internal api to query the suspended state of a package.
+     * @param packageName The package to check.
+     * @param userId The user id to check for.
+     * @return {@code true} if the package is suspended, {@code false} otherwise.
+     * @see PackageManager#isPackageSuspended(String)
+     */
+    public abstract boolean isPackageSuspended(String packageName, int userId);
+
+    /**
+     * Get the name of the package that suspended the given package. Packages can be suspended by
+     * device administrators or apps holding {@link android.Manifest.permission#MANAGE_USERS} or
+     * {@link android.Manifest.permission#SUSPEND_APPS}.
+     *
+     * @param suspendedPackage The package that has been suspended.
+     * @param userId The user for which to check.
+     * @return Name of the package that suspended the given package. Returns {@code null} if the
+     * given package is not currently suspended and the platform package name - i.e.
+     * {@code "android"} - if the package was suspended by a device admin.
+     */
+    public abstract String getSuspendingPackage(String suspendedPackage, int userId);
+
+    /**
+     * Get the dialog message to be shown to the user when they try to launch a suspended
+     * application.
+     *
+     * @param suspendedPackage The package that has been suspended.
+     * @param userId The user for which to check.
+     * @return The dialog message to be shown to the user.
+     */
+    public abstract String getSuspendedDialogMessage(String suspendedPackage, int userId);
+
+    /**
      * Do a straight uid lookup for the given package/application in the given user.
      * @see PackageManager#getPackageUidAsUser(String, int, int)
      * @return The app's uid, or < 0 if the package was not found in that user
@@ -429,7 +461,7 @@
      * Resolves an activity intent, allowing instant apps to be resolved.
      */
     public abstract ResolveInfo resolveIntent(Intent intent, String resolvedType,
-            int flags, int userId, boolean resolveForStart);
+            int flags, int userId, boolean resolveForStart, int filterCallingUid);
 
     /**
     * Resolves a service intent, allowing instant apps to be resolved.
diff --git a/android/content/pm/PackageParser.java b/android/content/pm/PackageParser.java
index 2f0faf2..453a74a 100644
--- a/android/content/pm/PackageParser.java
+++ b/android/content/pm/PackageParser.java
@@ -810,21 +810,11 @@
 
         // replacement for GET_SIGNATURES
         if ((flags & PackageManager.GET_SIGNING_CERTIFICATES) != 0) {
-            if (p.mSigningDetails.hasPastSigningCertificates()) {
-                // Package has included signing certificate rotation information.  Convert each
-                // entry to an array
-                int numberOfSigs = p.mSigningDetails.pastSigningCertificates.length;
-                pi.signingCertificateHistory = new Signature[numberOfSigs][];
-                for (int i = 0; i < numberOfSigs; i++) {
-                    pi.signingCertificateHistory[i] =
-                            new Signature[] { p.mSigningDetails.pastSigningCertificates[i] };
-                }
-            } else if (p.mSigningDetails.hasSignatures()) {
-                // otherwise keep old behavior
-                int numberOfSigs = p.mSigningDetails.signatures.length;
-                pi.signingCertificateHistory = new Signature[1][numberOfSigs];
-                System.arraycopy(p.mSigningDetails.signatures, 0,
-                        pi.signingCertificateHistory[0], 0, numberOfSigs);
+            if (p.mSigningDetails != SigningDetails.UNKNOWN) {
+                // only return a valid SigningInfo if there is signing information to report
+                pi.signingInfo = new SigningInfo(p.mSigningDetails);
+            } else {
+                pi.signingInfo = null;
             }
         }
         return pi;
@@ -1918,7 +1908,7 @@
                 com.android.internal.R.styleable.AndroidManifest_versionCode, 0);
         pkg.mVersionCodeMajor = sa.getInteger(
                 com.android.internal.R.styleable.AndroidManifest_versionCodeMajor, 0);
-        pkg.applicationInfo.versionCode = pkg.getLongVersionCode();
+        pkg.applicationInfo.setVersionCode(pkg.getLongVersionCode());
         pkg.baseRevisionCode = sa.getInteger(
                 com.android.internal.R.styleable.AndroidManifest_revisionCode, 0);
         pkg.mVersionName = sa.getNonConfigurationString(
@@ -2726,7 +2716,7 @@
 
         // Fot apps targeting O-MR1 we require explicit enumeration of all certs.
         String[] additionalCertSha256Digests = EmptyArray.STRING;
-        if (pkg.applicationInfo.targetSdkVersion > Build.VERSION_CODES.O) {
+        if (pkg.applicationInfo.targetSdkVersion >= Build.VERSION_CODES.O_MR1) {
             additionalCertSha256Digests = parseAdditionalCertificates(res, parser, outError);
             if (additionalCertSha256Digests == null) {
                 return false;
diff --git a/android/content/pm/PackageSharedLibraryUpdater.java b/android/content/pm/PackageSharedLibraryUpdater.java
index fa89432..b14b321 100644
--- a/android/content/pm/PackageSharedLibraryUpdater.java
+++ b/android/content/pm/PackageSharedLibraryUpdater.java
@@ -62,7 +62,7 @@
 
     static boolean apkTargetsApiLevelLessThanOrEqualToOMR1(PackageParser.Package pkg) {
         int targetSdkVersion = pkg.applicationInfo.targetSdkVersion;
-        return targetSdkVersion <= Build.VERSION_CODES.O_MR1;
+        return targetSdkVersion < Build.VERSION_CODES.P;
     }
 
     /**
diff --git a/android/content/pm/PackageUserState.java b/android/content/pm/PackageUserState.java
index f7b6e09..f471a1d 100644
--- a/android/content/pm/PackageUserState.java
+++ b/android/content/pm/PackageUserState.java
@@ -34,6 +34,7 @@
 import com.android.internal.util.ArrayUtils;
 
 import java.util.Arrays;
+import java.util.Objects;
 
 /**
  * Per-user state information about a package.
@@ -47,6 +48,7 @@
     public boolean hidden; // Is the app restricted by owner / admin
     public boolean suspended;
     public String suspendingPackage;
+    public String dialogMessage; // Message to show when a suspended package launch attempt is made
     public PersistableBundle suspendedAppExtras;
     public PersistableBundle suspendedLauncherExtras;
     public boolean instantApp;
@@ -82,6 +84,7 @@
         hidden = o.hidden;
         suspended = o.suspended;
         suspendingPackage = o.suspendingPackage;
+        dialogMessage = o.dialogMessage;
         suspendedAppExtras = o.suspendedAppExtras;
         suspendedLauncherExtras = o.suspendedLauncherExtras;
         instantApp = o.instantApp;
@@ -208,6 +211,9 @@
                     || !suspendingPackage.equals(oldState.suspendingPackage)) {
                 return false;
             }
+            if (!Objects.equals(dialogMessage, oldState.dialogMessage)) {
+                return false;
+            }
             if (!BaseBundle.kindofEquals(suspendedAppExtras,
                     oldState.suspendedAppExtras)) {
                 return false;
diff --git a/android/content/pm/SigningInfo.java b/android/content/pm/SigningInfo.java
new file mode 100644
index 0000000..ef87403
--- /dev/null
+++ b/android/content/pm/SigningInfo.java
@@ -0,0 +1,139 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.content.pm;
+
+
+import android.annotation.NonNull;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * Information pertaining to the signing certificates used to sign a package.
+ */
+public final class SigningInfo implements Parcelable {
+
+    @NonNull
+    private final PackageParser.SigningDetails mSigningDetails;
+
+    public SigningInfo() {
+        mSigningDetails = PackageParser.SigningDetails.UNKNOWN;
+    }
+
+    /**
+     * @hide only packagemanager should be populating this
+     */
+    public SigningInfo(PackageParser.SigningDetails signingDetails) {
+        mSigningDetails = new PackageParser.SigningDetails(signingDetails);
+    }
+
+    public SigningInfo(SigningInfo orig) {
+        mSigningDetails = new PackageParser.SigningDetails(orig.mSigningDetails);
+    }
+
+    private SigningInfo(Parcel source) {
+        mSigningDetails = PackageParser.SigningDetails.CREATOR.createFromParcel(source);
+    }
+
+    /**
+     * Although relatively uncommon, packages may be signed by more than one signer, in which case
+     * their identity is viewed as being the set of all signers, not just any one.
+     */
+    public boolean hasMultipleSigners() {
+        return mSigningDetails.signatures != null && mSigningDetails.signatures.length > 1;
+    }
+
+    /**
+     * APK Signature Scheme v3 enables packages to provide a proof-of-rotation record that the
+     * platform verifies, and uses, to allow the use of new signing certificates.  This is only
+     * available to packages that are not signed by multiple signers.  In the event of a change to a
+     * new signing certificate, the package's past signing certificates are presented as well.  Any
+     * check of a package's signing certificate should also include a search through its entire
+     * signing history, since it could change to a new signing certificate at any time.
+     */
+    public boolean hasPastSigningCertificates() {
+        return mSigningDetails.signatures != null
+                && mSigningDetails.pastSigningCertificates != null;
+    }
+
+    /**
+     * Returns the signing certificates this package has proven it is authorized to use. This
+     * includes both the signing certificate associated with the signer of the package and the past
+     * signing certificates it included as its proof of signing certificate rotation.  This method
+     * is the preferred replacement for the {@code GET_SIGNATURES} flag used with {@link
+     * PackageManager#getPackageInfo(String, int)}.  When determining if a package is signed by a
+     * desired certificate, the returned array should be checked to determine if it is one of the
+     * entries.
+     *
+     * <note>
+     *     This method returns null if the package is signed by multiple signing certificates, as
+     *     opposed to being signed by one current signer and also providing the history of past
+     *     signing certificates.  {@link #hasMultipleSigners()} may be used to determine if this
+     *     package is signed by multiple signers.  Packages which are signed by multiple signers
+     *     cannot change their signing certificates and their {@code Signature} array should be
+     *     checked to make sure that every entry matches the looked-for signing certificates.
+     * </note>
+     */
+    public Signature[] getSigningCertificateHistory() {
+        if (hasMultipleSigners()) {
+            return null;
+        } else if (!hasPastSigningCertificates()) {
+
+            // this package is only signed by one signer with no history, return it
+            return mSigningDetails.signatures;
+        } else {
+
+            // this package has provided proof of past signing certificates, include them
+            return mSigningDetails.pastSigningCertificates;
+        }
+    }
+
+    /**
+     * Returns the signing certificates used to sign the APK contents of this application.  Not
+     * including any past signing certificates the package proved it is authorized to use.
+     * <note>
+     *     This method should not be used unless {@link #hasMultipleSigners()} returns true,
+     *     indicating that {@link #getSigningCertificateHistory()} cannot be used, otherwise {@link
+     *     #getSigningCertificateHistory()} should be preferred.
+     * </note>
+     */
+    public Signature[] getApkContentsSigners() {
+        return mSigningDetails.signatures;
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int parcelableFlags) {
+        mSigningDetails.writeToParcel(dest, parcelableFlags);
+    }
+
+    public static final Parcelable.Creator<SigningInfo> CREATOR =
+            new Parcelable.Creator<SigningInfo>() {
+        @Override
+        public SigningInfo createFromParcel(Parcel source) {
+            return new SigningInfo(source);
+        }
+
+        @Override
+        public SigningInfo[] newArray(int size) {
+            return new SigningInfo[size];
+        }
+    };
+}
diff --git a/android/graphics/ImageDecoder.java b/android/graphics/ImageDecoder.java
index 506eab5..098f100 100644
--- a/android/graphics/ImageDecoder.java
+++ b/android/graphics/ImageDecoder.java
@@ -16,36 +16,177 @@
 
 package android.graphics;
 
+import static android.system.OsConstants.SEEK_CUR;
+import static android.system.OsConstants.SEEK_SET;
+
+import static java.lang.annotation.RetentionPolicy.SOURCE;
+
+import android.annotation.AnyThread;
+import android.annotation.IntDef;
+import android.annotation.IntRange;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.annotation.Px;
+import android.annotation.TestApi;
+import android.annotation.WorkerThread;
 import android.content.ContentResolver;
+import android.content.res.AssetFileDescriptor;
+import android.content.res.AssetManager;
 import android.content.res.AssetManager.AssetInputStream;
 import android.content.res.Resources;
 import android.graphics.drawable.AnimatedImageDrawable;
-import android.graphics.drawable.Drawable;
 import android.graphics.drawable.BitmapDrawable;
+import android.graphics.drawable.Drawable;
+import android.graphics.drawable.NinePatchDrawable;
 import android.net.Uri;
+import android.os.Build;
+import android.system.ErrnoException;
+import android.system.Os;
 import android.util.DisplayMetrics;
 import android.util.Size;
 import android.util.TypedValue;
 
-import java.nio.ByteBuffer;
+import dalvik.system.CloseGuard;
+
+import libcore.io.IoUtils;
+
 import java.io.File;
+import java.io.FileDescriptor;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
 import java.io.IOException;
 import java.io.InputStream;
-import java.lang.ArrayIndexOutOfBoundsException;
-import java.lang.AutoCloseable;
-import java.lang.NullPointerException;
 import java.lang.annotation.Retention;
-import static java.lang.annotation.RetentionPolicy.SOURCE;
+import java.nio.ByteBuffer;
+import java.util.concurrent.atomic.AtomicBoolean;
 
 /**
- *  Class for decoding images as {@link Bitmap}s or {@link Drawable}s.
+ *  <p>A class for converting encoded images (like {@code PNG}, {@code JPEG},
+ *  {@code WEBP}, {@code GIF}, or {@code HEIF}) into {@link Drawable} or
+ *  {@link Bitmap} objects.
+ *
+ *  <p>To use it, first create a {@link Source Source} using one of the
+ *  {@code createSource} overloads. For example, to decode from a {@link File}, call
+ *  {@link #createSource(File)} and pass the result to {@link #decodeDrawable(Source)}
+ *  or {@link #decodeBitmap(Source)}:
+ *
+ *  <pre class="prettyprint">
+ *  File file = new File(...);
+ *  ImageDecoder.Source source = ImageDecoder.createSource(file);
+ *  Drawable drawable = ImageDecoder.decodeDrawable(source);
+ *  </pre>
+ *
+ *  <p>To change the default settings, pass the {@link Source Source} and an
+ *  {@link OnHeaderDecodedListener OnHeaderDecodedListener} to
+ *  {@link #decodeDrawable(Source, OnHeaderDecodedListener)} or
+ *  {@link #decodeBitmap(Source, OnHeaderDecodedListener)}. For example, to
+ *  create a sampled image with half the width and height of the original image,
+ *  call {@link #setTargetSampleSize setTargetSampleSize(2)} inside
+ *  {@link OnHeaderDecodedListener#onHeaderDecoded onHeaderDecoded}:
+ *
+ *  <pre class="prettyprint">
+ *  OnHeaderDecodedListener listener = new OnHeaderDecodedListener() {
+ *      public void onHeaderDecoded(ImageDecoder decoder, ImageInfo info, Source source) {
+ *          decoder.setTargetSampleSize(2);
+ *      }
+ *  };
+ *  Drawable drawable = ImageDecoder.decodeDrawable(source, listener);
+ *  </pre>
+ *
+ *  <p>The {@link ImageInfo ImageInfo} contains information about the encoded image, like
+ *  its width and height, and the {@link Source Source} can be used to match to a particular
+ *  {@link Source Source} if a single {@link OnHeaderDecodedListener OnHeaderDecodedListener}
+ *  is used with multiple {@link Source Source} objects.
+ *
+ *  <p>The {@link OnHeaderDecodedListener OnHeaderDecodedListener} can also be implemented
+ *  as a lambda:
+ *
+ *  <pre class="prettyprint">
+ *  Drawable drawable = ImageDecoder.decodeDrawable(source, (decoder, info, src) -&gt; {
+ *      decoder.setTargetSampleSize(2);
+ *  });
+ *  </pre>
+ *
+ *  <p>If the encoded image is an animated {@code GIF} or {@code WEBP},
+ *  {@link #decodeDrawable decodeDrawable} will return an {@link AnimatedImageDrawable}. To
+ *  start its animation, call {@link AnimatedImageDrawable#start AnimatedImageDrawable.start()}:
+ *
+ *  <pre class="prettyprint">
+ *  Drawable drawable = ImageDecoder.decodeDrawable(source);
+ *  if (drawable instanceof AnimatedImageDrawable) {
+ *      ((AnimatedImageDrawable) drawable).start();
+ *  }
+ *  </pre>
+ *
+ *  <p>By default, a {@link Bitmap} created by {@link ImageDecoder} (including
+ *  one that is inside a {@link Drawable}) will be immutable (i.e.
+ *  {@link Bitmap#isMutable Bitmap.isMutable()} returns {@code false}), and it
+ *  will typically have {@code Config} {@link Bitmap.Config#HARDWARE}. Although
+ *  these properties can be changed with {@link #setMutableRequired setMutableRequired(true)}
+ *  (which is only compatible with {@link #decodeBitmap(Source)} and
+ *  {@link #decodeBitmap(Source, OnHeaderDecodedListener)}) and {@link #setAllocator},
+ *  it is also possible to apply custom effects regardless of the mutability of
+ *  the final returned object by passing a {@link PostProcessor} to
+ *  {@link #setPostProcessor setPostProcessor}. A {@link PostProcessor} can also be a lambda:
+ *
+ *  <pre class="prettyprint">
+ *  Drawable drawable = ImageDecoder.decodeDrawable(source, (decoder, info, src) -&gt; {
+ *      decoder.setPostProcessor((canvas) -&gt; {
+ *              // This will create rounded corners.
+ *              Path path = new Path();
+ *              path.setFillType(Path.FillType.INVERSE_EVEN_ODD);
+ *              int width = canvas.getWidth();
+ *              int height = canvas.getHeight();
+ *              path.addRoundRect(0, 0, width, height, 20, 20, Path.Direction.CW);
+ *              Paint paint = new Paint();
+ *              paint.setAntiAlias(true);
+ *              paint.setColor(Color.TRANSPARENT);
+ *              paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC));
+ *              canvas.drawPath(path, paint);
+ *              return PixelFormat.TRANSLUCENT;
+ *      });
+ *  });
+ *  </pre>
+ *
+ *  <p>If the encoded image is incomplete or contains an error, or if an
+ *  {@link Exception} occurs during decoding, a {@link DecodeException DecodeException}
+ *  will be thrown. In some cases, the {@link ImageDecoder} may have decoded part of
+ *  the image. In order to display the partial image, an
+ *  {@link OnPartialImageListener OnPartialImageListener} must be passed to
+ *  {@link #setOnPartialImageListener setOnPartialImageListener}. For example:
+ *
+ *  <pre class="prettyprint">
+ *  Drawable drawable = ImageDecoder.decodeDrawable(source, (decoder, info, src) -&gt; {
+ *      decoder.setOnPartialImageListener((DecodeException e) -&gt; {
+ *              // Returning true indicates to create a Drawable or Bitmap even
+ *              // if the whole image could not be decoded. Any remaining lines
+ *              // will be blank.
+ *              return true;
+ *      });
+ *  });
+ *  </pre>
  */
 public final class ImageDecoder implements AutoCloseable {
+    /** @hide **/
+    public static int sApiLevel;
 
     /**
-     *  Source of the encoded image data.
+     *  Source of encoded image data.
+     *
+     *  <p>References the data that will be used to decode a {@link Drawable}
+     *  or {@link Bitmap} in {@link #decodeDrawable decodeDrawable} or
+     *  {@link #decodeBitmap decodeBitmap}. Constructing a {@code Source} (with
+     *  one of the overloads of {@code createSource}) can be done on any thread
+     *  because the construction simply captures values. The real work is done
+     *  in {@link #decodeDrawable decodeDrawable} or {@link #decodeBitmap decodeBitmap}.
+     *
+     *  <p>A {@code Source} object can be reused to create multiple versions of the
+     *  same image. For example, to decode a full size image and its thumbnail,
+     *  the same {@code Source} can be used once with no
+     *  {@link OnHeaderDecodedListener OnHeaderDecodedListener} and once with an
+     *  implementation of {@link OnHeaderDecodedListener#onHeaderDecoded onHeaderDecoded}
+     *  that calls {@link #setTargetSize} with smaller dimensions. One {@code Source}
+     *  even used simultaneously in multiple threads.</p>
      */
     public static abstract class Source {
         private Source() {}
@@ -58,7 +199,7 @@
         int getDensity() { return Bitmap.DENSITY_NONE; }
 
         /* @hide */
-        int computeDstDensity() {
+        final int computeDstDensity() {
             Resources res = getResources();
             if (res == null) {
                 return Bitmap.getDefaultDensity();
@@ -84,7 +225,7 @@
 
         @Override
         public ImageDecoder createImageDecoder() throws IOException {
-            return new ImageDecoder();
+            return nCreate(mData, mOffset, mLength, this);
         }
     }
 
@@ -96,27 +237,126 @@
 
         @Override
         public ImageDecoder createImageDecoder() throws IOException {
-            return new ImageDecoder();
+            if (!mBuffer.isDirect() && mBuffer.hasArray()) {
+                int offset = mBuffer.arrayOffset() + mBuffer.position();
+                int length = mBuffer.limit() - mBuffer.position();
+                return nCreate(mBuffer.array(), offset, length, this);
+            }
+            ByteBuffer buffer = mBuffer.slice();
+            return nCreate(buffer, buffer.position(), buffer.limit(), this);
         }
     }
 
     private static class ContentResolverSource extends Source {
-        ContentResolverSource(@NonNull ContentResolver resolver, @NonNull Uri uri) {
+        ContentResolverSource(@NonNull ContentResolver resolver, @NonNull Uri uri,
+                @Nullable Resources res) {
             mResolver = resolver;
             mUri = uri;
+            mResources = res;
         }
 
         private final ContentResolver mResolver;
         private final Uri mUri;
+        private final Resources mResources;
+
+        @Nullable
+        Resources getResources() { return mResources; }
 
         @Override
         public ImageDecoder createImageDecoder() throws IOException {
-            return new ImageDecoder();
+            AssetFileDescriptor assetFd = null;
+            try {
+                if (mUri.getScheme() == ContentResolver.SCHEME_CONTENT) {
+                    assetFd = mResolver.openTypedAssetFileDescriptor(mUri,
+                            "image/*", null);
+                } else {
+                    assetFd = mResolver.openAssetFileDescriptor(mUri, "r");
+                }
+            } catch (FileNotFoundException e) {
+                // Some images cannot be opened as AssetFileDescriptors (e.g.
+                // bmp, ico). Open them as InputStreams.
+                InputStream is = mResolver.openInputStream(mUri);
+                if (is == null) {
+                    throw new FileNotFoundException(mUri.toString());
+                }
+
+                return createFromStream(is, true, this);
+            }
+
+            final FileDescriptor fd = assetFd.getFileDescriptor();
+            final long offset = assetFd.getStartOffset();
+
+            ImageDecoder decoder = null;
+            try {
+                try {
+                    Os.lseek(fd, offset, SEEK_SET);
+                    decoder = nCreate(fd, this);
+                } catch (ErrnoException e) {
+                    decoder = createFromStream(new FileInputStream(fd), true, this);
+                }
+            } finally {
+                if (decoder == null) {
+                    IoUtils.closeQuietly(assetFd);
+                } else {
+                    decoder.mAssetFd = assetFd;
+                }
+            }
+            return decoder;
         }
     }
 
+    @NonNull
+    private static ImageDecoder createFromFile(@NonNull File file,
+            @NonNull Source source) throws IOException {
+        FileInputStream stream = new FileInputStream(file);
+        FileDescriptor fd = stream.getFD();
+        try {
+            Os.lseek(fd, 0, SEEK_CUR);
+        } catch (ErrnoException e) {
+            return createFromStream(stream, true, source);
+        }
+
+        ImageDecoder decoder = null;
+        try {
+            decoder = nCreate(fd, source);
+        } finally {
+            if (decoder == null) {
+                IoUtils.closeQuietly(stream);
+            } else {
+                decoder.mInputStream = stream;
+                decoder.mOwnsInputStream = true;
+            }
+        }
+        return decoder;
+    }
+
+    @NonNull
+    private static ImageDecoder createFromStream(@NonNull InputStream is,
+            boolean closeInputStream, Source source) throws IOException {
+        // Arbitrary size matches BitmapFactory.
+        byte[] storage = new byte[16 * 1024];
+        ImageDecoder decoder = null;
+        try {
+            decoder = nCreate(is, storage, source);
+        } finally {
+            if (decoder == null) {
+                if (closeInputStream) {
+                    IoUtils.closeQuietly(is);
+                }
+            } else {
+                decoder.mInputStream = is;
+                decoder.mOwnsInputStream = closeInputStream;
+                decoder.mTempStorage = storage;
+            }
+        }
+
+        return decoder;
+    }
+
     /**
      * For backwards compatibility, this does *not* close the InputStream.
+     *
+     * Further, unlike other Sources, this one is not reusable.
      */
     private static class InputStreamSource extends Source {
         InputStreamSource(Resources res, InputStream is, int inputDensity) {
@@ -140,7 +380,15 @@
 
         @Override
         public ImageDecoder createImageDecoder() throws IOException {
-            return new ImageDecoder();
+
+            synchronized (this) {
+                if (mInputStream == null) {
+                    throw new IOException("Cannot reuse InputStreamSource");
+                }
+                InputStream is = mInputStream;
+                mInputStream = null;
+                return createFromStream(is, false, this);
+            }
         }
     }
 
@@ -178,7 +426,14 @@
 
         @Override
         public ImageDecoder createImageDecoder() throws IOException {
-            return new ImageDecoder();
+            synchronized (this) {
+                if (mAssetInputStream == null) {
+                    throw new IOException("Cannot reuse AssetInputStreamSource");
+                }
+                AssetInputStream ais = mAssetInputStream;
+                mAssetInputStream = null;
+                return createFromAsset(ais, this);
+            }
         }
     }
 
@@ -192,16 +447,70 @@
         final Resources mResources;
         final int       mResId;
         int             mResDensity;
+        private Object  mLock = new Object();
 
         @Override
         public Resources getResources() { return mResources; }
 
         @Override
-        public int getDensity() { return mResDensity; }
+        public int getDensity() {
+            synchronized (mLock) {
+                return mResDensity;
+            }
+        }
 
         @Override
         public ImageDecoder createImageDecoder() throws IOException {
-            return new ImageDecoder();
+            TypedValue value = new TypedValue();
+            // This is just used in order to access the underlying Asset and
+            // keep it alive.
+            InputStream is = mResources.openRawResource(mResId, value);
+
+            synchronized (mLock) {
+                if (value.density == TypedValue.DENSITY_DEFAULT) {
+                    mResDensity = DisplayMetrics.DENSITY_DEFAULT;
+                } else if (value.density != TypedValue.DENSITY_NONE) {
+                    mResDensity = value.density;
+                }
+            }
+
+            return createFromAsset((AssetInputStream) is, this);
+        }
+    }
+
+    /**
+     *  ImageDecoder will own the AssetInputStream.
+     */
+    private static ImageDecoder createFromAsset(AssetInputStream ais,
+            Source source) throws IOException {
+        ImageDecoder decoder = null;
+        try {
+            long asset = ais.getNativeAsset();
+            decoder = nCreate(asset, source);
+        } finally {
+            if (decoder == null) {
+                IoUtils.closeQuietly(ais);
+            } else {
+                decoder.mInputStream = ais;
+                decoder.mOwnsInputStream = true;
+            }
+        }
+        return decoder;
+    }
+
+    private static class AssetSource extends Source {
+        AssetSource(@NonNull AssetManager assets, @NonNull String fileName) {
+            mAssets = assets;
+            mFileName = fileName;
+        }
+
+        private final AssetManager mAssets;
+        private final String mFileName;
+
+        @Override
+        public ImageDecoder createImageDecoder() throws IOException {
+            InputStream is = mAssets.open(mFileName);
+            return createFromAsset((AssetInputStream) is, this);
         }
     }
 
@@ -214,17 +523,19 @@
 
         @Override
         public ImageDecoder createImageDecoder() throws IOException {
-            return new ImageDecoder();
+            return createFromFile(mFile, this);
         }
     }
 
     /**
-     *  Contains information about the encoded image.
+     *  Information about an encoded image.
      */
     public static class ImageInfo {
+        private final Size mSize;
         private ImageDecoder mDecoder;
 
         private ImageInfo(@NonNull ImageDecoder decoder) {
+            mSize = new Size(decoder.mWidth, decoder.mHeight);
             mDecoder = decoder;
         }
 
@@ -233,7 +544,7 @@
          */
         @NonNull
         public Size getSize() {
-            return new Size(0, 0);
+            return mSize;
         }
 
         /**
@@ -241,100 +552,271 @@
          */
         @NonNull
         public String getMimeType() {
-            return "";
+            return mDecoder.getMimeType();
         }
 
         /**
          * Whether the image is animated.
          *
-         * <p>Calling {@link #decodeDrawable} will return an
-         * {@link AnimatedImageDrawable}.</p>
+         * <p>If {@code true}, {@link #decodeDrawable decodeDrawable} will
+         * return an {@link AnimatedImageDrawable}.</p>
          */
         public boolean isAnimated() {
             return mDecoder.mAnimated;
         }
+
+        /**
+         * If known, the color space the decoded bitmap will have. Note that the
+         * output color space is not guaranteed to be the color space the bitmap
+         * is encoded with. If not known (when the config is
+         * {@link Bitmap.Config#ALPHA_8} for instance), or there is an error,
+         * it is set to null.
+         */
+        @Nullable
+        public ColorSpace getColorSpace() {
+            return mDecoder.getColorSpace();
+        }
     };
 
-    /**
-     *  Thrown if the provided data is incomplete.
+    /** @removed
+     * @deprecated Subsumed by {@link #DecodeException}.
      */
+    @Deprecated
     public static class IncompleteException extends IOException {};
 
     /**
-     *  Optional listener supplied to {@link #decodeDrawable} or
-     *  {@link #decodeBitmap}.
+     *  Interface for changing the default settings of a decode.
+     *
+     *  <p>Supply an instance to
+     *  {@link #decodeDrawable(Source, OnHeaderDecodedListener) decodeDrawable}
+     *  or {@link #decodeBitmap(Source, OnHeaderDecodedListener) decodeBitmap},
+     *  which will call {@link OnHeaderDecodedListener#onHeaderDecoded onHeaderDecoded}
+     *  (in the same thread) once the size is known. The implementation of
+     *  {@link OnHeaderDecodedListener#onHeaderDecoded onHeaderDecoded} can then
+     *  change the decode settings as desired.
      */
-    public interface OnHeaderDecodedListener {
+    public static interface OnHeaderDecodedListener {
         /**
-         *  Called when the header is decoded and the size is known.
+         *  Called by {@link ImageDecoder} when the header has been decoded and
+         *  the image size is known.
          *
-         *  @param decoder allows changing the default settings of the decode.
-         *  @param info Information about the encoded image.
-         *  @param source that created the decoder.
+         *  @param decoder the object performing the decode, for changing
+         *      its default settings.
+         *  @param info information about the encoded image.
+         *  @param source object that created {@code decoder}.
          */
-        void onHeaderDecoded(@NonNull ImageDecoder decoder,
+        public void onHeaderDecoded(@NonNull ImageDecoder decoder,
                 @NonNull ImageInfo info, @NonNull Source source);
 
     };
 
-    /**
-     *  An Exception was thrown reading the {@link Source}.
+    /** @removed
+     * @deprecated Replaced by {@link #DecodeException#SOURCE_EXCEPTION}.
      */
+    @Deprecated
     public static final int ERROR_SOURCE_EXCEPTION  = 1;
 
-    /**
-     *  The encoded data was incomplete.
+    /** @removed
+     * @deprecated Replaced by {@link #DecodeException#SOURCE_INCOMPLETE}.
      */
+    @Deprecated
     public static final int ERROR_SOURCE_INCOMPLETE = 2;
 
-    /**
-     *  The encoded data contained an error.
+    /** @removed
+     * @deprecated Replaced by {@link #DecodeException#SOURCE_MALFORMED_DATA}.
      */
+    @Deprecated
     public static final int ERROR_SOURCE_ERROR      = 3;
 
-    @Retention(SOURCE)
-    public @interface Error {}
-
     /**
-     *  Optional listener supplied to the ImageDecoder.
-     *
-     *  Without this listener, errors will throw {@link java.io.IOException}.
+     *  Information about an interrupted decode.
      */
-    public interface OnPartialImageListener {
+    public static final class DecodeException extends IOException {
         /**
-         *  Called when there is only a partial image to display.
-         *
-         *  If decoding is interrupted after having decoded a partial image,
-         *  this listener lets the client know that and allows them to
-         *  optionally finish the rest of the decode/creation process to create
-         *  a partial {@link Drawable}/{@link Bitmap}.
-         *
-         *  @param error indicating what interrupted the decode.
-         *  @param source that had the error.
-         *  @return True to create and return a {@link Drawable}/{@link Bitmap}
-         *      with partial data. False (which is the default) to abort the
-         *      decode and throw {@link java.io.IOException}.
+         *  An Exception was thrown reading the {@link Source}.
          */
-        boolean onPartialImage(@Error int error, @NonNull Source source);
-    }
+        public static final int SOURCE_EXCEPTION  = 1;
 
-    private boolean mAnimated;
-    private Rect mOutPaddingRect;
+        /**
+         *  The encoded data was incomplete.
+         */
+        public static final int SOURCE_INCOMPLETE = 2;
 
-    public ImageDecoder() {
-        mAnimated = true; // This is too avoid throwing an exception in AnimatedImageDrawable
+        /**
+         *  The encoded data contained an error.
+         */
+        public static final int SOURCE_MALFORMED_DATA      = 3;
+
+        /** @hide **/
+        @Retention(SOURCE)
+        @IntDef(value = { SOURCE_EXCEPTION, SOURCE_INCOMPLETE, SOURCE_MALFORMED_DATA },
+                prefix = {"SOURCE_"})
+        public @interface Error {};
+
+        @Error final int mError;
+        @NonNull final Source mSource;
+
+        DecodeException(@Error int error, @Nullable Throwable cause, @NonNull Source source) {
+            super(errorMessage(error, cause), cause);
+            mError = error;
+            mSource = source;
+        }
+
+        /**
+         * Private method called by JNI.
+         */
+        @SuppressWarnings("unused")
+        DecodeException(@Error int error, @Nullable String msg, @Nullable Throwable cause,
+                @NonNull Source source) {
+            super(msg + errorMessage(error, cause), cause);
+            mError = error;
+            mSource = source;
+        }
+
+        /**
+         *  Retrieve the reason that decoding was interrupted.
+         *
+         *  <p>If the error is {@link #SOURCE_EXCEPTION}, the underlying
+         *  {@link java.lang.Throwable} can be retrieved with
+         *  {@link java.lang.Throwable#getCause}.</p>
+         */
+        @Error
+        public int getError() {
+            return mError;
+        }
+
+        /**
+         *  Retrieve the {@link Source Source} that was interrupted.
+         *
+         *  <p>This can be used for equality checking to find the Source which
+         *  failed to completely decode.</p>
+         */
+        @NonNull
+        public Source getSource() {
+            return mSource;
+        }
+
+        private static String errorMessage(@Error int error, @Nullable Throwable cause) {
+            switch (error) {
+                case SOURCE_EXCEPTION:
+                    return "Exception in input: " + cause;
+                case SOURCE_INCOMPLETE:
+                    return "Input was incomplete.";
+                case SOURCE_MALFORMED_DATA:
+                    return "Input contained an error.";
+                default:
+                    return "";
+            }
+        }
     }
 
     /**
-     * Create a new {@link Source} from an asset.
-     * @hide
+     *  Interface for inspecting a {@link DecodeException DecodeException}
+     *  and potentially preventing it from being thrown.
+     *
+     *  <p>If an instance is passed to
+     *  {@link #setOnPartialImageListener setOnPartialImageListener}, a
+     *  {@link DecodeException DecodeException} that would otherwise have been
+     *  thrown can be inspected inside
+     *  {@link OnPartialImageListener#onPartialImage onPartialImage}.
+     *  If {@link OnPartialImageListener#onPartialImage onPartialImage} returns
+     *  {@code true}, a partial image will be created.
+     */
+    public static interface OnPartialImageListener {
+        /**
+         *  Called by {@link ImageDecoder} when there is only a partial image to
+         *  display.
+         *
+         *  <p>If decoding is interrupted after having decoded a partial image,
+         *  this method will be called. The implementation can inspect the
+         *  {@link DecodeException DecodeException} and optionally finish the
+         *  rest of the decode creation process to create a partial {@link Drawable}
+         *  or {@link Bitmap}.
+         *
+         *  @param exception exception containing information about the
+         *      decode interruption.
+         *  @return {@code true} to create and return a {@link Drawable} or
+         *      {@link Bitmap} with partial data. {@code false} (which is the
+         *      default) to abort the decode and throw {@code e}. Any undecoded
+         *      lines in the image will be blank.
+         */
+        boolean onPartialImage(@NonNull DecodeException exception);
+    };
+
+    // Fields
+    private long          mNativePtr;
+    private final int     mWidth;
+    private final int     mHeight;
+    private final boolean mAnimated;
+    private final boolean mIsNinePatch;
+
+    private int        mDesiredWidth;
+    private int        mDesiredHeight;
+    private int        mAllocator = ALLOCATOR_DEFAULT;
+    private boolean    mUnpremultipliedRequired = false;
+    private boolean    mMutable = false;
+    private boolean    mConserveMemory = false;
+    private boolean    mDecodeAsAlphaMask = false;
+    private ColorSpace mDesiredColorSpace = null;
+    private Rect       mCropRect;
+    private Rect       mOutPaddingRect;
+    private Source     mSource;
+
+    private PostProcessor          mPostProcessor;
+    private OnPartialImageListener mOnPartialImageListener;
+
+    // Objects for interacting with the input.
+    private InputStream         mInputStream;
+    private boolean             mOwnsInputStream;
+    private byte[]              mTempStorage;
+    private AssetFileDescriptor mAssetFd;
+    private final AtomicBoolean mClosed = new AtomicBoolean();
+    private final CloseGuard    mCloseGuard = CloseGuard.get();
+
+    /**
+     * Private constructor called by JNI. {@link #close} must be
+     * called after decoding to delete native resources.
+     */
+    @SuppressWarnings("unused")
+    private ImageDecoder(long nativePtr, int width, int height,
+            boolean animated, boolean isNinePatch) {
+        mNativePtr = nativePtr;
+        mWidth = width;
+        mHeight = height;
+        mDesiredWidth = width;
+        mDesiredHeight = height;
+        mAnimated = animated;
+        mIsNinePatch = isNinePatch;
+        mCloseGuard.open("close");
+    }
+
+    @Override
+    protected void finalize() throws Throwable {
+        try {
+            if (mCloseGuard != null) {
+                mCloseGuard.warnIfOpen();
+            }
+
+            // Avoid closing these in finalizer.
+            mInputStream = null;
+            mAssetFd = null;
+
+            close();
+        } finally {
+            super.finalize();
+        }
+    }
+
+    /**
+     * Create a new {@link Source Source} from a resource.
      *
      * @param res the {@link Resources} object containing the image data.
      * @param resId resource ID of the image data.
-     *      // FIXME: Can be an @DrawableRes?
      * @return a new Source object, which can be passed to
-     *      {@link #decodeDrawable} or {@link #decodeBitmap}.
+     *      {@link #decodeDrawable decodeDrawable} or
+     *      {@link #decodeBitmap decodeBitmap}.
      */
+    @AnyThread
     @NonNull
     public static Source createSource(@NonNull Resources res, int resId)
     {
@@ -342,34 +824,71 @@
     }
 
     /**
-     * Create a new {@link Source} from a {@link android.net.Uri}.
+     * Create a new {@link Source Source} from a {@link android.net.Uri}.
+     *
+     * <h5>Accepts the following URI schemes:</h5>
+     * <ul>
+     * <li>content ({@link ContentResolver#SCHEME_CONTENT})</li>
+     * <li>android.resource ({@link ContentResolver#SCHEME_ANDROID_RESOURCE})</li>
+     * <li>file ({@link ContentResolver#SCHEME_FILE})</li>
+     * </ul>
      *
      * @param cr to retrieve from.
      * @param uri of the image file.
      * @return a new Source object, which can be passed to
-     *      {@link #decodeDrawable} or {@link #decodeBitmap}.
+     *      {@link #decodeDrawable decodeDrawable} or
+     *      {@link #decodeBitmap decodeBitmap}.
      */
+    @AnyThread
     @NonNull
     public static Source createSource(@NonNull ContentResolver cr,
             @NonNull Uri uri) {
-        return new ContentResolverSource(cr, uri);
+        return new ContentResolverSource(cr, uri, null);
     }
 
     /**
-     * Create a new {@link Source} from a byte array.
+     * Provide Resources for density scaling.
+     *
+     * @hide
+     */
+    @AnyThread
+    @NonNull
+    public static Source createSource(@NonNull ContentResolver cr,
+            @NonNull Uri uri, @Nullable Resources res) {
+        return new ContentResolverSource(cr, uri, res);
+    }
+
+    /**
+     * Create a new {@link Source Source} from a file in the "assets" directory.
+     */
+    @AnyThread
+    @NonNull
+    public static Source createSource(@NonNull AssetManager assets, @NonNull String fileName) {
+        return new AssetSource(assets, fileName);
+    }
+
+    /**
+     * Create a new {@link Source Source} from a byte array.
      *
      * @param data byte array of compressed image data.
      * @param offset offset into data for where the decoder should begin
      *      parsing.
      * @param length number of bytes, beginning at offset, to parse.
+     * @return a new Source object, which can be passed to
+     *      {@link #decodeDrawable decodeDrawable} or
+     *      {@link #decodeBitmap decodeBitmap}.
      * @throws NullPointerException if data is null.
      * @throws ArrayIndexOutOfBoundsException if offset and length are
      *      not within data.
      * @hide
      */
+    @AnyThread
     @NonNull
     public static Source createSource(@NonNull byte[] data, int offset,
             int length) throws ArrayIndexOutOfBoundsException {
+        if (data == null) {
+            throw new NullPointerException("null byte[] in createSource!");
+        }
         if (offset < 0 || length < 0 || offset >= data.length ||
                 offset + length > data.length) {
             throw new ArrayIndexOutOfBoundsException(
@@ -382,21 +901,29 @@
      * See {@link #createSource(byte[], int, int).
      * @hide
      */
+    @AnyThread
     @NonNull
     public static Source createSource(@NonNull byte[] data) {
         return createSource(data, 0, data.length);
     }
 
     /**
-     * Create a new {@link Source} from a {@link java.nio.ByteBuffer}.
+     * Create a new {@link Source Source} from a {@link java.nio.ByteBuffer}.
      *
-     * <p>The returned {@link Source} effectively takes ownership of the
-     * {@link java.nio.ByteBuffer}; i.e. no other code should modify it after
-     * this call.</p>
+     * <p>Decoding will start from {@link java.nio.ByteBuffer#position() buffer.position()}.
+     * The position of {@code buffer} will not be affected.</p>
      *
-     * Decoding will start from {@link java.nio.ByteBuffer#position()}. The
-     * position after decoding is undefined.
+     * <p>Note: If this {@code Source} is passed to {@link #decodeDrawable decodeDrawable},
+     * and the encoded image is animated, the returned {@link AnimatedImageDrawable}
+     * will continue reading from the {@code buffer}, so its contents must not
+     * be modified, even after the {@code AnimatedImageDrawable} is returned.
+     * {@code buffer}'s contents should never be modified during decode.</p>
+     *
+     * @return a new Source object, which can be passed to
+     *      {@link #decodeDrawable decodeDrawable} or
+     *      {@link #decodeBitmap decodeBitmap}.
      */
+    @AnyThread
     @NonNull
     public static Source createSource(@NonNull ByteBuffer buffer) {
         return new ByteBufferSource(buffer);
@@ -404,23 +931,39 @@
 
     /**
      * Internal API used to generate bitmaps for use by Drawables (i.e. BitmapDrawable)
+     *
+     * <p>Unlike other Sources, this one cannot be reused.</p>
+     *
      * @hide
      */
+    @AnyThread
+    @NonNull
     public static Source createSource(Resources res, InputStream is) {
         return new InputStreamSource(res, is, Bitmap.getDefaultDensity());
     }
 
     /**
      * Internal API used to generate bitmaps for use by Drawables (i.e. BitmapDrawable)
+     *
+     * <p>Unlike other Sources, this one cannot be reused.</p>
+     *
      * @hide
      */
+    @AnyThread
+    @TestApi
+    @NonNull
     public static Source createSource(Resources res, InputStream is, int density) {
         return new InputStreamSource(res, is, density);
     }
 
     /**
-     * Create a new {@link Source} from a {@link java.io.File}.
+     * Create a new {@link Source Source} from a {@link java.io.File}.
+     *
+     * @return a new Source object, which can be passed to
+     *      {@link #decodeDrawable decodeDrawable} or
+     *      {@link #decodeBitmap decodeBitmap}.
      */
+    @AnyThread
     @NonNull
     public static Source createSource(@NonNull File file) {
         return new FileSource(file);
@@ -431,39 +974,142 @@
      *
      *  <p>This takes an input that functions like
      *  {@link BitmapFactory.Options#inSampleSize}. It returns a width and
-     *  height that can be acheived by sampling the encoded image. Other widths
+     *  height that can be achieved by sampling the encoded image. Other widths
      *  and heights may be supported, but will require an additional (internal)
      *  scaling step. Such internal scaling is *not* supported with
-     *  {@link #setRequireUnpremultiplied} set to {@code true}.</p>
+     *  {@link #setUnpremultipliedRequired} set to {@code true}.</p>
      *
      *  @param sampleSize Sampling rate of the encoded image.
      *  @return {@link android.util.Size} of the width and height after
      *      sampling.
+     *
+     *  @hide
      */
     @NonNull
     public Size getSampledSize(int sampleSize) {
-        return new Size(0, 0);
+        if (sampleSize <= 0) {
+            throw new IllegalArgumentException("sampleSize must be positive! "
+                    + "provided " + sampleSize);
+        }
+        if (mNativePtr == 0) {
+            throw new IllegalStateException("ImageDecoder is closed!");
+        }
+
+        return nGetSampledSize(mNativePtr, sampleSize);
     }
 
     // Modifiers
-    /**
-     *  Resize the output to have the following size.
-     *
-     *  @param width must be greater than 0.
-     *  @param height must be greater than 0.
+    /** @removed
+     * @deprecated Renamed to {@link #setTargetSize}.
      */
-    public void setResize(int width, int height) {
+    @Deprecated
+    public ImageDecoder setResize(int width, int height) {
+        this.setTargetSize(width, height);
+        return this;
     }
 
     /**
-     *  Resize based on a sample size.
+     *  Specify the size of the output {@link Drawable} or {@link Bitmap}.
      *
-     *  <p>This has the same effect as passing the result of
-     *  {@link #getSampledSize} to {@link #setResize(int, int)}.</p>
+     *  <p>By default, the output size will match the size of the encoded
+     *  image, which can be retrieved from the {@link ImageInfo ImageInfo} in
+     *  {@link OnHeaderDecodedListener#onHeaderDecoded onHeaderDecoded}.</p>
      *
-     *  @param sampleSize Sampling rate of the encoded image.
+     *  <p>This will sample or scale the output to an arbitrary size that may
+     *  be smaller or larger than the encoded size.</p>
+     *
+     *  <p>Only the last call to this or {@link #setTargetSampleSize} is
+     *  respected.</p>
+     *
+     *  <p>Like all setters on ImageDecoder, this must be called inside
+     *  {@link OnHeaderDecodedListener#onHeaderDecoded onHeaderDecoded}.</p>
+     *
+     *  @param width width in pixels of the output, must be greater than 0
+     *  @param height height in pixels of the output, must be greater than 0
      */
-    public void setResize(int sampleSize) {
+    public void setTargetSize(@Px @IntRange(from = 1) int width,
+                              @Px @IntRange(from = 1) int height) {
+        if (width <= 0 || height <= 0) {
+            throw new IllegalArgumentException("Dimensions must be positive! "
+                    + "provided (" + width + ", " + height + ")");
+        }
+
+        mDesiredWidth = width;
+        mDesiredHeight = height;
+    }
+
+    /** @removed
+     * @deprecated Renamed to {@link #setTargetSampleSize}.
+     */
+    @Deprecated
+    public ImageDecoder setResize(int sampleSize) {
+        this.setTargetSampleSize(sampleSize);
+        return this;
+    }
+
+    private int getTargetDimension(int original, int sampleSize, int computed) {
+        // Sampling will never result in a smaller size than 1.
+        if (sampleSize >= original) {
+            return 1;
+        }
+
+        // Use integer divide to find the desired size. If that is what
+        // getSampledSize computed, that is the size to use.
+        int target = original / sampleSize;
+        if (computed == target) {
+            return computed;
+        }
+
+        // If sampleSize does not divide evenly into original, the decoder
+        // may round in either direction. It just needs to get a result that
+        // is close.
+        int reverse = computed * sampleSize;
+        if (Math.abs(reverse - original) < sampleSize) {
+            // This is the size that can be decoded most efficiently.
+            return computed;
+        }
+
+        // The decoder could not get close (e.g. it is a DNG image).
+        return target;
+    }
+
+    /**
+     *  Set the target size with a sampleSize.
+     *
+     *  <p>By default, the output size will match the size of the encoded
+     *  image, which can be retrieved from the {@link ImageInfo ImageInfo} in
+     *  {@link OnHeaderDecodedListener#onHeaderDecoded onHeaderDecoded}.</p>
+     *
+     *  <p>Requests the decoder to subsample the original image, returning a
+     *  smaller image to save memory. The {@code sampleSize} is the number of pixels
+     *  in either dimension that correspond to a single pixel in the output.
+     *  For example, {@code sampleSize == 4} returns an image that is 1/4 the
+     *  width/height of the original, and 1/16 the number of pixels.</p>
+     *
+     *  <p>Must be greater than or equal to 1.</p>
+     *
+     *  <p>This has the same effect as calling {@link #setTargetSize} with
+     *  dimensions based on the {@code sampleSize}. Unlike dividing the original
+     *  width and height by the {@code sampleSize} manually, calling this method
+     *  allows {@code ImageDecoder} to round in the direction that it can do most
+     *  efficiently.</p>
+     *
+     *  <p>Only the last call to this or {@link #setTargetSize} is respected.</p>
+     *
+     *  <p>Like all setters on ImageDecoder, this must be called inside
+     *  {@link OnHeaderDecodedListener#onHeaderDecoded onHeaderDecoded}.</p>
+     *
+     *  @param sampleSize sampling rate of the encoded image.
+     */
+    public void setTargetSampleSize(@IntRange(from = 1) int sampleSize) {
+        Size size = this.getSampledSize(sampleSize);
+        int targetWidth = getTargetDimension(mWidth, sampleSize, size.getWidth());
+        int targetHeight = getTargetDimension(mHeight, sampleSize, size.getHeight());
+        this.setTargetSize(targetWidth, targetHeight);
+    }
+
+    private boolean requestedResize() {
+        return mWidth != mDesiredWidth || mHeight != mDesiredHeight;
     }
 
     // These need to stay in sync with ImageDecoder.cpp's Allocator enum.
@@ -473,14 +1119,15 @@
      *  Will typically result in a {@link Bitmap.Config#HARDWARE}
      *  allocation, but may be software for small images. In addition, this will
      *  switch to software when HARDWARE is incompatible, e.g.
-     *  {@link #setMutable}, {@link #setAsAlphaMask}.
+     *  {@link #setMutableRequired setMutableRequired(true)} or
+     *  {@link #setDecodeAsAlphaMaskEnabled setDecodeAsAlphaMaskEnabled(true)}.
      */
     public static final int ALLOCATOR_DEFAULT = 0;
 
     /**
      *  Use a software allocation for the pixel memory.
      *
-     *  Useful for drawing to a software {@link Canvas} or for
+     *  <p>Useful for drawing to a software {@link Canvas} or for
      *  accessing the pixels on the final output.
      */
     public static final int ALLOCATOR_SOFTWARE = 1;
@@ -488,92 +1135,177 @@
     /**
      *  Use shared memory for the pixel memory.
      *
-     *  Useful for sharing across processes.
+     *  <p>Useful for sharing across processes.
      */
     public static final int ALLOCATOR_SHARED_MEMORY = 2;
 
     /**
      *  Require a {@link Bitmap.Config#HARDWARE} {@link Bitmap}.
      *
-     *  When this is combined with incompatible options, like
-     *  {@link #setMutable} or {@link #setAsAlphaMask}, {@link #decodeDrawable}
-     *  / {@link #decodeBitmap} will throw an
-     *  {@link java.lang.IllegalStateException}.
+     *  <p>When this is combined with incompatible options, like
+     *  {@link #setMutableRequired setMutableRequired(true)} or
+     *  {@link #setDecodeAsAlphaMaskEnabled setDecodeAsAlphaMaskEnabled(true)},
+     *  {@link #decodeDrawable decodeDrawable} or {@link #decodeBitmap decodeBitmap}
+     *  will throw an {@link java.lang.IllegalStateException}.
      */
     public static final int ALLOCATOR_HARDWARE = 3;
 
     /** @hide **/
     @Retention(SOURCE)
+    @IntDef(value = { ALLOCATOR_DEFAULT, ALLOCATOR_SOFTWARE,
+              ALLOCATOR_SHARED_MEMORY, ALLOCATOR_HARDWARE },
+              prefix = {"ALLOCATOR_"})
     public @interface Allocator {};
 
     /**
      *  Choose the backing for the pixel memory.
      *
-     *  This is ignored for animated drawables.
+     *  <p>This is ignored for animated drawables.</p>
+     *
+     *  <p>Like all setters on ImageDecoder, this must be called inside
+     *  {@link OnHeaderDecodedListener#onHeaderDecoded onHeaderDecoded}.</p>
      *
      *  @param allocator Type of allocator to use.
      */
-    public ImageDecoder setAllocator(@Allocator int allocator) {
-        return this;
+    public void setAllocator(@Allocator int allocator) {
+        if (allocator < ALLOCATOR_DEFAULT || allocator > ALLOCATOR_HARDWARE) {
+            throw new IllegalArgumentException("invalid allocator " + allocator);
+        }
+        mAllocator = allocator;
+    }
+
+    /**
+     *  Return the allocator for the pixel memory.
+     */
+    @Allocator
+    public int getAllocator() {
+        return mAllocator;
     }
 
     /**
      *  Specify whether the {@link Bitmap} should have unpremultiplied pixels.
      *
-     *  By default, ImageDecoder will create a {@link Bitmap} with
+     *  <p>By default, ImageDecoder will create a {@link Bitmap} with
      *  premultiplied pixels, which is required for drawing with the
      *  {@link android.view.View} system (i.e. to a {@link Canvas}). Calling
      *  this method with a value of {@code true} will result in
      *  {@link #decodeBitmap} returning a {@link Bitmap} with unpremultiplied
-     *  pixels. See {@link Bitmap#isPremultiplied}. This is incompatible with
-     *  {@link #decodeDrawable}; attempting to decode an unpremultiplied
-     *  {@link Drawable} will throw an {@link java.lang.IllegalStateException}.
+     *  pixels. See {@link Bitmap#isPremultiplied Bitmap.isPremultiplied()}.
+     *  This is incompatible with {@link #decodeDrawable decodeDrawable};
+     *  attempting to decode an unpremultiplied {@link Drawable} will throw an
+     *  {@link java.lang.IllegalStateException}. </p>
+     *
+     *  <p>Like all setters on ImageDecoder, this must be called inside
+     *  {@link OnHeaderDecodedListener#onHeaderDecoded onHeaderDecoded}.</p>
      */
-    public ImageDecoder setRequireUnpremultiplied(boolean requireUnpremultiplied) {
+    public void setUnpremultipliedRequired(boolean unpremultipliedRequired) {
+        mUnpremultipliedRequired = unpremultipliedRequired;
+    }
+
+    /** @removed
+     * @deprecated Renamed to {@link #setUnpremultipliedRequired}.
+     */
+    @Deprecated
+    public ImageDecoder setRequireUnpremultiplied(boolean unpremultipliedRequired) {
+        this.setUnpremultipliedRequired(unpremultipliedRequired);
         return this;
     }
 
     /**
+     *  Return whether the {@link Bitmap} will have unpremultiplied pixels.
+     */
+    public boolean isUnpremultipliedRequired() {
+        return mUnpremultipliedRequired;
+    }
+
+    /** @removed
+     * @deprecated Renamed to {@link #isUnpremultipliedRequired}.
+     */
+    @Deprecated
+    public boolean getRequireUnpremultiplied() {
+        return this.isUnpremultipliedRequired();
+    }
+
+    /**
      *  Modify the image after decoding and scaling.
      *
      *  <p>This allows adding effects prior to returning a {@link Drawable} or
      *  {@link Bitmap}. For a {@code Drawable} or an immutable {@code Bitmap},
      *  this is the only way to process the image after decoding.</p>
      *
+     *  <p>If combined with {@link #setTargetSize} and/or {@link #setCrop},
+     *  {@link PostProcessor#onPostProcess} occurs last.</p>
+     *
      *  <p>If set on a nine-patch image, the nine-patch data is ignored.</p>
      *
      *  <p>For an animated image, the drawing commands drawn on the
      *  {@link Canvas} will be recorded immediately and then applied to each
      *  frame.</p>
+     *
+     *  <p>Like all setters on ImageDecoder, this must be called inside
+     *  {@link OnHeaderDecodedListener#onHeaderDecoded onHeaderDecoded}.</p>
+     *
      */
-    public ImageDecoder setPostProcessor(@Nullable PostProcessor p) {
-        return this;
+    public void setPostProcessor(@Nullable PostProcessor postProcessor) {
+        mPostProcessor = postProcessor;
+    }
+
+    /**
+     *  Return the {@link PostProcessor} currently set.
+     */
+    @Nullable
+    public PostProcessor getPostProcessor() {
+        return mPostProcessor;
     }
 
     /**
      *  Set (replace) the {@link OnPartialImageListener} on this object.
      *
-     *  Will be called if there is an error in the input. Without one, a
-     *  partial {@link Bitmap} will be created.
+     *  <p>Will be called if there is an error in the input. Without one, an
+     *  error will result in an {@code Exception} being thrown.</p>
+     *
+     *  <p>Like all setters on ImageDecoder, this must be called inside
+     *  {@link OnHeaderDecodedListener#onHeaderDecoded onHeaderDecoded}.</p>
+     *
      */
-    public ImageDecoder setOnPartialImageListener(@Nullable OnPartialImageListener l) {
-        return this;
+    public void setOnPartialImageListener(@Nullable OnPartialImageListener listener) {
+        mOnPartialImageListener = listener;
+    }
+
+    /**
+     *  Return the {@link OnPartialImageListener OnPartialImageListener} currently set.
+     */
+    @Nullable
+    public OnPartialImageListener getOnPartialImageListener() {
+        return mOnPartialImageListener;
     }
 
     /**
      *  Crop the output to {@code subset} of the (possibly) scaled image.
      *
      *  <p>{@code subset} must be contained within the size set by
-     *  {@link #setResize} or the bounds of the image if setResize was not
-     *  called. Otherwise an {@link IllegalStateException} will be thrown by
-     *  {@link #decodeDrawable}/{@link #decodeBitmap}.</p>
+     *  {@link #setTargetSize} or the bounds of the image if setTargetSize was
+     *  not called. Otherwise an {@link IllegalStateException} will be thrown by
+     *  {@link #decodeDrawable decodeDrawable}/{@link #decodeBitmap decodeBitmap}.</p>
      *
      *  <p>NOT intended as a replacement for
-     *  {@link BitmapRegionDecoder#decodeRegion}. This supports all formats,
-     *  but merely crops the output.</p>
+     *  {@link BitmapRegionDecoder#decodeRegion BitmapRegionDecoder.decodeRegion()}.
+     *  This supports all formats, but merely crops the output.</p>
+     *
+     *  <p>Like all setters on ImageDecoder, this must be called inside
+     *  {@link OnHeaderDecodedListener#onHeaderDecoded onHeaderDecoded}.</p>
+     *
      */
-    public ImageDecoder setCrop(@Nullable Rect subset) {
-        return this;
+    public void setCrop(@Nullable Rect subset) {
+        mCropRect = subset;
+    }
+
+    /**
+     *  Return the cropping rectangle, if set.
+     */
+    @Nullable
+    public Rect getCrop() {
+        return mCropRect;
     }
 
     /**
@@ -582,43 +1314,122 @@
      *  If the image is a nine patch, this Rect will be set to the padding
      *  rectangle during decode. Otherwise it will not be modified.
      *
+     *  <p>Like all setters on ImageDecoder, this must be called inside
+     *  {@link OnHeaderDecodedListener#onHeaderDecoded onHeaderDecoded}.</p>
+     *
      *  @hide
      */
-    public ImageDecoder setOutPaddingRect(@NonNull Rect outPadding) {
+    public void setOutPaddingRect(@NonNull Rect outPadding) {
         mOutPaddingRect = outPadding;
-        return this;
     }
 
     /**
      *  Specify whether the {@link Bitmap} should be mutable.
      *
-     *  <p>By default, a {@link Bitmap} created will be immutable, but that can
-     *  be changed with this call.</p>
+     *  <p>By default, a {@link Bitmap} created by {@link #decodeBitmap decodeBitmap}
+     *  will be immutable i.e. {@link Bitmap#isMutable() Bitmap.isMutable()} returns
+     *  {@code false}. This can be changed with {@code setMutableRequired(true)}.
      *
      *  <p>Mutable Bitmaps are incompatible with {@link #ALLOCATOR_HARDWARE},
      *  because {@link Bitmap.Config#HARDWARE} Bitmaps cannot be mutable.
      *  Attempting to combine them will throw an
      *  {@link java.lang.IllegalStateException}.</p>
      *
-     *  <p>Mutable Bitmaps are also incompatible with {@link #decodeDrawable},
+     *  <p>Mutable Bitmaps are also incompatible with {@link #decodeDrawable decodeDrawable},
      *  which would require retrieving the Bitmap from the returned Drawable in
      *  order to modify. Attempting to decode a mutable {@link Drawable} will
      *  throw an {@link java.lang.IllegalStateException}.</p>
+     *
+     *  <p>Like all setters on ImageDecoder, this must be called inside
+     *  {@link OnHeaderDecodedListener#onHeaderDecoded onHeaderDecoded}.</p>
      */
+    public void setMutableRequired(boolean mutable) {
+        mMutable = mutable;
+    }
+
+    /** @removed
+     * @deprecated Renamed to {@link #setMutableRequired}.
+     */
+    @Deprecated
     public ImageDecoder setMutable(boolean mutable) {
+        this.setMutableRequired(mutable);
         return this;
     }
 
     /**
-     *  Specify whether to potentially save RAM at the expense of quality.
-     *
-     *  Setting this to {@code true} may result in a {@link Bitmap} with a
-     *  denser {@link Bitmap.Config}, depending on the image. For example, for
-     *  an opaque {@link Bitmap}, this may result in a {@link Bitmap.Config}
-     *  with no alpha information.
+     *  Return whether the decoded {@link Bitmap} will be mutable.
      */
-    public ImageDecoder setPreferRamOverQuality(boolean preferRamOverQuality) {
-        return this;
+    public boolean isMutableRequired() {
+        return mMutable;
+    }
+
+    /** @removed
+     * @deprecated Renamed to {@link #isMutableRequired}.
+     */
+    @Deprecated
+    public boolean getMutable() {
+        return this.isMutableRequired();
+    }
+
+    /**
+     * Save memory if possible by using a denser {@link Bitmap.Config} at the
+     * cost of some image quality.
+     *
+     * <p>For example an opaque 8-bit image may be compressed into an
+     * {@link Bitmap.Config#RGB_565} configuration, sacrificing image
+     * quality to save memory.
+     */
+    public static final int MEMORY_POLICY_LOW_RAM = 0;
+
+    /**
+     * Use the most natural {@link Bitmap.Config} for the internal {@link Bitmap}.
+     *
+     * <p>This is the recommended default for most applications and usages. This
+     * will use the closest {@link Bitmap.Config} for the encoded source. If the
+     * encoded source does not exactly match any {@link Bitmap.Config}, the next
+     * highest quality {@link Bitmap.Config} will be used avoiding any loss in
+     * image quality.
+     */
+    public static final int MEMORY_POLICY_DEFAULT  = 1;
+
+    /** @hide **/
+    @Retention(SOURCE)
+    @IntDef(value = { MEMORY_POLICY_DEFAULT, MEMORY_POLICY_LOW_RAM },
+              prefix = {"MEMORY_POLICY_"})
+    public @interface MemoryPolicy {};
+
+    /**
+     *  Specify the memory policy for the decoded {@link Bitmap}.
+     *
+     *  <p>Like all setters on ImageDecoder, this must be called inside
+     *  {@link OnHeaderDecodedListener#onHeaderDecoded onHeaderDecoded}.</p>
+     */
+    public void setMemorySizePolicy(@MemoryPolicy int policy) {
+        mConserveMemory = (policy == MEMORY_POLICY_LOW_RAM);
+    }
+
+    /**
+     *  Retrieve the memory policy for the decoded {@link Bitmap}.
+     */
+    @MemoryPolicy
+    public int getMemorySizePolicy() {
+        return mConserveMemory ? MEMORY_POLICY_LOW_RAM : MEMORY_POLICY_DEFAULT;
+    }
+
+    /** @removed
+     * @deprecated Replaced by {@link #setMemorySizePolicy}.
+     */
+    @Deprecated
+    public void setConserveMemory(boolean conserveMemory) {
+        mConserveMemory = conserveMemory;
+    }
+
+    /** @removed
+     * @deprecated Replaced by {@link #getMemorySizePolicy}.
+     */
+    @Deprecated
+    public boolean getConserveMemory() {
+        return mConserveMemory;
     }
 
     /**
@@ -628,77 +1439,467 @@
      *  with only one channel, treat that channel as alpha. Otherwise this call has
      *  no effect.</p>
      *
-     *  <p>setAsAlphaMask is incompatible with {@link #ALLOCATOR_HARDWARE}. Trying to
-     *  combine them will result in {@link #decodeDrawable}/
-     *  {@link #decodeBitmap} throwing an
+     *  <p>This is incompatible with {@link #ALLOCATOR_HARDWARE}. Trying to
+     *  combine them will result in {@link #decodeDrawable decodeDrawable}/
+     *  {@link #decodeBitmap decodeBitmap} throwing an
      *  {@link java.lang.IllegalStateException}.</p>
+     *
+     *  <p>Like all setters on ImageDecoder, this must be called inside
+     *  {@link OnHeaderDecodedListener#onHeaderDecoded onHeaderDecoded}.</p>
      */
-    public ImageDecoder setAsAlphaMask(boolean asAlphaMask) {
+    public void setDecodeAsAlphaMaskEnabled(boolean enabled) {
+        mDecodeAsAlphaMask = enabled;
+    }
+
+    /** @removed
+     * @deprecated Renamed to {@link #setDecodeAsAlphaMaskEnabled}.
+     */
+    @Deprecated
+    public ImageDecoder setDecodeAsAlphaMask(boolean enabled) {
+        this.setDecodeAsAlphaMaskEnabled(enabled);
         return this;
     }
 
+    /** @removed
+     * @deprecated Renamed to {@link #setDecodeAsAlphaMaskEnabled}.
+     */
+    @Deprecated
+    public ImageDecoder setAsAlphaMask(boolean asAlphaMask) {
+        this.setDecodeAsAlphaMask(asAlphaMask);
+        return this;
+    }
+
+    /**
+     *  Return whether to treat single channel input as alpha.
+     *
+     *  <p>This returns whether {@link #setDecodeAsAlphaMaskEnabled} was set to
+     *  {@code true}. It may still return {@code true} even if the image has
+     *  more than one channel and therefore will not be treated as an alpha
+     *  mask.</p>
+     */
+    public boolean isDecodeAsAlphaMaskEnabled() {
+        return mDecodeAsAlphaMask;
+    }
+
+    /** @removed
+     * @deprecated Renamed to {@link #isDecodeAsAlphaMaskEnabled}.
+     */
+    @Deprecated
+    public boolean getDecodeAsAlphaMask() {
+        return mDecodeAsAlphaMask;
+    }
+
+    /** @removed
+     * @deprecated Renamed to {@link #isDecodeAsAlphaMaskEnabled}.
+     */
+    @Deprecated
+    public boolean getAsAlphaMask() {
+        return this.getDecodeAsAlphaMask();
+    }
+
+    /**
+     * Specify the desired {@link ColorSpace} for the output.
+     *
+     * <p>If non-null, the decoder will try to decode into {@code colorSpace}.
+     * If it is null, which is the default, or the request cannot be met, the
+     * decoder will pick either the color space embedded in the image or the
+     * {@link ColorSpace} best suited for the requested image configuration
+     * (for instance {@link ColorSpace.Named#SRGB sRGB} for the
+     * {@link Bitmap.Config#ARGB_8888} configuration).</p>
+     *
+     * <p>{@link Bitmap.Config#RGBA_F16} always uses the
+     * {@link ColorSpace.Named#LINEAR_EXTENDED_SRGB scRGB} color space.
+     * Bitmaps in other configurations without an embedded color space are
+     * assumed to be in the {@link ColorSpace.Named#SRGB sRGB} color space.</p>
+     *
+     * <p class="note">Only {@link ColorSpace.Model#RGB} color spaces are
+     * currently supported. An <code>IllegalArgumentException</code> will
+     * be thrown by {@link #decodeDrawable decodeDrawable}/
+     * {@link #decodeBitmap decodeBitmap} when setting a non-RGB color space
+     * such as {@link ColorSpace.Named#CIE_LAB Lab}.</p>
+     *
+     * <p class="note">The specified color space's transfer function must be
+     * an {@link ColorSpace.Rgb.TransferParameters ICC parametric curve}. An
+     * <code>IllegalArgumentException</code> will be thrown by the decode methods
+     * if calling {@link ColorSpace.Rgb#getTransferParameters()} on the
+     * specified color space returns null.</p>
+     *
+     * <p>Like all setters on ImageDecoder, this must be called inside
+     * {@link OnHeaderDecodedListener#onHeaderDecoded onHeaderDecoded}.</p>
+     */
+    public void setTargetColorSpace(ColorSpace colorSpace) {
+        mDesiredColorSpace = colorSpace;
+    }
+
+    /**
+     * Closes this resource, relinquishing any underlying resources. This method
+     * is invoked automatically on objects managed by the try-with-resources
+     * statement.
+     *
+     * <p>This is an implementation detail of {@link ImageDecoder}, and should
+     * never be called manually.</p>
+     */
     @Override
     public void close() {
+        mCloseGuard.close();
+        if (!mClosed.compareAndSet(false, true)) {
+            return;
+        }
+        nClose(mNativePtr);
+        mNativePtr = 0;
+
+        if (mOwnsInputStream) {
+            IoUtils.closeQuietly(mInputStream);
+        }
+        IoUtils.closeQuietly(mAssetFd);
+
+        mInputStream = null;
+        mAssetFd = null;
+        mTempStorage = null;
+    }
+
+    private void checkState() {
+        if (mNativePtr == 0) {
+            throw new IllegalStateException("Cannot use closed ImageDecoder!");
+        }
+
+        checkSubset(mDesiredWidth, mDesiredHeight, mCropRect);
+
+        if (mAllocator == ALLOCATOR_HARDWARE) {
+            if (mMutable) {
+                throw new IllegalStateException("Cannot make mutable HARDWARE Bitmap!");
+            }
+            if (mDecodeAsAlphaMask) {
+                throw new IllegalStateException("Cannot make HARDWARE Alpha mask Bitmap!");
+            }
+        }
+
+        if (mPostProcessor != null && mUnpremultipliedRequired) {
+            throw new IllegalStateException("Cannot draw to unpremultiplied pixels!");
+        }
+
+        if (mDesiredColorSpace != null) {
+            if (!(mDesiredColorSpace instanceof ColorSpace.Rgb)) {
+                throw new IllegalArgumentException("The target color space must use the "
+                            + "RGB color model - provided: " + mDesiredColorSpace);
+            }
+            if (((ColorSpace.Rgb) mDesiredColorSpace).getTransferParameters() == null) {
+                throw new IllegalArgumentException("The target color space must use an "
+                            + "ICC parametric transfer function - provided: " + mDesiredColorSpace);
+            }
+        }
+    }
+
+    private static void checkSubset(int width, int height, Rect r) {
+        if (r == null) {
+            return;
+        }
+        if (r.left < 0 || r.top < 0 || r.right > width || r.bottom > height) {
+            throw new IllegalStateException("Subset " + r + " not contained by "
+                    + "scaled image bounds: (" + width + " x " + height + ")");
+        }
+    }
+
+    @WorkerThread
+    @NonNull
+    private Bitmap decodeBitmapInternal() throws IOException {
+        checkState();
+        return nDecodeBitmap(mNativePtr, this, mPostProcessor != null,
+                mDesiredWidth, mDesiredHeight, mCropRect,
+                mMutable, mAllocator, mUnpremultipliedRequired,
+                mConserveMemory, mDecodeAsAlphaMask, mDesiredColorSpace);
+    }
+
+    private void callHeaderDecoded(@Nullable OnHeaderDecodedListener listener,
+            @NonNull Source src) {
+        if (listener != null) {
+            ImageInfo info = new ImageInfo(this);
+            try {
+                listener.onHeaderDecoded(this, info, src);
+            } finally {
+                info.mDecoder = null;
+            }
+        }
     }
 
     /**
      *  Create a {@link Drawable} from a {@code Source}.
      *
      *  @param src representing the encoded image.
-     *  @param listener for learning the {@link ImageInfo} and changing any
-     *      default settings on the {@code ImageDecoder}. If not {@code null},
-     *      this will be called on the same thread as {@code decodeDrawable}
-     *      before that method returns.
+     *  @param listener for learning the {@link ImageInfo ImageInfo} and changing any
+     *      default settings on the {@code ImageDecoder}. This will be called on
+     *      the same thread as {@code decodeDrawable} before that method returns.
+     *      This is required in order to change any of the default settings.
      *  @return Drawable for displaying the image.
      *  @throws IOException if {@code src} is not found, is an unsupported
      *      format, or cannot be decoded for any reason.
      */
+    @WorkerThread
     @NonNull
     public static Drawable decodeDrawable(@NonNull Source src,
+            @NonNull OnHeaderDecodedListener listener) throws IOException {
+        if (listener == null) {
+            throw new IllegalArgumentException("listener cannot be null! "
+                    + "Use decodeDrawable(Source) to not have a listener");
+        }
+        return decodeDrawableImpl(src, listener);
+    }
+
+    @WorkerThread
+    @NonNull
+    private static Drawable decodeDrawableImpl(@NonNull Source src,
             @Nullable OnHeaderDecodedListener listener) throws IOException {
-        Bitmap bitmap = decodeBitmap(src, listener);
-        return new BitmapDrawable(src.getResources(), bitmap);
+        try (ImageDecoder decoder = src.createImageDecoder()) {
+            decoder.mSource = src;
+            decoder.callHeaderDecoded(listener, src);
+
+            if (decoder.mUnpremultipliedRequired) {
+                // Though this could be supported (ignored) for opaque images,
+                // it seems better to always report this error.
+                throw new IllegalStateException("Cannot decode a Drawable " +
+                                                "with unpremultiplied pixels!");
+            }
+
+            if (decoder.mMutable) {
+                throw new IllegalStateException("Cannot decode a mutable " +
+                                                "Drawable!");
+            }
+
+            // this call potentially manipulates the decoder so it must be performed prior to
+            // decoding the bitmap and after decode set the density on the resulting bitmap
+            final int srcDensity = decoder.computeDensity(src);
+            if (decoder.mAnimated) {
+                // AnimatedImageDrawable calls postProcessAndRelease only if
+                // mPostProcessor exists.
+                ImageDecoder postProcessPtr = decoder.mPostProcessor == null ?
+                        null : decoder;
+                Drawable d = new AnimatedImageDrawable(decoder.mNativePtr,
+                        postProcessPtr, decoder.mDesiredWidth,
+                        decoder.mDesiredHeight, srcDensity,
+                        src.computeDstDensity(), decoder.mCropRect,
+                        decoder.mInputStream, decoder.mAssetFd);
+                // d has taken ownership of these objects.
+                decoder.mInputStream = null;
+                decoder.mAssetFd = null;
+                return d;
+            }
+
+            Bitmap bm = decoder.decodeBitmapInternal();
+            bm.setDensity(srcDensity);
+
+            Resources res = src.getResources();
+            byte[] np = bm.getNinePatchChunk();
+            if (np != null && NinePatch.isNinePatchChunk(np)) {
+                Rect opticalInsets = new Rect();
+                bm.getOpticalInsets(opticalInsets);
+                Rect padding = decoder.mOutPaddingRect;
+                if (padding == null) {
+                    padding = new Rect();
+                }
+                nGetPadding(decoder.mNativePtr, padding);
+                return new NinePatchDrawable(res, bm, np, padding,
+                        opticalInsets, null);
+            }
+
+            return new BitmapDrawable(res, bm);
+        }
     }
 
     /**
-     * See {@link #decodeDrawable(Source, OnHeaderDecodedListener)}.
+     *  Create a {@link Drawable} from a {@code Source}.
+     *
+     *  <p>Since there is no {@link OnHeaderDecodedListener OnHeaderDecodedListener},
+     *  the default settings will be used. In order to change any settings, call
+     *  {@link #decodeDrawable(Source, OnHeaderDecodedListener)} instead.</p>
+     *
+     *  @param src representing the encoded image.
+     *  @return Drawable for displaying the image.
+     *  @throws IOException if {@code src} is not found, is an unsupported
+     *      format, or cannot be decoded for any reason.
      */
+    @WorkerThread
     @NonNull
     public static Drawable decodeDrawable(@NonNull Source src)
             throws IOException {
-        return decodeDrawable(src, null);
+        return decodeDrawableImpl(src, null);
     }
 
     /**
      *  Create a {@link Bitmap} from a {@code Source}.
      *
      *  @param src representing the encoded image.
-     *  @param listener for learning the {@link ImageInfo} and changing any
-     *      default settings on the {@code ImageDecoder}. If not {@code null},
-     *      this will be called on the same thread as {@code decodeBitmap}
-     *      before that method returns.
+     *  @param listener for learning the {@link ImageInfo ImageInfo} and changing any
+     *      default settings on the {@code ImageDecoder}. This will be called on
+     *      the same thread as {@code decodeBitmap} before that method returns.
+     *      This is required in order to change any of the default settings.
      *  @return Bitmap containing the image.
      *  @throws IOException if {@code src} is not found, is an unsupported
      *      format, or cannot be decoded for any reason.
      */
+    @WorkerThread
     @NonNull
     public static Bitmap decodeBitmap(@NonNull Source src,
-            @Nullable OnHeaderDecodedListener listener) throws IOException {
-        TypedValue value = new TypedValue();
-        value.density = src.getDensity();
-        ImageDecoder decoder = src.createImageDecoder();
-        if (listener != null) {
-            listener.onHeaderDecoded(decoder, new ImageInfo(decoder), src);
+            @NonNull OnHeaderDecodedListener listener) throws IOException {
+        if (listener == null) {
+            throw new IllegalArgumentException("listener cannot be null! "
+                    + "Use decodeBitmap(Source) to not have a listener");
         }
-        return BitmapFactory.decodeResourceStream(src.getResources(), value,
-                ((InputStreamSource) src).mInputStream, decoder.mOutPaddingRect, null);
+        return decodeBitmapImpl(src, listener);
+    }
+
+    @WorkerThread
+    @NonNull
+    private static Bitmap decodeBitmapImpl(@NonNull Source src,
+            @Nullable OnHeaderDecodedListener listener) throws IOException {
+        try (ImageDecoder decoder = src.createImageDecoder()) {
+            decoder.mSource = src;
+            decoder.callHeaderDecoded(listener, src);
+
+            // this call potentially manipulates the decoder so it must be performed prior to
+            // decoding the bitmap
+            final int srcDensity = decoder.computeDensity(src);
+            Bitmap bm = decoder.decodeBitmapInternal();
+            bm.setDensity(srcDensity);
+
+            Rect padding = decoder.mOutPaddingRect;
+            if (padding != null) {
+                byte[] np = bm.getNinePatchChunk();
+                if (np != null && NinePatch.isNinePatchChunk(np)) {
+                    nGetPadding(decoder.mNativePtr, padding);
+                }
+            }
+
+            return bm;
+        }
+    }
+
+    // This method may modify the decoder so it must be called prior to performing the decode
+    private int computeDensity(@NonNull Source src) {
+        // if the caller changed the size then we treat the density as unknown
+        if (this.requestedResize()) {
+            return Bitmap.DENSITY_NONE;
+        }
+
+        final int srcDensity = src.getDensity();
+        if (srcDensity == Bitmap.DENSITY_NONE) {
+            return srcDensity;
+        }
+
+        // Scaling up nine-patch divs is imprecise and is better handled
+        // at draw time. An app won't be relying on the internal Bitmap's
+        // size, so it is safe to let NinePatchDrawable handle scaling.
+        // mPostProcessor disables nine-patching, so behave normally if
+        // it is present.
+        if (mIsNinePatch && mPostProcessor == null) {
+            return srcDensity;
+        }
+
+        // Special stuff for compatibility mode: if the target density is not
+        // the same as the display density, but the resource -is- the same as
+        // the display density, then don't scale it down to the target density.
+        // This allows us to load the system's density-correct resources into
+        // an application in compatibility mode, without scaling those down
+        // to the compatibility density only to have them scaled back up when
+        // drawn to the screen.
+        Resources res = src.getResources();
+        if (res != null && res.getDisplayMetrics().noncompatDensityDpi == srcDensity) {
+            return srcDensity;
+        }
+
+        final int dstDensity = src.computeDstDensity();
+        if (srcDensity == dstDensity) {
+            return srcDensity;
+        }
+
+        // For P and above, only resize if it would be a downscale. Scale up prior
+        // to P in case the app relies on the Bitmap's size without considering density.
+        if (srcDensity < dstDensity && sApiLevel >= Build.VERSION_CODES.P) {
+            return srcDensity;
+        }
+
+        float scale = (float) dstDensity / srcDensity;
+        int scaledWidth = (int) (mWidth * scale + 0.5f);
+        int scaledHeight = (int) (mHeight * scale + 0.5f);
+        this.setTargetSize(scaledWidth, scaledHeight);
+        return dstDensity;
+    }
+
+    @NonNull
+    private String getMimeType() {
+        return nGetMimeType(mNativePtr);
+    }
+
+    @Nullable
+    private ColorSpace getColorSpace() {
+        return nGetColorSpace(mNativePtr);
     }
 
     /**
-     *  See {@link #decodeBitmap(Source, OnHeaderDecodedListener)}.
+     *  Create a {@link Bitmap} from a {@code Source}.
+     *
+     *  <p>Since there is no {@link OnHeaderDecodedListener OnHeaderDecodedListener},
+     *  the default settings will be used. In order to change any settings, call
+     *  {@link #decodeBitmap(Source, OnHeaderDecodedListener)} instead.</p>
+     *
+     *  @param src representing the encoded image.
+     *  @return Bitmap containing the image.
+     *  @throws IOException if {@code src} is not found, is an unsupported
+     *      format, or cannot be decoded for any reason.
      */
+    @WorkerThread
     @NonNull
     public static Bitmap decodeBitmap(@NonNull Source src) throws IOException {
-        return decodeBitmap(src, null);
+        return decodeBitmapImpl(src, null);
     }
+
+    /**
+     * Private method called by JNI.
+     */
+    @SuppressWarnings("unused")
+    private int postProcessAndRelease(@NonNull Canvas canvas) {
+        try {
+            return mPostProcessor.onPostProcess(canvas);
+        } finally {
+            canvas.release();
+        }
+    }
+
+    /**
+     * Private method called by JNI.
+     */
+    @SuppressWarnings("unused")
+    private void onPartialImage(@DecodeException.Error int error, @Nullable Throwable cause)
+            throws DecodeException {
+        DecodeException exception = new DecodeException(error, cause, mSource);
+        if (mOnPartialImageListener == null
+                || !mOnPartialImageListener.onPartialImage(exception)) {
+            throw exception;
+        }
+    }
+
+    private static native ImageDecoder nCreate(long asset, Source src) throws IOException;
+    private static native ImageDecoder nCreate(ByteBuffer buffer, int position,
+                                               int limit, Source src) throws IOException;
+    private static native ImageDecoder nCreate(byte[] data, int offset, int length,
+                                               Source src) throws IOException;
+    private static native ImageDecoder nCreate(InputStream is, byte[] storage,
+                                               Source src) throws IOException;
+    // The fd must be seekable.
+    private static native ImageDecoder nCreate(FileDescriptor fd, Source src) throws IOException;
+    @NonNull
+    private static native Bitmap nDecodeBitmap(long nativePtr,
+            @NonNull ImageDecoder decoder,
+            boolean doPostProcess,
+            int width, int height,
+            @Nullable Rect cropRect, boolean mutable,
+            int allocator, boolean unpremulRequired,
+            boolean conserveMemory, boolean decodeAsAlphaMask,
+            @Nullable ColorSpace desiredColorSpace)
+        throws IOException;
+    private static native Size nGetSampledSize(long nativePtr,
+                                               int sampleSize);
+    private static native void nGetPadding(long nativePtr, @NonNull Rect outRect);
+    private static native void nClose(long nativePtr);
+    private static native String nGetMimeType(long nativePtr);
+    private static native ColorSpace nGetColorSpace(long nativePtr);
 }
diff --git a/android/graphics/PostProcessor.java b/android/graphics/PostProcessor.java
index b1712e9..6fed39b 100644
--- a/android/graphics/PostProcessor.java
+++ b/android/graphics/PostProcessor.java
@@ -16,25 +16,26 @@
 
 package android.graphics;
 
-import android.annotation.IntDef;
 import android.annotation.NonNull;
+import android.graphics.drawable.AnimatedImageDrawable;
 import android.graphics.drawable.Drawable;
 
 /**
  *  Helper interface for adding custom processing to an image.
  *
- *  <p>The image being processed may be a {@link Drawable}, {@link Bitmap} or frame
- *  of an animated image produced by {@link ImageDecoder}. This is called before
- *  the requested object is returned.</p>
+ *  <p>The image being processed may be a {@link Drawable}, a {@link Bitmap}, or
+ *  a frame of an {@link AnimatedImageDrawable} produced by {@link ImageDecoder}.
+ *  This is called before the requested object is returned.</p>
  *
- *  <p>This custom processing also applies to image types that are otherwise
- *  immutable, such as {@link Bitmap.Config#HARDWARE}.</p>
+ *  <p>This custom processing can even be applied to images that will be returned
+ *  as immutable objects, such as a {@link Bitmap} with {@code Config}
+ *  {@link Bitmap.Config#HARDWARE} returned by {@link ImageDecoder}.</p>
  *
- *  <p>On an animated image, the callback will only be called once, but the drawing
- *  commands will be applied to each frame, as if the {@code Canvas} had been
- *  returned by {@link Picture#beginRecording}.<p>
+ *  <p>On an {@link AnimatedImageDrawable}, the callback will only be called once,
+ *  but the drawing commands will be applied to each frame, as if the {@link Canvas}
+ *  had been returned by {@link Picture#beginRecording Picture.beginRecording}.<p>
  *
- *  <p>Supplied to ImageDecoder via {@link ImageDecoder#setPostProcessor}.</p>
+ *  <p>Supplied to ImageDecoder via {@link ImageDecoder#setPostProcessor setPostProcessor}.</p>
  */
 public interface PostProcessor {
     /**
@@ -43,43 +44,44 @@
      *  <p>Drawing to the {@link Canvas} will behave as if the initial processing
      *  (e.g. decoding) already exists in the Canvas. An implementation can draw
      *  effects on top of this, or it can even draw behind it using
-     *  {@link PorterDuff.Mode#DST_OVER}. A common effect is to add transparency
-     *  to the corners to achieve rounded corners. That can be done with the
-     *  following code:</p>
+     *  {@link PorterDuff.Mode#DST_OVER PorterDuff.Mode.DST_OVER}. A common
+     *  effect is to add transparency to the corners to achieve rounded corners.
+     *  That can be done with the following code:</p>
      *
-     *  <code>
-     *      Path path = new Path();
-     *      path.setFillType(Path.FillType.INVERSE_EVEN_ODD);
-     *      int width = canvas.getWidth();
-     *      int height = canvas.getHeight();
-     *      path.addRoundRect(0, 0, width, height, 20, 20, Path.Direction.CW);
-     *      Paint paint = new Paint();
-     *      paint.setAntiAlias(true);
-     *      paint.setColor(Color.TRANSPARENT);
-     *      paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC));
-     *      canvas.drawPath(path, paint);
-     *      return PixelFormat.TRANSLUCENT;
-     *  </code>
+     *  <pre class="prettyprint">
+     *  Path path = new Path();
+     *  path.setFillType(Path.FillType.INVERSE_EVEN_ODD);
+     *  int width = canvas.getWidth();
+     *  int height = canvas.getHeight();
+     *  path.addRoundRect(0, 0, width, height, 20, 20, Path.Direction.CW);
+     *  Paint paint = new Paint();
+     *  paint.setAntiAlias(true);
+     *  paint.setColor(Color.TRANSPARENT);
+     *  paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC));
+     *  canvas.drawPath(path, paint);
+     *  return PixelFormat.TRANSLUCENT;
+     *  </pre>
      *
      *
      *  @param canvas The {@link Canvas} to draw to.
      *  @return Opacity of the result after drawing.
-     *      {@link PixelFormat#UNKNOWN} means that the implementation did not
-     *      change whether the image has alpha. Return this unless you added
-     *      transparency (e.g. with the code above, in which case you should
-     *      return {@code PixelFormat.TRANSLUCENT}) or you forced the image to
-     *      be opaque (e.g. by drawing everywhere with an opaque color and
-     *      {@code PorterDuff.Mode.DST_OVER}, in which case you should return
-     *      {@code PixelFormat.OPAQUE}).
-     *      {@link PixelFormat#TRANSLUCENT} means that the implementation added
-     *      transparency. This is safe to return even if the image already had
-     *      transparency. This is also safe to return if the result is opaque,
-     *      though it may draw more slowly.
-     *      {@link PixelFormat#OPAQUE} means that the implementation forced the
-     *      image to be opaque. This is safe to return even if the image was
-     *      already opaque.
-     *      {@link PixelFormat#TRANSPARENT} (or any other integer) is not
-     *      allowed, and will result in throwing an
+     *      {@link PixelFormat#UNKNOWN PixelFormat.UNKNOWN} means that the
+     *      implementation did not change whether the image has alpha. Return
+     *      this unless you added transparency (e.g. with the code above, in
+     *      which case you should return
+     *      {@link PixelFormat#TRANSLUCENT PixelFormat.TRANSLUCENT}) or you
+     *      forced the image to be opaque (e.g. by drawing everywhere with an
+     *      opaque color and {@link PorterDuff.Mode#DST_OVER PorterDuff.Mode.DST_OVER},
+     *      in which case you should return {@link PixelFormat#OPAQUE PixelFormat.OPAQUE}).
+     *      {@link PixelFormat#TRANSLUCENT PixelFormat.TRANSLUCENT} means that
+     *      the implementation added transparency. This is safe to return even
+     *      if the image already had transparency. This is also safe to return
+     *      if the result is opaque, though it may draw more slowly.
+     *      {@link PixelFormat#OPAQUE PixelFormat.OPAQUE} means that the
+     *      implementation forced the image to be opaque. This is safe to return
+     *      even if the image was already opaque.
+     *      {@link PixelFormat#TRANSPARENT PixelFormat.TRANSPARENT} (or any other
+     *      integer) is not allowed, and will result in throwing an
      *      {@link java.lang.IllegalArgumentException}.
      */
     @PixelFormat.Opacity
diff --git a/android/graphics/Typeface.java b/android/graphics/Typeface.java
index 20c22b7..18dd97f 100644
--- a/android/graphics/Typeface.java
+++ b/android/graphics/Typeface.java
@@ -738,6 +738,23 @@
     /**
      * Creates a typeface object that best matches the specified existing typeface and the specified
      * weight and italic style
+     * <p>Below are numerical values and corresponding common weight names.</p>
+     * <table>
+     * <thead>
+     * <tr><th>Value</th><th>Common weight name</th></tr>
+     * </thead>
+     * <tbody>
+     * <tr><td>100</td><td>Thin</td></tr>
+     * <tr><td>200</td><td>Extra Light</td></tr>
+     * <tr><td>300</td><td>Light</td></tr>
+     * <tr><td>400</td><td>Normal</td></tr>
+     * <tr><td>500</td><td>Medium</td></tr>
+     * <tr><td>600</td><td>Semi Bold</td></tr>
+     * <tr><td>700</td><td>Bold</td></tr>
+     * <tr><td>800</td><td>Extra Bold</td></tr>
+     * <tr><td>900</td><td>Black</td></tr>
+     * </tbody>
+     * </table>
      *
      * <p>
      * This method is thread safe.
@@ -749,6 +766,9 @@
      * @param italic {@code true} if italic style is desired to be drawn. Otherwise, {@code false}
      * @return A {@link Typeface} object for drawing specified weight and italic style. Never
      *         returns {@code null}
+     *
+     * @see #getWeight()
+     * @see #isItalic()
      */
     public static @NonNull Typeface create(@Nullable Typeface family,
             @IntRange(from = 1, to = 1000) int weight, boolean italic) {
diff --git a/android/hardware/biometrics/BiometricDialog.java b/android/hardware/biometrics/BiometricPrompt.java
similarity index 93%
rename from android/hardware/biometrics/BiometricDialog.java
rename to android/hardware/biometrics/BiometricPrompt.java
index dd848a3..1c9de45 100644
--- a/android/hardware/biometrics/BiometricDialog.java
+++ b/android/hardware/biometrics/BiometricPrompt.java
@@ -38,7 +38,7 @@
 /**
  * A class that manages a system-provided biometric dialog.
  */
-public class BiometricDialog implements BiometricAuthenticator, BiometricConstants {
+public class BiometricPrompt implements BiometricAuthenticator, BiometricConstants {
 
     /**
      * @hide
@@ -190,11 +190,11 @@
         }
 
         /**
-         * Creates a {@link BiometricDialog}.
-         * @return a {@link BiometricDialog}
+         * Creates a {@link BiometricPrompt}.
+         * @return a {@link BiometricPrompt}
          * @throws IllegalArgumentException if any of the required fields are not set.
          */
-        public BiometricDialog build() {
+        public BiometricPrompt build() {
             final CharSequence title = mBundle.getCharSequence(KEY_TITLE);
             final CharSequence negative = mBundle.getCharSequence(KEY_NEGATIVE_TEXT);
 
@@ -203,7 +203,7 @@
             } else if (TextUtils.isEmpty(negative)) {
                 throw new IllegalArgumentException("Negative text must be set and non-empty");
             }
-            return new BiometricDialog(mContext, mBundle, mPositiveButtonInfo, mNegativeButtonInfo);
+            return new BiometricPrompt(mContext, mBundle, mPositiveButtonInfo, mNegativeButtonInfo);
         }
     }
 
@@ -213,7 +213,7 @@
     private ButtonInfo mPositiveButtonInfo;
     private ButtonInfo mNegativeButtonInfo;
 
-    IBiometricDialogReceiver mDialogReceiver = new IBiometricDialogReceiver.Stub() {
+    IBiometricPromptReceiver mDialogReceiver = new IBiometricPromptReceiver.Stub() {
         @Override
         public void onDialogDismissed(int reason) {
             // Check the reason and invoke OnClickListener(s) if necessary
@@ -229,7 +229,7 @@
         }
     };
 
-    private BiometricDialog(Context context, Bundle bundle,
+    private BiometricPrompt(Context context, Bundle bundle,
             ButtonInfo positiveButtonInfo, ButtonInfo negativeButtonInfo) {
         mBundle = bundle;
         mPositiveButtonInfo = positiveButtonInfo;
@@ -239,7 +239,7 @@
     }
 
     /**
-     * A wrapper class for the crypto objects supported by BiometricDialog. Currently the framework
+     * A wrapper class for the crypto objects supported by BiometricPrompt. Currently the framework
      * supports {@link Signature}, {@link Cipher} and {@link Mac} objects.
      */
     public static final class CryptoObject extends android.hardware.biometrics.CryptoObject {
@@ -308,8 +308,8 @@
     }
 
     /**
-     * Callback structure provided to {@link BiometricDialog#authenticate(CancellationSignal,
-     * Executor, AuthenticationCallback)} or {@link BiometricDialog#authenticate(CryptoObject,
+     * Callback structure provided to {@link BiometricPrompt#authenticate(CancellationSignal,
+     * Executor, AuthenticationCallback)} or {@link BiometricPrompt#authenticate(CryptoObject,
      * CancellationSignal, Executor, AuthenticationCallback)}. Users must provide an implementation
      * of this for listening to authentication events.
      */
@@ -378,7 +378,7 @@
             @NonNull CancellationSignal cancel,
             @NonNull @CallbackExecutor Executor executor,
             @NonNull BiometricAuthenticator.AuthenticationCallback callback) {
-        if (!(callback instanceof BiometricDialog.AuthenticationCallback)) {
+        if (!(callback instanceof BiometricPrompt.AuthenticationCallback)) {
             throw new IllegalArgumentException("Callback cannot be casted");
         }
         authenticate(crypto, cancel, executor, (AuthenticationCallback) callback);
@@ -395,7 +395,7 @@
     public void authenticate(@NonNull CancellationSignal cancel,
             @NonNull @CallbackExecutor Executor executor,
             @NonNull BiometricAuthenticator.AuthenticationCallback callback) {
-        if (!(callback instanceof BiometricDialog.AuthenticationCallback)) {
+        if (!(callback instanceof BiometricPrompt.AuthenticationCallback)) {
             throw new IllegalArgumentException("Callback cannot be casted");
         }
         authenticate(cancel, executor, (AuthenticationCallback) callback);
@@ -410,8 +410,8 @@
      * operation can be canceled by using the provided cancel object. The application will receive
      * authentication errors through {@link AuthenticationCallback}, and button events through the
      * corresponding callback set in {@link Builder#setNegativeButton(CharSequence, Executor,
-     * DialogInterface.OnClickListener)}. It is safe to reuse the {@link BiometricDialog} object,
-     * and calling {@link BiometricDialog#authenticate( CancellationSignal, Executor,
+     * DialogInterface.OnClickListener)}. It is safe to reuse the {@link BiometricPrompt} object,
+     * and calling {@link BiometricPrompt#authenticate( CancellationSignal, Executor,
      * AuthenticationCallback)} while an existing authentication attempt is occurring will stop the
      * previous client and start a new authentication. The interrupted client will receive a
      * cancelled notification through {@link AuthenticationCallback#onAuthenticationError(int,
@@ -445,8 +445,8 @@
      * provided cancel object. The application will receive authentication errors through {@link
      * AuthenticationCallback}, and button events through the corresponding callback set in {@link
      * Builder#setNegativeButton(CharSequence, Executor, DialogInterface.OnClickListener)}.  It is
-     * safe to reuse the {@link BiometricDialog} object, and calling {@link
-     * BiometricDialog#authenticate(CancellationSignal, Executor, AuthenticationCallback)} while
+     * safe to reuse the {@link BiometricPrompt} object, and calling {@link
+     * BiometricPrompt#authenticate(CancellationSignal, Executor, AuthenticationCallback)} while
      * an existing authentication attempt is occurring will stop the previous client and start a new
      * authentication. The interrupted client will receive a cancelled notification through {@link
      * AuthenticationCallback#onAuthenticationError(int, CharSequence)}.
@@ -470,15 +470,15 @@
     private boolean handlePreAuthenticationErrors(AuthenticationCallback callback,
             Executor executor) {
         if (!mPackageManager.hasSystemFeature(PackageManager.FEATURE_FINGERPRINT)) {
-            sendError(BiometricDialog.BIOMETRIC_ERROR_HW_NOT_PRESENT, callback,
+            sendError(BiometricPrompt.BIOMETRIC_ERROR_HW_NOT_PRESENT, callback,
                       executor);
             return true;
         } else if (!mFingerprintManager.isHardwareDetected()) {
-            sendError(BiometricDialog.BIOMETRIC_ERROR_HW_UNAVAILABLE, callback,
+            sendError(BiometricPrompt.BIOMETRIC_ERROR_HW_UNAVAILABLE, callback,
                       executor);
             return true;
         } else if (!mFingerprintManager.hasEnrolledFingerprints()) {
-            sendError(BiometricDialog.BIOMETRIC_ERROR_NO_BIOMETRICS, callback,
+            sendError(BiometricPrompt.BIOMETRIC_ERROR_NO_BIOMETRICS, callback,
                       executor);
             return true;
         }
diff --git a/android/hardware/camera2/CameraManager.java b/android/hardware/camera2/CameraManager.java
index 4124536..7ebe0f9 100644
--- a/android/hardware/camera2/CameraManager.java
+++ b/android/hardware/camera2/CameraManager.java
@@ -43,6 +43,9 @@
 import android.util.Log;
 
 import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Comparator;
+
 import java.util.concurrent.Executor;
 import java.util.concurrent.Executors;
 import java.util.concurrent.RejectedExecutionException;
@@ -924,6 +927,37 @@
                     idCount++;
                 }
             }
+
+            // The sort logic must match the logic in
+            // libcameraservice/common/CameraProviderManager.cpp::getAPI1CompatibleCameraDeviceIds
+            Arrays.sort(cameraIds, new Comparator<String>() {
+                    @Override
+                    public int compare(String s1, String s2) {
+                        int s1Int = 0, s2Int = 0;
+                        try {
+                            s1Int = Integer.parseInt(s1);
+                        } catch (NumberFormatException e) {
+                            s1Int = -1;
+                        }
+
+                        try {
+                            s2Int = Integer.parseInt(s2);
+                        } catch (NumberFormatException e) {
+                            s2Int = -1;
+                        }
+
+                        // Uint device IDs first
+                        if (s1Int >= 0 && s2Int >= 0) {
+                            return s1Int - s2Int;
+                        } else if (s1Int >= 0) {
+                            return -1;
+                        } else if (s2Int >= 0) {
+                            return 1;
+                        } else {
+                            // Simple string compare if both id are not uint
+                            return s1.compareTo(s2);
+                        }
+                    }});
             return cameraIds;
         }
 
diff --git a/android/hardware/camera2/CaptureRequest.java b/android/hardware/camera2/CaptureRequest.java
index 2252571..411a97e 100644
--- a/android/hardware/camera2/CaptureRequest.java
+++ b/android/hardware/camera2/CaptureRequest.java
@@ -2105,8 +2105,8 @@
      * the thumbnail data will also be rotated.</p>
      * <p>Note that this orientation is relative to the orientation of the camera sensor, given
      * by {@link CameraCharacteristics#SENSOR_ORIENTATION android.sensor.orientation}.</p>
-     * <p>To translate from the device orientation given by the Android sensor APIs, the following
-     * sample code may be used:</p>
+     * <p>To translate from the device orientation given by the Android sensor APIs for camera
+     * sensors which are not EXTERNAL, the following sample code may be used:</p>
      * <pre><code>private int getJpegOrientation(CameraCharacteristics c, int deviceOrientation) {
      *     if (deviceOrientation == android.view.OrientationEventListener.ORIENTATION_UNKNOWN) return 0;
      *     int sensorOrientation = c.get(CameraCharacteristics.SENSOR_ORIENTATION);
@@ -2125,6 +2125,8 @@
      *     return jpegOrientation;
      * }
      * </code></pre>
+     * <p>For EXTERNAL cameras the sensor orientation will always be set to 0 and the facing will
+     * also be set to EXTERNAL. The above code is not relevant in such case.</p>
      * <p><b>Units</b>: Degrees in multiples of 90</p>
      * <p><b>Range of valid values:</b><br>
      * 0, 90, 180, 270</p>
diff --git a/android/hardware/camera2/CaptureResult.java b/android/hardware/camera2/CaptureResult.java
index 8df5447..c156616 100644
--- a/android/hardware/camera2/CaptureResult.java
+++ b/android/hardware/camera2/CaptureResult.java
@@ -2422,8 +2422,8 @@
      * the thumbnail data will also be rotated.</p>
      * <p>Note that this orientation is relative to the orientation of the camera sensor, given
      * by {@link CameraCharacteristics#SENSOR_ORIENTATION android.sensor.orientation}.</p>
-     * <p>To translate from the device orientation given by the Android sensor APIs, the following
-     * sample code may be used:</p>
+     * <p>To translate from the device orientation given by the Android sensor APIs for camera
+     * sensors which are not EXTERNAL, the following sample code may be used:</p>
      * <pre><code>private int getJpegOrientation(CameraCharacteristics c, int deviceOrientation) {
      *     if (deviceOrientation == android.view.OrientationEventListener.ORIENTATION_UNKNOWN) return 0;
      *     int sensorOrientation = c.get(CameraCharacteristics.SENSOR_ORIENTATION);
@@ -2442,6 +2442,8 @@
      *     return jpegOrientation;
      * }
      * </code></pre>
+     * <p>For EXTERNAL cameras the sensor orientation will always be set to 0 and the facing will
+     * also be set to EXTERNAL. The above code is not relevant in such case.</p>
      * <p><b>Units</b>: Degrees in multiples of 90</p>
      * <p><b>Range of valid values:</b><br>
      * 0, 90, 180, 270</p>
diff --git a/android/hardware/display/BrightnessConfiguration.java b/android/hardware/display/BrightnessConfiguration.java
index 67e97bf..6d9ba77 100644
--- a/android/hardware/display/BrightnessConfiguration.java
+++ b/android/hardware/display/BrightnessConfiguration.java
@@ -86,7 +86,9 @@
             sb.append("(").append(mLux[i]).append(", ").append(mNits[i]).append(")");
         }
         sb.append("], '");
-        sb.append(mDescription);
+        if (mDescription != null) {
+            sb.append(mDescription);
+        }
         sb.append("'}");
         return sb.toString();
     }
@@ -96,7 +98,9 @@
         int result = 1;
         result = result * 31 + Arrays.hashCode(mLux);
         result = result * 31 + Arrays.hashCode(mNits);
-        result = result * 31 + mDescription.hashCode();
+        if (mDescription != null) {
+            result = result * 31 + mDescription.hashCode();
+        }
         return result;
     }
 
diff --git a/android/hardware/display/Curve.java b/android/hardware/display/Curve.java
new file mode 100644
index 0000000..ac28fdd
--- /dev/null
+++ b/android/hardware/display/Curve.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.display;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/** @hide */
+public final class Curve implements Parcelable {
+    private final float[] mX;
+    private final float[] mY;
+
+    public Curve(float[] x, float[] y) {
+        mX = x;
+        mY = y;
+    }
+
+    public float[] getX() {
+        return mX;
+    }
+
+    public float[] getY() {
+        return mY;
+    }
+
+    public static final Creator<Curve> CREATOR = new Creator<Curve>() {
+        public Curve createFromParcel(Parcel in) {
+            float[] x = in.createFloatArray();
+            float[] y = in.createFloatArray();
+            return new Curve(x, y);
+        }
+
+        public Curve[] newArray(int size) {
+            return new Curve[size];
+        }
+    };
+
+    @Override
+    public void writeToParcel(Parcel out, int flags) {
+        out.writeFloatArray(mX);
+        out.writeFloatArray(mY);
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+}
diff --git a/android/hardware/display/DisplayManager.java b/android/hardware/display/DisplayManager.java
index efb9517..b182fa2 100644
--- a/android/hardware/display/DisplayManager.java
+++ b/android/hardware/display/DisplayManager.java
@@ -28,6 +28,7 @@
 import android.graphics.Point;
 import android.media.projection.MediaProjection;
 import android.os.Handler;
+import android.util.Pair;
 import android.util.SparseArray;
 import android.view.Display;
 import android.view.Surface;
@@ -748,6 +749,22 @@
     }
 
     /**
+     * Returns the minimum brightness curve, which guarantess that any brightness curve that dips
+     * below it is rejected by the system.
+     * This prevent auto-brightness from setting the screen so dark as to prevent the user from
+     * resetting or disabling it, and maps lux to the absolute minimum nits that are still readable
+     * in that ambient brightness.
+     *
+     * @return The minimum brightness curve (as lux values and their corresponding nits values).
+     *
+     * @hide
+     */
+    @SystemApi
+    public Pair<float[], float[]> getMinimumBrightnessCurve() {
+        return mGlobal.getMinimumBrightnessCurve();
+    }
+
+    /**
      * Listens for changes in available display devices.
      */
     public interface DisplayListener {
diff --git a/android/hardware/display/DisplayManagerGlobal.java b/android/hardware/display/DisplayManagerGlobal.java
index 2d0ef2f..d968a3e 100644
--- a/android/hardware/display/DisplayManagerGlobal.java
+++ b/android/hardware/display/DisplayManagerGlobal.java
@@ -31,6 +31,7 @@
 import android.os.ServiceManager;
 import android.text.TextUtils;
 import android.util.Log;
+import android.util.Pair;
 import android.util.SparseArray;
 import android.view.Display;
 import android.view.DisplayAdjustments;
@@ -563,6 +564,24 @@
     }
 
     /**
+     * Returns the minimum brightness curve, which guarantess that any brightness curve that dips
+     * below it is rejected by the system.
+     * This prevent auto-brightness from setting the screen so dark as to prevent the user from
+     * resetting or disabling it, and maps lux to the absolute minimum nits that are still readable
+     * in that ambient brightness.
+     *
+     * @return The minimum brightness curve (as lux values and their corresponding nits values).
+     */
+    public Pair<float[], float[]> getMinimumBrightnessCurve() {
+        try {
+            Curve curve = mDm.getMinimumBrightnessCurve();
+            return Pair.create(curve.getX(), curve.getY());
+        } catch (RemoteException ex) {
+            throw ex.rethrowFromSystemServer();
+        }
+    }
+
+    /**
      * Retrieves ambient brightness stats.
      */
     public List<AmbientBrightnessDayStats> getAmbientBrightnessStats() {
diff --git a/android/hardware/fingerprint/FingerprintManager.java b/android/hardware/fingerprint/FingerprintManager.java
index a6c8c67..40d31bf 100644
--- a/android/hardware/fingerprint/FingerprintManager.java
+++ b/android/hardware/fingerprint/FingerprintManager.java
@@ -31,9 +31,9 @@
 import android.content.Context;
 import android.content.pm.PackageManager;
 import android.hardware.biometrics.BiometricAuthenticator;
-import android.hardware.biometrics.BiometricDialog;
 import android.hardware.biometrics.BiometricFingerprintConstants;
-import android.hardware.biometrics.IBiometricDialogReceiver;
+import android.hardware.biometrics.BiometricPrompt;
+import android.hardware.biometrics.IBiometricPromptReceiver;
 import android.os.Binder;
 import android.os.Bundle;
 import android.os.CancellationSignal;
@@ -57,7 +57,7 @@
 
 /**
  * A class that coordinates access to the fingerprint hardware.
- * @deprecated See {@link BiometricDialog} which shows a system-provided dialog upon starting
+ * @deprecated See {@link BiometricPrompt} which shows a system-provided dialog upon starting
  * authentication. In a world where devices may have different types of biometric authentication,
  * it's much more realistic to have a system-provided authentication dialog since the method may
  * vary by vendor/device.
@@ -111,7 +111,7 @@
     /**
      * A wrapper class for the crypto objects supported by FingerprintManager. Currently the
      * framework supports {@link Signature}, {@link Cipher} and {@link Mac} objects.
-     * @deprecated See {@link android.hardware.biometrics.BiometricDialog.CryptoObject}
+     * @deprecated See {@link android.hardware.biometrics.BiometricPrompt.CryptoObject}
      */
     @Deprecated
     public static final class CryptoObject extends android.hardware.biometrics.CryptoObject {
@@ -155,7 +155,7 @@
     /**
      * Container for callback data from {@link FingerprintManager#authenticate(CryptoObject,
      *     CancellationSignal, int, AuthenticationCallback, Handler)}.
-     * @deprecated See {@link android.hardware.biometrics.BiometricDialog.AuthenticationResult}
+     * @deprecated See {@link android.hardware.biometrics.BiometricPrompt.AuthenticationResult}
      */
     @Deprecated
     public static class AuthenticationResult {
@@ -204,7 +204,7 @@
      * FingerprintManager#authenticate(CryptoObject, CancellationSignal,
      * int, AuthenticationCallback, Handler) } must provide an implementation of this for listening to
      * fingerprint events.
-     * @deprecated See {@link android.hardware.biometrics.BiometricDialog.AuthenticationCallback}
+     * @deprecated See {@link android.hardware.biometrics.BiometricPrompt.AuthenticationCallback}
      */
     @Deprecated
     public static abstract class AuthenticationCallback
@@ -378,10 +378,10 @@
      *         by <a href="{@docRoot}training/articles/keystore.html">Android Keystore
      *         facility</a>.
      * @throws IllegalStateException if the crypto primitive is not initialized.
-     * @deprecated See {@link BiometricDialog#authenticate(CancellationSignal, Executor,
-     * BiometricDialog.AuthenticationCallback)} and {@link BiometricDialog#authenticate(
-     * BiometricDialog.CryptoObject, CancellationSignal, Executor,
-     * BiometricDialog.AuthenticationCallback)}
+     * @deprecated See {@link BiometricPrompt#authenticate(CancellationSignal, Executor,
+     * BiometricPrompt.AuthenticationCallback)} and {@link BiometricPrompt#authenticate(
+     * BiometricPrompt.CryptoObject, CancellationSignal, Executor,
+     * BiometricPrompt.AuthenticationCallback)}
      */
     @Deprecated
     @RequiresPermission(anyOf = {USE_BIOMETRIC, USE_FINGERPRINT})
@@ -444,7 +444,7 @@
 
     /**
      * Per-user version, see {@link FingerprintManager#authenticate(CryptoObject,
-     * CancellationSignal, Bundle, Executor, IBiometricDialogReceiver, AuthenticationCallback)}
+     * CancellationSignal, Bundle, Executor, IBiometricPromptReceiver, AuthenticationCallback)}
      * @param userId the user ID that the fingerprint hardware will authenticate for.
      */
     private void authenticate(int userId,
@@ -452,7 +452,7 @@
             @NonNull CancellationSignal cancel,
             @NonNull Bundle bundle,
             @NonNull @CallbackExecutor Executor executor,
-            @NonNull IBiometricDialogReceiver receiver,
+            @NonNull IBiometricPromptReceiver receiver,
             @NonNull BiometricAuthenticator.AuthenticationCallback callback) {
         mCryptoObject = crypto;
         if (cancel.isCanceled()) {
@@ -480,8 +480,8 @@
     }
 
     /**
-     * Private method, see {@link BiometricDialog#authenticate(CancellationSignal, Executor,
-     * BiometricDialog.AuthenticationCallback)}
+     * Private method, see {@link BiometricPrompt#authenticate(CancellationSignal, Executor,
+     * BiometricPrompt.AuthenticationCallback)}
      * @param cancel
      * @param executor
      * @param callback
@@ -491,7 +491,7 @@
             @NonNull CancellationSignal cancel,
             @NonNull Bundle bundle,
             @NonNull @CallbackExecutor Executor executor,
-            @NonNull IBiometricDialogReceiver receiver,
+            @NonNull IBiometricPromptReceiver receiver,
             @NonNull BiometricAuthenticator.AuthenticationCallback callback) {
         if (cancel == null) {
             throw new IllegalArgumentException("Must supply a cancellation signal");
@@ -512,8 +512,8 @@
     }
 
     /**
-     * Private method, see {@link BiometricDialog#authenticate(BiometricDialog.CryptoObject,
-     * CancellationSignal, Executor, BiometricDialog.AuthenticationCallback)}
+     * Private method, see {@link BiometricPrompt#authenticate(BiometricPrompt.CryptoObject,
+     * CancellationSignal, Executor, BiometricPrompt.AuthenticationCallback)}
      * @param crypto
      * @param cancel
      * @param executor
@@ -524,7 +524,7 @@
             @NonNull CancellationSignal cancel,
             @NonNull Bundle bundle,
             @NonNull @CallbackExecutor Executor executor,
-            @NonNull IBiometricDialogReceiver receiver,
+            @NonNull IBiometricPromptReceiver receiver,
             @NonNull BiometricAuthenticator.AuthenticationCallback callback) {
         if (crypto == null) {
             throw new IllegalArgumentException("Must supply a crypto object");
@@ -743,7 +743,7 @@
      * Determine if there is at least one fingerprint enrolled.
      *
      * @return true if at least one fingerprint is enrolled, false otherwise
-     * @deprecated See {@link BiometricDialog} and
+     * @deprecated See {@link BiometricPrompt} and
      * {@link FingerprintManager#FINGERPRINT_ERROR_NO_FINGERPRINTS}
      */
     @Deprecated
@@ -777,7 +777,7 @@
      * Determine if fingerprint hardware is present and functional.
      *
      * @return true if hardware is present and functional, false otherwise.
-     * @deprecated See {@link BiometricDialog} and
+     * @deprecated See {@link BiometricPrompt} and
      * {@link FingerprintManager#FINGERPRINT_ERROR_HW_UNAVAILABLE}
      */
     @Deprecated
@@ -1158,7 +1158,7 @@
         @Override // binder call
         public void onError(long deviceId, int error, int vendorCode) {
             if (mExecutor != null) {
-                // BiometricDialog case
+                // BiometricPrompt case
                 if (error == FingerprintManager.FINGERPRINT_ERROR_USER_CANCELED) {
                     // User tapped somewhere to cancel, the biometric dialog is already dismissed.
                     mExecutor.execute(() -> {
@@ -1172,7 +1172,7 @@
                         mExecutor.execute(() -> {
                             sendErrorResult(deviceId, error, vendorCode);
                         });
-                    }, BiometricDialog.HIDE_DIALOG_DELAY);
+                    }, BiometricPrompt.HIDE_DIALOG_DELAY);
                 }
             } else {
                 mHandler.obtainMessage(MSG_ERROR, error, vendorCode, deviceId).sendToTarget();
diff --git a/android/location/LocationManager.java b/android/location/LocationManager.java
index a523958..6eb3d8d 100644
--- a/android/location/LocationManager.java
+++ b/android/location/LocationManager.java
@@ -29,6 +29,7 @@
 import android.annotation.SuppressLint;
 import android.annotation.SystemApi;
 import android.annotation.SystemService;
+import android.annotation.TestApi;
 import android.app.PendingIntent;
 import android.content.Context;
 import android.content.Intent;
@@ -392,6 +393,18 @@
     }
 
     /**
+     * @hide
+     */
+    @TestApi
+    public String[] getBackgroundThrottlingWhitelist() {
+        try {
+            return mService.getBackgroundThrottlingWhitelist();
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
      * @hide - hide this constructor because it has a parameter
      * of type ILocationManager, which is a system private class. The
      * right way to create an instance of this class is using the
@@ -1249,40 +1262,11 @@
     @SystemApi
     @RequiresPermission(WRITE_SECURE_SETTINGS)
     public void setLocationEnabledForUser(boolean enabled, UserHandle userHandle) {
-        final List<String> allProvidersList = getAllProviders();
-        // Update all providers on device plus gps and network provider when disabling location.
-        Set<String> allProvidersSet = new ArraySet<>(allProvidersList.size() + 2);
-        allProvidersSet.addAll(allProvidersList);
-        // When disabling location, disable gps and network provider that could have been enabled by
-        // location mode api.
-        if (enabled == false) {
-            allProvidersSet.add(GPS_PROVIDER);
-            allProvidersSet.add(NETWORK_PROVIDER);
+        try {
+            mService.setLocationEnabledForUser(enabled, userHandle.getIdentifier());
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
         }
-        if (allProvidersSet.isEmpty()) {
-            return;
-        }
-        // to ensure thread safety, we write the provider name with a '+' or '-'
-        // and let the SettingsProvider handle it rather than reading and modifying
-        // the list of enabled providers.
-        final String prefix = enabled ? "+" : "-";
-        StringBuilder locationProvidersAllowed = new StringBuilder();
-        for (String provider : allProvidersSet) {
-            checkProvider(provider);
-            if (provider.equals(PASSIVE_PROVIDER)) {
-                continue;
-            }
-            locationProvidersAllowed.append(prefix);
-            locationProvidersAllowed.append(provider);
-            locationProvidersAllowed.append(",");
-        }
-        // Remove the trailing comma
-        locationProvidersAllowed.setLength(locationProvidersAllowed.length() - 1);
-        Settings.Secure.putStringForUser(
-                mContext.getContentResolver(),
-                Settings.Secure.LOCATION_PROVIDERS_ALLOWED,
-                locationProvidersAllowed.toString(),
-                userHandle.getIdentifier());
     }
 
     /**
@@ -1295,22 +1279,11 @@
      */
     @SystemApi
     public boolean isLocationEnabledForUser(UserHandle userHandle) {
-        final String allowedProviders = Settings.Secure.getStringForUser(
-                mContext.getContentResolver(), Settings.Secure.LOCATION_PROVIDERS_ALLOWED,
-                userHandle.getIdentifier());
-        if (allowedProviders == null) {
-            return false;
+        try {
+            return mService.isLocationEnabledForUser(userHandle.getIdentifier());
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
         }
-        final List<String> providerList = Arrays.asList(allowedProviders.split(","));
-        for(String provider : getAllProviders()) {
-            if (provider.equals(PASSIVE_PROVIDER)) {
-                continue;
-            }
-            if (providerList.contains(provider)) {
-                return true;
-            }
-        }
-        return false;
     }
 
     /**
@@ -1362,9 +1335,12 @@
     @SystemApi
     public boolean isProviderEnabledForUser(String provider, UserHandle userHandle) {
         checkProvider(provider);
-        String allowedProviders = Settings.Secure.getStringForUser(mContext.getContentResolver(),
-                Settings.Secure.LOCATION_PROVIDERS_ALLOWED, userHandle.getIdentifier());
-        return TextUtils.delimitedStringContains(allowedProviders, ',', provider);
+
+        try {
+            return mService.isProviderEnabledForUser(provider, userHandle.getIdentifier());
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
     }
 
     /**
@@ -1383,16 +1359,13 @@
     public boolean setProviderEnabledForUser(
             String provider, boolean enabled, UserHandle userHandle) {
         checkProvider(provider);
-        // to ensure thread safety, we write the provider name with a '+' or '-'
-        // and let the SettingsProvider handle it rather than reading and modifying
-        // the list of enabled providers.
-        if (enabled) {
-            provider = "+" + provider;
-        } else {
-            provider = "-" + provider;
+
+        try {
+            return mService.setProviderEnabledForUser(
+                    provider, enabled, userHandle.getIdentifier());
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
         }
-        return Settings.Secure.putStringForUser(mContext.getContentResolver(),
-                Settings.Secure.LOCATION_PROVIDERS_ALLOWED, provider, userHandle.getIdentifier());
     }
 
     /**
diff --git a/android/media/AudioAttributes.java b/android/media/AudioAttributes.java
index d432658..9152ff2 100644
--- a/android/media/AudioAttributes.java
+++ b/android/media/AudioAttributes.java
@@ -180,6 +180,7 @@
      * IMPORTANT: when adding new usage types, add them to SDK_USAGES and update SUPPRESSIBLE_USAGES
      *            if applicable, as well as audioattributes.proto.
      *            Also consider adding them to <aaudio/AAudio.h> for the NDK.
+     *            Also consider adding them to UsageTypeConverter for service dump and etc.
      */
 
     /**
@@ -249,9 +250,10 @@
         SUPPRESSIBLE_USAGES.put(USAGE_ASSISTANCE_NAVIGATION_GUIDANCE,    SUPPRESSIBLE_MEDIA);
         SUPPRESSIBLE_USAGES.put(USAGE_GAME,                              SUPPRESSIBLE_MEDIA);
         SUPPRESSIBLE_USAGES.put(USAGE_ASSISTANT,                         SUPPRESSIBLE_MEDIA);
+        /** default volume assignment is STREAM_MUSIC, handle unknown usage as media */
+        SUPPRESSIBLE_USAGES.put(USAGE_UNKNOWN,                           SUPPRESSIBLE_MEDIA);
         SUPPRESSIBLE_USAGES.put(USAGE_VOICE_COMMUNICATION_SIGNALLING,    SUPPRESSIBLE_SYSTEM);
         SUPPRESSIBLE_USAGES.put(USAGE_ASSISTANCE_SONIFICATION,           SUPPRESSIBLE_SYSTEM);
-        SUPPRESSIBLE_USAGES.put(USAGE_UNKNOWN,                           SUPPRESSIBLE_SYSTEM);
     }
 
     /**
@@ -1056,8 +1058,7 @@
             case USAGE_ASSISTANCE_ACCESSIBILITY:
                 return AudioSystem.STREAM_ACCESSIBILITY;
             case USAGE_UNKNOWN:
-                return fromGetVolumeControlStream ?
-                        AudioManager.USE_DEFAULT_STREAM_TYPE : AudioSystem.STREAM_MUSIC;
+                return AudioSystem.STREAM_MUSIC;
             default:
                 if (fromGetVolumeControlStream) {
                     throw new IllegalArgumentException("Unknown usage value " + aa.getUsage() +
diff --git a/android/media/AudioFocusRequest.java b/android/media/AudioFocusRequest.java
index 7104dad..fe89b89 100644
--- a/android/media/AudioFocusRequest.java
+++ b/android/media/AudioFocusRequest.java
@@ -19,6 +19,7 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.SystemApi;
+import android.annotation.TestApi;
 import android.media.AudioManager.OnAudioFocusChangeListener;
 import android.os.Bundle;
 import android.os.Handler;
@@ -262,6 +263,7 @@
      * Returns the focus change listener set for this {@code AudioFocusRequest}.
      * @return null if no {@link AudioManager.OnAudioFocusChangeListener} was set.
      */
+    @TestApi
     public @Nullable OnAudioFocusChangeListener getOnAudioFocusChangeListener() {
         return mFocusListener;
     }
diff --git a/android/media/AudioFormat.java b/android/media/AudioFormat.java
index f98480b..d0a2c98 100644
--- a/android/media/AudioFormat.java
+++ b/android/media/AudioFormat.java
@@ -18,6 +18,7 @@
 
 import android.annotation.IntDef;
 import android.annotation.NonNull;
+import android.annotation.TestApi;
 import android.os.Parcel;
 import android.os.Parcelable;
 
@@ -437,6 +438,7 @@
      * @param mask a combination of the CHANNEL_IN_* definitions, even CHANNEL_IN_DEFAULT
      * @return number of channels for the mask
      */
+    @TestApi
     public static int channelCountFromInChannelMask(int mask) {
         return Integer.bitCount(mask);
     }
@@ -446,6 +448,7 @@
      * @param mask a combination of the CHANNEL_OUT_* definitions, but not CHANNEL_OUT_DEFAULT
      * @return number of channels for the mask
      */
+    @TestApi
     public static int channelCountFromOutChannelMask(int mask) {
         return Integer.bitCount(mask);
     }
@@ -492,6 +495,7 @@
     // CHANNEL_IN_ALL is not yet defined; if added then it should match AUDIO_CHANNEL_IN_ALL
 
     /** @hide */
+    @TestApi
     public static int getBytesPerSample(int audioFormat)
     {
         switch (audioFormat) {
@@ -562,6 +566,7 @@
     }
 
     /** @hide */
+    @TestApi
     public static boolean isEncodingLinearPcm(int audioFormat)
     {
         switch (audioFormat) {
diff --git a/android/media/AudioManager.java b/android/media/AudioManager.java
index aeef215..fdb7499 100644
--- a/android/media/AudioManager.java
+++ b/android/media/AudioManager.java
@@ -63,6 +63,7 @@
 import java.lang.annotation.RetentionPolicy;
 import java.util.ArrayList;
 import java.util.HashMap;
+import java.util.HashSet;
 import java.util.Iterator;
 import java.util.List;
 import java.util.concurrent.ConcurrentHashMap;
@@ -4786,6 +4787,21 @@
     }
 
     /**
+     * Add {@link MicrophoneInfo} by device information while filtering certain types.
+     */
+    private void addMicrophonesFromAudioDeviceInfo(ArrayList<MicrophoneInfo> microphones,
+                    HashSet<Integer> filterTypes) {
+        AudioDeviceInfo[] devices = getDevicesStatic(GET_DEVICES_INPUTS);
+        for (AudioDeviceInfo device : devices) {
+            if (filterTypes.contains(device.getType())) {
+                continue;
+            }
+            MicrophoneInfo microphone = microphoneInfoFromAudioDeviceInfo(device);
+            microphones.add(microphone);
+        }
+    }
+
+    /**
      * Returns a list of {@link MicrophoneInfo} that corresponds to the characteristics
      * of all available microphones. The list is empty when no microphones are available
      * on the device. An error during the query will result in an IOException being thrown.
@@ -4796,21 +4812,17 @@
     public List<MicrophoneInfo> getMicrophones() throws IOException {
         ArrayList<MicrophoneInfo> microphones = new ArrayList<MicrophoneInfo>();
         int status = AudioSystem.getMicrophones(microphones);
+        HashSet<Integer> filterTypes = new HashSet<>();
+        filterTypes.add(AudioDeviceInfo.TYPE_TELEPHONY);
         if (status != AudioManager.SUCCESS) {
-            // fail and bail!
+            // fail and populate microphones with unknown characteristics by device information.
             Log.e(TAG, "getMicrophones failed:" + status);
-            return new ArrayList<MicrophoneInfo>(); // Always return a list.
+            addMicrophonesFromAudioDeviceInfo(microphones, filterTypes);
+            return microphones;
         }
         setPortIdForMicrophones(microphones);
-        AudioDeviceInfo[] devices = getDevicesStatic(GET_DEVICES_INPUTS);
-        for (AudioDeviceInfo device : devices) {
-            if (device.getType() == AudioDeviceInfo.TYPE_BUILTIN_MIC ||
-                    device.getType() == AudioDeviceInfo.TYPE_TELEPHONY) {
-                continue;
-            }
-            MicrophoneInfo microphone = microphoneInfoFromAudioDeviceInfo(device);
-            microphones.add(microphone);
-        }
+        filterTypes.add(AudioDeviceInfo.TYPE_BUILTIN_MIC);
+        addMicrophonesFromAudioDeviceInfo(microphones, filterTypes);
         return microphones;
     }
 
diff --git a/android/media/AudioPlaybackConfiguration.java b/android/media/AudioPlaybackConfiguration.java
index 8a36f91..7dfdb20 100644
--- a/android/media/AudioPlaybackConfiguration.java
+++ b/android/media/AudioPlaybackConfiguration.java
@@ -43,6 +43,8 @@
     /** @hide */
     public static final int PLAYER_PIID_INVALID = -1;
     /** @hide */
+    public static final int PLAYER_PIID_UNASSIGNED = 0;
+    /** @hide */
     public static final int PLAYER_UPID_INVALID = -1;
 
     // information about the implementation
diff --git a/android/media/AudioPresentation.java b/android/media/AudioPresentation.java
index e39cb7d..ce71436 100644
--- a/android/media/AudioPresentation.java
+++ b/android/media/AudioPresentation.java
@@ -18,8 +18,7 @@
 
 import android.annotation.IntDef;
 import android.annotation.NonNull;
-
-import com.android.internal.annotations.VisibleForTesting;
+import android.annotation.TestApi;
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
@@ -94,7 +93,7 @@
     /**
      * @hide
      */
-    @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
+    @TestApi
     public AudioPresentation(int presentationId,
                         int programId,
                         @NonNull Map<String, String> labels,
@@ -119,7 +118,7 @@
      * decoder. Presentation id is typically sequential, but does not have to be.
      * @hide
      */
-    @VisibleForTesting
+    @TestApi
     public int getPresentationId() {
         return mPresentationId;
     }
@@ -129,7 +128,7 @@
      * Program id can be used to further uniquely identify the presentation to a decoder.
      * @hide
      */
-    @VisibleForTesting
+    @TestApi
     public int getProgramId() {
         return mProgramId;
     }
diff --git a/android/media/AudioRecord.java b/android/media/AudioRecord.java
index 4f0dccb..6b35dd4 100644
--- a/android/media/AudioRecord.java
+++ b/android/media/AudioRecord.java
@@ -1628,7 +1628,6 @@
         int status = native_get_active_microphones(activeMicrophones);
         if (status != AudioManager.SUCCESS) {
             Log.e(TAG, "getActiveMicrophones failed:" + status);
-            return new ArrayList<MicrophoneInfo>();
         }
         AudioManager.setPortIdForMicrophones(activeMicrophones);
 
diff --git a/android/media/BufferingParams.java b/android/media/BufferingParams.java
index 521e897..aaae5e7 100644
--- a/android/media/BufferingParams.java
+++ b/android/media/BufferingParams.java
@@ -17,6 +17,7 @@
 package android.media;
 
 import android.annotation.IntDef;
+import android.annotation.TestApi;
 import android.os.Parcel;
 import android.os.Parcelable;
 
@@ -63,6 +64,7 @@
  * <p>Users should use {@link Builder} to change {@link BufferingParams}.
  * @hide
  */
+@TestApi
 public final class BufferingParams implements Parcelable {
     private static final int BUFFERING_NO_MARK = -1;
 
diff --git a/android/media/ExifInterface.java b/android/media/ExifInterface.java
index bc0e43b..7888436 100644
--- a/android/media/ExifInterface.java
+++ b/android/media/ExifInterface.java
@@ -66,7 +66,7 @@
 /**
  * This is a class for reading and writing Exif tags in a JPEG file or a RAW image file.
  * <p>
- * Supported formats are: JPEG, DNG, CR2, NEF, NRW, ARW, RW2, ORF, PEF, SRW and RAF.
+ * Supported formats are: JPEG, DNG, CR2, NEF, NRW, ARW, RW2, ORF, PEF, SRW, RAF and HEIF.
  * <p>
  * Attribute mutation is supported for JPEG image files.
  */
@@ -2524,46 +2524,46 @@
     private void getHeifAttributes(ByteOrderedDataInputStream in) throws IOException {
         MediaMetadataRetriever retriever = new MediaMetadataRetriever();
         try {
-            if (mSeekableFileDescriptor != null) {
-                retriever.setDataSource(mSeekableFileDescriptor);
-            } else {
-                retriever.setDataSource(new MediaDataSource() {
-                    long mPosition;
+            retriever.setDataSource(new MediaDataSource() {
+                long mPosition;
 
-                    @Override
-                    public void close() throws IOException {}
+                @Override
+                public void close() throws IOException {}
 
-                    @Override
-                    public int readAt(long position, byte[] buffer, int offset, int size)
-                            throws IOException {
-                        if (size == 0) {
-                            return 0;
-                        }
-                        if (position < 0) {
-                            return -1;
-                        }
-                        if (mPosition != position) {
-                            in.seek(position);
-                            mPosition = position;
-                        }
-
-                        int bytesRead = in.read(buffer, offset, size);
-                        if (bytesRead < 0) {
-                            mPosition = -1; // need to seek on next read
-                            return -1;
-                        }
-
-                        mPosition += bytesRead;
-                        return bytesRead;
+                @Override
+                public int readAt(long position, byte[] buffer, int offset, int size)
+                        throws IOException {
+                    if (size == 0) {
+                        return 0;
                     }
-
-                    @Override
-                    public long getSize() throws IOException {
+                    if (position < 0) {
                         return -1;
                     }
-                });
-            }
+                    if (mPosition != position) {
+                        in.seek(position);
+                        mPosition = position;
+                    }
 
+                    int bytesRead = in.read(buffer, offset, size);
+                    if (bytesRead < 0) {
+                        mPosition = -1; // need to seek on next read
+                        return -1;
+                    }
+
+                    mPosition += bytesRead;
+                    return bytesRead;
+                }
+
+                @Override
+                public long getSize() throws IOException {
+                    return -1;
+                }
+            });
+
+            String exifOffsetStr = retriever.extractMetadata(
+                    MediaMetadataRetriever.METADATA_KEY_EXIF_OFFSET);
+            String exifLengthStr = retriever.extractMetadata(
+                    MediaMetadataRetriever.METADATA_KEY_EXIF_LENGTH);
             String hasImage = retriever.extractMetadata(
                     MediaMetadataRetriever.METADATA_KEY_HAS_IMAGE);
             String hasVideo = retriever.extractMetadata(
@@ -2622,6 +2622,30 @@
                         ExifAttribute.createUShort(orientation, mExifByteOrder));
             }
 
+            if (exifOffsetStr != null && exifLengthStr != null) {
+                int offset = Integer.parseInt(exifOffsetStr);
+                int length = Integer.parseInt(exifLengthStr);
+                if (length <= 6) {
+                    throw new IOException("Invalid exif length");
+                }
+                in.seek(offset);
+                byte[] identifier = new byte[6];
+                if (in.read(identifier) != 6) {
+                    throw new IOException("Can't read identifier");
+                }
+                offset += 6;
+                length -= 6;
+                if (!Arrays.equals(identifier, IDENTIFIER_EXIF_APP1)) {
+                    throw new IOException("Invalid identifier");
+                }
+
+                byte[] bytes = new byte[length];
+                if (in.read(bytes) != length) {
+                    throw new IOException("Can't read exif");
+                }
+                readExifSegment(bytes, IFD_TYPE_PRIMARY);
+            }
+
             if (DEBUG) {
                 Log.d(TAG, "Heif meta: " + width + "x" + height + ", rotation " + rotation);
             }
diff --git a/android/media/Image.java b/android/media/Image.java
index 37c5785..9828275 100644
--- a/android/media/Image.java
+++ b/android/media/Image.java
@@ -193,6 +193,13 @@
     public abstract int getTransform();
 
     /**
+     * Get the scaling mode associated with this frame.
+     * @return The scaling mode that needs to be applied for this frame.
+     * @hide
+     */
+    public abstract int getScalingMode();
+
+    /**
      * Get the {@link android.hardware.HardwareBuffer HardwareBuffer} handle of the input image
      * intended for GPU and/or hardware access.
      * <p>
diff --git a/android/media/ImageReader.java b/android/media/ImageReader.java
index 72d52d3..8ec0e35 100644
--- a/android/media/ImageReader.java
+++ b/android/media/ImageReader.java
@@ -871,6 +871,12 @@
         }
 
         @Override
+        public int getScalingMode() {
+            throwISEIfImageIsInvalid();
+            return mScalingMode;
+        }
+
+        @Override
         public HardwareBuffer getHardwareBuffer() {
             throwISEIfImageIsInvalid();
             return nativeGetHardwareBuffer();
@@ -1004,14 +1010,11 @@
         private long mNativeBuffer;
 
         /**
-         * This field is set by native code during nativeImageSetup().
+         * These fields are set by native code during nativeImageSetup().
          */
         private long mTimestamp;
-
-        /**
-         * This field is set by native code during nativeImageSetup().
-         */
         private int mTransform;
+        private int mScalingMode;
 
         private SurfacePlane[] mPlanes;
         private int mFormat = ImageFormat.UNKNOWN;
diff --git a/android/media/ImageWriter.java b/android/media/ImageWriter.java
index 8ee27ae..397768a 100644
--- a/android/media/ImageWriter.java
+++ b/android/media/ImageWriter.java
@@ -371,7 +371,7 @@
 
         Rect crop = image.getCropRect();
         nativeQueueInputImage(mNativeContext, image, image.getTimestamp(), crop.left, crop.top,
-                crop.right, crop.bottom, image.getTransform());
+                crop.right, crop.bottom, image.getTransform(), image.getScalingMode());
 
         /**
          * Only remove and cleanup the Images that are owned by this
@@ -558,7 +558,7 @@
         Rect crop = image.getCropRect();
         nativeAttachAndQueueImage(mNativeContext, image.getNativeContext(), image.getFormat(),
                 image.getTimestamp(), crop.left, crop.top, crop.right, crop.bottom,
-                image.getTransform());
+                image.getTransform(), image.getScalingMode());
     }
 
     /**
@@ -676,6 +676,7 @@
         private long mTimestamp = DEFAULT_TIMESTAMP;
 
         private int mTransform = 0; //Default no transform
+        private int mScalingMode = 0; //Default frozen scaling mode
 
         public WriterSurfaceImage(ImageWriter writer) {
             mOwner = writer;
@@ -721,6 +722,13 @@
         }
 
         @Override
+        public int getScalingMode() {
+            throwISEIfImageIsInvalid();
+
+            return mScalingMode;
+        }
+
+        @Override
         public long getTimestamp() {
             throwISEIfImageIsInvalid();
 
@@ -866,11 +874,12 @@
     private synchronized native void nativeDequeueInputImage(long nativeCtx, Image wi);
 
     private synchronized native void nativeQueueInputImage(long nativeCtx, Image image,
-            long timestampNs, int left, int top, int right, int bottom, int transform);
+            long timestampNs, int left, int top, int right, int bottom, int transform,
+            int scalingMode);
 
     private synchronized native int nativeAttachAndQueueImage(long nativeCtx,
             long imageNativeBuffer, int imageFormat, long timestampNs, int left,
-            int top, int right, int bottom, int transform);
+            int top, int right, int bottom, int transform, int scalingMode);
 
     private synchronized native void cancelImage(long nativeCtx, Image image);
 
diff --git a/android/media/MediaCodec.java b/android/media/MediaCodec.java
index e3fba0c..1f00c78 100644
--- a/android/media/MediaCodec.java
+++ b/android/media/MediaCodec.java
@@ -3574,6 +3574,7 @@
         private final static int TYPE_YUV = 1;
 
         private final int mTransform = 0; //Default no transform
+        private final int mScalingMode = 0; //Default frozen scaling mode
 
         @Override
         public int getFormat() {
@@ -3600,6 +3601,12 @@
         }
 
         @Override
+        public int getScalingMode() {
+            throwISEIfImageIsInvalid();
+            return mScalingMode;
+        }
+
+        @Override
         public long getTimestamp() {
             throwISEIfImageIsInvalid();
             return mTimestamp;
diff --git a/android/media/MediaCodecInfo.java b/android/media/MediaCodecInfo.java
index 2a601f9..c29300d 100644
--- a/android/media/MediaCodecInfo.java
+++ b/android/media/MediaCodecInfo.java
@@ -2971,6 +2971,8 @@
         public static final int AACObjectLD         = 23;
         public static final int AACObjectHE_PS      = 29;
         public static final int AACObjectELD        = 39;
+        /** xHE-AAC (includes USAC) */
+        public static final int AACObjectXHE        = 42;
 
         // from OMX_VIDEO_VP8LEVELTYPE
         public static final int VP8Level_Version0 = 0x01;
diff --git a/android/media/MediaMetadataRetriever.java b/android/media/MediaMetadataRetriever.java
index 0955dd6..8ab5ec4 100644
--- a/android/media/MediaMetadataRetriever.java
+++ b/android/media/MediaMetadataRetriever.java
@@ -890,5 +890,14 @@
      */
     public static final int METADATA_KEY_VIDEO_FRAME_COUNT = 32;
 
+    /**
+     * @hide
+     */
+    public static final int METADATA_KEY_EXIF_OFFSET = 33;
+
+    /**
+     * @hide
+     */
+    public static final int METADATA_KEY_EXIF_LENGTH = 34;
     // Add more here...
 }
diff --git a/android/media/MediaPlayer.java b/android/media/MediaPlayer.java
index aef31b1..392a1eb 100644
--- a/android/media/MediaPlayer.java
+++ b/android/media/MediaPlayer.java
@@ -19,6 +19,7 @@
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.annotation.TestApi;
 import android.app.ActivityThread;
 import android.content.ContentProvider;
 import android.content.ContentResolver;
@@ -1680,6 +1681,7 @@
      * @hide
      */
     @NonNull
+    @TestApi
     public native BufferingParams getBufferingParams();
 
     /**
@@ -1696,6 +1698,7 @@
      * @throws IllegalArgumentException if params is invalid or not supported.
      * @hide
      */
+    @TestApi
     public native void setBufferingParams(@NonNull BufferingParams params);
 
     /**
@@ -2128,7 +2131,13 @@
             mTimeProvider.close();
             mTimeProvider = null;
         }
-        mOnSubtitleDataListener = null;
+        synchronized(this) {
+            mSubtitleDataListenerDisabled = false;
+            mExtSubtitleDataListener = null;
+            mExtSubtitleDataHandler = null;
+            mOnMediaTimeDiscontinuityListener = null;
+            mOnMediaTimeDiscontinuityHandler = null;
+        }
 
         // Modular DRM clean up
         mOnDrmConfigHelper = null;
@@ -2699,7 +2708,7 @@
     private int mSelectedSubtitleTrackIndex = -1;
     private Vector<InputStream> mOpenSubtitleSources;
 
-    private OnSubtitleDataListener mSubtitleDataListener = new OnSubtitleDataListener() {
+    private final OnSubtitleDataListener mIntSubtitleDataListener = new OnSubtitleDataListener() {
         @Override
         public void onSubtitleData(MediaPlayer mp, SubtitleData data) {
             int index = data.getTrackIndex();
@@ -2725,7 +2734,9 @@
             }
             mSelectedSubtitleTrackIndex = -1;
         }
-        setOnSubtitleDataListener(null);
+        synchronized (this) {
+            mSubtitleDataListenerDisabled = true;
+        }
         if (track == null) {
             return;
         }
@@ -2745,7 +2756,9 @@
                 selectOrDeselectInbandTrack(mSelectedSubtitleTrackIndex, true);
             } catch (IllegalStateException e) {
             }
-            setOnSubtitleDataListener(mSubtitleDataListener);
+            synchronized (this) {
+                mSubtitleDataListenerDisabled = false;
+            }
         }
         // no need to select out-of-band tracks
     }
@@ -3304,6 +3317,7 @@
     private static final int MEDIA_SUBTITLE_DATA = 201;
     private static final int MEDIA_META_DATA = 202;
     private static final int MEDIA_DRM_INFO = 210;
+    private static final int MEDIA_TIME_DISCONTINUITY = 211;
     private static final int MEDIA_AUDIO_ROUTING_CHANGED = 10000;
 
     private TimeProvider mTimeProvider;
@@ -3514,15 +3528,34 @@
                 return;
 
             case MEDIA_SUBTITLE_DATA:
-                OnSubtitleDataListener onSubtitleDataListener = mOnSubtitleDataListener;
-                if (onSubtitleDataListener == null) {
-                    return;
+                final OnSubtitleDataListener extSubtitleListener;
+                final Handler extSubtitleHandler;
+                synchronized(this) {
+                    if (mSubtitleDataListenerDisabled) {
+                        return;
+                    }
+                    extSubtitleListener = mExtSubtitleDataListener;
+                    extSubtitleHandler = mExtSubtitleDataHandler;
                 }
                 if (msg.obj instanceof Parcel) {
                     Parcel parcel = (Parcel) msg.obj;
-                    SubtitleData data = new SubtitleData(parcel);
+                    final SubtitleData data = new SubtitleData(parcel);
                     parcel.recycle();
-                    onSubtitleDataListener.onSubtitleData(mMediaPlayer, data);
+
+                    mIntSubtitleDataListener.onSubtitleData(mMediaPlayer, data);
+
+                    if (extSubtitleListener != null) {
+                        if (extSubtitleHandler == null) {
+                            extSubtitleListener.onSubtitleData(mMediaPlayer, data);
+                        } else {
+                            extSubtitleHandler.post(new Runnable() {
+                                @Override
+                                public void run() {
+                                    extSubtitleListener.onSubtitleData(mMediaPlayer, data);
+                                }
+                            });
+                        }
+                    }
                 }
                 return;
 
@@ -3553,6 +3586,43 @@
                 }
                 return;
 
+            case MEDIA_TIME_DISCONTINUITY:
+                final OnMediaTimeDiscontinuityListener mediaTimeListener;
+                final Handler mediaTimeHandler;
+                synchronized(this) {
+                    mediaTimeListener = mOnMediaTimeDiscontinuityListener;
+                    mediaTimeHandler = mOnMediaTimeDiscontinuityHandler;
+                }
+                if (mediaTimeListener == null) {
+                    return;
+                }
+                if (msg.obj instanceof Parcel) {
+                    Parcel parcel = (Parcel) msg.obj;
+                    parcel.setDataPosition(0);
+                    long anchorMediaUs = parcel.readLong();
+                    long anchorRealUs = parcel.readLong();
+                    float playbackRate = parcel.readFloat();
+                    parcel.recycle();
+                    final MediaTimestamp timestamp;
+                    if (anchorMediaUs != -1 && anchorRealUs != -1) {
+                        timestamp = new MediaTimestamp(
+                                anchorMediaUs /*Us*/, anchorRealUs * 1000 /*Ns*/, playbackRate);
+                    } else {
+                        timestamp = MediaTimestamp.TIMESTAMP_UNKNOWN;
+                    }
+                    if (mediaTimeHandler == null) {
+                        mediaTimeListener.onMediaTimeDiscontinuity(mMediaPlayer, timestamp);
+                    } else {
+                        mediaTimeHandler.post(new Runnable() {
+                            @Override
+                            public void run() {
+                                mediaTimeListener.onMediaTimeDiscontinuity(mMediaPlayer, timestamp);
+                            }
+                        });
+                    }
+                }
+                return;
+
             default:
                 Log.e(TAG, "Unknown message type " + msg.what);
                 return;
@@ -3877,13 +3947,15 @@
     private void setOnSubtitleDataListenerInt(
             @Nullable OnSubtitleDataListener listener, @Nullable Handler handler) {
         synchronized (this) {
-            mOnSubtitleDataListener = listener;
-            mOnSubtitleDataHandler = handler;
+            mExtSubtitleDataListener = listener;
+            mExtSubtitleDataHandler = handler;
         }
     }
 
-    private OnSubtitleDataListener mOnSubtitleDataListener;
-    private Handler mOnSubtitleDataHandler;
+    private boolean mSubtitleDataListenerDisabled;
+    /** External OnSubtitleDataListener, the one set by {@link #setOnSubtitleDataListener}. */
+    private OnSubtitleDataListener mExtSubtitleDataListener;
+    private Handler mExtSubtitleDataHandler;
 
     /**
      * Interface definition of a callback to be invoked when discontinuity in the normal progression
diff --git a/android/media/MediaRecorder.java b/android/media/MediaRecorder.java
index 90b6bff..82d64f3 100644
--- a/android/media/MediaRecorder.java
+++ b/android/media/MediaRecorder.java
@@ -1434,7 +1434,6 @@
         int status = native_getActiveMicrophones(activeMicrophones);
         if (status != AudioManager.SUCCESS) {
             Log.e(TAG, "getActiveMicrophones failed:" + status);
-            return new ArrayList<MicrophoneInfo>();
         }
         AudioManager.setPortIdForMicrophones(activeMicrophones);
 
diff --git a/android/media/MediaTimestamp.java b/android/media/MediaTimestamp.java
index 938dd14..dd43b4e 100644
--- a/android/media/MediaTimestamp.java
+++ b/android/media/MediaTimestamp.java
@@ -98,4 +98,13 @@
                 && (this.nanoTime == that.nanoTime)
                 && (this.clockRate == that.clockRate);
     }
+
+    @Override
+    public String toString() {
+        return getClass().getName()
+                + "{AnchorMediaTimeUs=" + mediaTimeUs
+                + " AnchorSystemNanoTime=" + nanoTime
+                + " clockRate=" + clockRate
+                + "}";
+    }
 }
diff --git a/android/media/PlaybackParams.java b/android/media/PlaybackParams.java
index 938a953..b85e4d0 100644
--- a/android/media/PlaybackParams.java
+++ b/android/media/PlaybackParams.java
@@ -17,6 +17,7 @@
 package android.media;
 
 import android.annotation.IntDef;
+import android.annotation.TestApi;
 import android.os.Parcel;
 import android.os.Parcelable;
 
@@ -151,6 +152,7 @@
      * @param audioStretchMode
      * @return this <code>PlaybackParams</code> instance.
      */
+    @TestApi
     public PlaybackParams setAudioStretchMode(@AudioStretchMode int audioStretchMode) {
         mAudioStretchMode = audioStretchMode;
         mSet |= SET_AUDIO_STRETCH_MODE;
@@ -163,6 +165,7 @@
      * @return audio stretch mode
      * @throws IllegalStateException if the audio stretch mode is not set.
      */
+    @TestApi
     public @AudioStretchMode int getAudioStretchMode() {
         if ((mSet & SET_AUDIO_STRETCH_MODE) == 0) {
             throw new IllegalStateException("audio stretch mode not set");
diff --git a/android/media/PlayerBase.java b/android/media/PlayerBase.java
index 80049ba..7c6367e 100644
--- a/android/media/PlayerBase.java
+++ b/android/media/PlayerBase.java
@@ -31,6 +31,7 @@
 import android.os.ServiceManager;
 import android.util.Log;
 
+import com.android.internal.annotations.GuardedBy;
 import com.android.internal.app.IAppOpsCallback;
 import com.android.internal.app.IAppOpsService;
 
@@ -58,20 +59,29 @@
     protected float mRightVolume = 1.0f;
     protected float mAuxEffectSendLevel = 0.0f;
 
-    // for AppOps
-    private IAppOpsService mAppOps; // may be null
-    private IAppOpsCallback mAppOpsCallback;
-    private boolean mHasAppOpsPlayAudio = true; // sync'd on mLock
+    // NEVER call into AudioService (see getService()) with mLock held: PlayerBase can run in
+    // the same process as AudioService, which can synchronously call back into this class,
+    // causing deadlocks between the two
     private final Object mLock = new Object();
 
+    // for AppOps
+    private @Nullable IAppOpsService mAppOps;
+    private IAppOpsCallback mAppOpsCallback;
+    @GuardedBy("mLock")
+    private boolean mHasAppOpsPlayAudio = true;
+
     private final int mImplType;
     // uniquely identifies the Player Interface throughout the system (P I Id)
-    private int mPlayerIId;
+    private int mPlayerIId = AudioPlaybackConfiguration.PLAYER_PIID_UNASSIGNED;
 
-    private int mState; // sync'd on mLock
-    private int mStartDelayMs = 0; // sync'd on mLock
-    private float mPanMultiplierL = 1.0f; // sync'd on mLock
-    private float mPanMultiplierR = 1.0f; // sync'd on mLock
+    @GuardedBy("mLock")
+    private int mState;
+    @GuardedBy("mLock")
+    private int mStartDelayMs = 0;
+    @GuardedBy("mLock")
+    private float mPanMultiplierL = 1.0f;
+    @GuardedBy("mLock")
+    private float mPanMultiplierR = 1.0f;
 
     /**
      * Constructor. Must be given audio attributes, as they are required for AppOps.
@@ -134,16 +144,24 @@
         }
     }
 
+    private void updateState(int state) {
+        final int piid;
+        synchronized (mLock) {
+            mState = state;
+            piid = mPlayerIId;
+        }
+        try {
+            getService().playerEvent(piid, state);
+        } catch (RemoteException e) {
+            Log.e(TAG, "Error talking to audio service, "
+                    + AudioPlaybackConfiguration.toLogFriendlyPlayerState(state)
+                    + " state will not be tracked for piid=" + piid, e);
+        }
+    }
+
     void baseStart() {
         if (DEBUG) { Log.v(TAG, "baseStart() piid=" + mPlayerIId); }
-        try {
-            synchronized (mLock) {
-                mState = AudioPlaybackConfiguration.PLAYER_STATE_STARTED;
-                getService().playerEvent(mPlayerIId, mState);
-            }
-        } catch (RemoteException e) {
-            Log.e(TAG, "Error talking to audio service, STARTED state will not be tracked", e);
-        }
+        updateState(AudioPlaybackConfiguration.PLAYER_STATE_STARTED);
         synchronized (mLock) {
             if (isRestricted_sync()) {
                 playerSetVolume(true/*muting*/,0, 0);
@@ -165,26 +183,12 @@
 
     void basePause() {
         if (DEBUG) { Log.v(TAG, "basePause() piid=" + mPlayerIId); }
-        try {
-            synchronized (mLock) {
-                mState = AudioPlaybackConfiguration.PLAYER_STATE_PAUSED;
-                getService().playerEvent(mPlayerIId, mState);
-            }
-        } catch (RemoteException e) {
-            Log.e(TAG, "Error talking to audio service, PAUSED state will not be tracked", e);
-        }
+        updateState(AudioPlaybackConfiguration.PLAYER_STATE_PAUSED);
     }
 
     void baseStop() {
         if (DEBUG) { Log.v(TAG, "baseStop() piid=" + mPlayerIId); }
-        try {
-            synchronized (mLock) {
-                mState = AudioPlaybackConfiguration.PLAYER_STATE_STOPPED;
-                getService().playerEvent(mPlayerIId, mState);
-            }
-        } catch (RemoteException e) {
-            Log.e(TAG, "Error talking to audio service, STOPPED state will not be tracked", e);
-        }
+        updateState(AudioPlaybackConfiguration.PLAYER_STATE_STOPPED);
     }
 
     void baseSetPan(float pan) {
@@ -228,12 +232,16 @@
      */
     void baseRelease() {
         if (DEBUG) { Log.v(TAG, "baseRelease() piid=" + mPlayerIId + " state=" + mState); }
+        boolean releasePlayer = false;
+        synchronized (mLock) {
+            if (mState != AudioPlaybackConfiguration.PLAYER_STATE_RELEASED) {
+                releasePlayer = true;
+                mState = AudioPlaybackConfiguration.PLAYER_STATE_RELEASED;
+            }
+        }
         try {
-            synchronized (mLock) {
-                if (mState != AudioPlaybackConfiguration.PLAYER_STATE_RELEASED) {
-                    getService().releasePlayer(mPlayerIId);
-                    mState = AudioPlaybackConfiguration.PLAYER_STATE_RELEASED;
-                }
+            if (releasePlayer) {
+                getService().releasePlayer(mPlayerIId);
             }
         } catch (RemoteException e) {
             Log.e(TAG, "Error talking to audio service, the player will still be tracked", e);
diff --git a/android/media/VolumeShaper.java b/android/media/VolumeShaper.java
index 3068706..b654214 100644
--- a/android/media/VolumeShaper.java
+++ b/android/media/VolumeShaper.java
@@ -18,6 +18,7 @@
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.annotation.TestApi;
 import android.os.Parcel;
 import android.os.Parcelable;
 
@@ -843,6 +844,7 @@
              * @return the same {@code Builder} instance.
              * @throws IllegalArgumentException if flag is not recognized.
              */
+            @TestApi
             public @NonNull Builder setOptionFlags(@OptionFlag int optionFlags) {
                 if ((optionFlags & ~OPTION_FLAG_PUBLIC_ALL) != 0) {
                     throw new IllegalArgumentException("invalid bits in flag: " + optionFlags);
diff --git a/android/media/audiofx/AudioEffect.java b/android/media/audiofx/AudioEffect.java
index 21d6873..24c595f 100644
--- a/android/media/audiofx/AudioEffect.java
+++ b/android/media/audiofx/AudioEffect.java
@@ -18,6 +18,7 @@
 
 import android.annotation.SdkConstant;
 import android.annotation.SdkConstant.SdkConstantType;
+import android.annotation.TestApi;
 import android.app.ActivityThread;
 import android.os.Handler;
 import android.os.Looper;
@@ -133,9 +134,10 @@
               .fromString("7261676f-6d75-7369-6364-28e2fd3ac39e");
 
     /**
-     * Null effect UUID. Used when the UUID for effect type of
+     * Null effect UUID. See {@link AudioEffect(UUID, UUID, int, int)} for use.
      * @hide
      */
+    @TestApi
     public static final UUID EFFECT_TYPE_NULL = UUID
             .fromString("ec7178ec-e5e1-4432-a3f4-4657e6795210");
 
@@ -492,6 +494,7 @@
      * @return true if the device implements the specified effect type, false otherwise.
      * @hide
      */
+    @TestApi
     public static boolean isEffectTypeAvailable(UUID type) {
         AudioEffect.Descriptor[] desc = AudioEffect.queryEffects();
         if (desc == null) {
@@ -544,6 +547,7 @@
      * @throws IllegalStateException
      * @hide
      */
+    @TestApi
     public int setParameter(byte[] param, byte[] value)
             throws IllegalStateException {
         checkState("setParameter()");
@@ -556,6 +560,7 @@
      * @see #setParameter(byte[], byte[])
      * @hide
      */
+    @TestApi
     public int setParameter(int param, int value) throws IllegalStateException {
         byte[] p = intToByteArray(param);
         byte[] v = intToByteArray(value);
@@ -569,6 +574,7 @@
      * @see #setParameter(byte[], byte[])
      * @hide
      */
+    @TestApi
     public int setParameter(int param, short value)
             throws IllegalStateException {
         byte[] p = intToByteArray(param);
@@ -583,6 +589,7 @@
      * @see #setParameter(byte[], byte[])
      * @hide
      */
+    @TestApi
     public int setParameter(int param, byte[] value)
             throws IllegalStateException {
         byte[] p = intToByteArray(param);
@@ -596,6 +603,7 @@
      * @see #setParameter(byte[], byte[])
      * @hide
      */
+    @TestApi
     public int setParameter(int[] param, int[] value)
             throws IllegalStateException {
         if (param.length > 2 || value.length > 2) {
@@ -647,6 +655,7 @@
      * @see #setParameter(byte[], byte[])
      * @hide
      */
+    @TestApi
     public int setParameter(int[] param, byte[] value)
             throws IllegalStateException {
         if (param.length > 2) {
@@ -675,6 +684,7 @@
      * @throws IllegalStateException
      * @hide
      */
+    @TestApi
     public int getParameter(byte[] param, byte[] value)
             throws IllegalStateException {
         checkState("getParameter()");
@@ -688,6 +698,7 @@
      * @see #getParameter(byte[], byte[])
      * @hide
      */
+    @TestApi
     public int getParameter(int param, byte[] value)
             throws IllegalStateException {
         byte[] p = intToByteArray(param);
@@ -703,6 +714,7 @@
      * In case of success, returns the number of meaningful integers in value array.
      * @hide
      */
+    @TestApi
     public int getParameter(int param, int[] value)
             throws IllegalStateException {
         if (value.length > 2) {
@@ -734,6 +746,7 @@
      * In case of success, returns the number of meaningful short integers in value array.
      * @hide
      */
+    @TestApi
     public int getParameter(int param, short[] value)
             throws IllegalStateException {
         if (value.length > 2) {
@@ -799,6 +812,7 @@
      * In case of success, returns the number of meaningful short integers in value array.
      * @hide
      */
+    @TestApi
     public int getParameter(int[] param, short[] value)
             throws IllegalStateException {
         if (param.length > 2 || value.length > 2) {
@@ -938,6 +952,7 @@
      * @param listener
      * @hide
      */
+    @TestApi
     public void setParameterListener(OnParameterChangeListener listener) {
         synchronized (mListenerLock) {
             mParameterChangeListener = listener;
@@ -999,6 +1014,7 @@
      * when a parameter is changed in the effect engine by the controlling application.
      * @hide
      */
+    @TestApi
     public interface OnParameterChangeListener {
         /**
          * Called on the listener to notify it that a parameter value has changed.
@@ -1291,6 +1307,7 @@
     /**
      * @hide
      */
+    @TestApi
     public static boolean isError(int status) {
         return (status < 0);
     }
@@ -1298,6 +1315,7 @@
     /**
      * @hide
      */
+    @TestApi
     public static int byteArrayToInt(byte[] valueBuf) {
         return byteArrayToInt(valueBuf, 0);
 
@@ -1316,6 +1334,7 @@
     /**
      * @hide
      */
+    @TestApi
     public static byte[] intToByteArray(int value) {
         ByteBuffer converter = ByteBuffer.allocate(4);
         converter.order(ByteOrder.nativeOrder());
@@ -1326,6 +1345,7 @@
     /**
      * @hide
      */
+    @TestApi
     public static short byteArrayToShort(byte[] valueBuf) {
         return byteArrayToShort(valueBuf, 0);
     }
@@ -1343,6 +1363,7 @@
     /**
      * @hide
      */
+    @TestApi
     public static byte[] shortToByteArray(short value) {
         ByteBuffer converter = ByteBuffer.allocate(2);
         converter.order(ByteOrder.nativeOrder());
diff --git a/android/media/session/MediaController.java b/android/media/session/MediaController.java
index f16804c..84f85e7 100644
--- a/android/media/session/MediaController.java
+++ b/android/media/session/MediaController.java
@@ -531,7 +531,7 @@
          *
          * @param state The new playback state of the session
          */
-        public void onPlaybackStateChanged(@NonNull PlaybackState state) {
+        public void onPlaybackStateChanged(@Nullable PlaybackState state) {
         }
 
         /**
diff --git a/android/media/session/MediaSessionManager.java b/android/media/session/MediaSessionManager.java
index 519af1b..fbc1438 100644
--- a/android/media/session/MediaSessionManager.java
+++ b/android/media/session/MediaSessionManager.java
@@ -47,6 +47,7 @@
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
+import java.util.Objects;
 import java.util.concurrent.Executor;
 
 /**
@@ -342,12 +343,16 @@
     }
 
     /**
-     * Returns whether the app is trusted.
+     * Checks whether the remote user is a trusted app.
      * <p>
      * An app is trusted if the app holds the android.Manifest.permission.MEDIA_CONTENT_CONTROL
      * permission or has an enabled notification listener.
      *
-     * @param userInfo The remote user info
+     * @param userInfo The remote user info from either
+     *            {@link MediaSession#getCurrentControllerInfo()} or
+     *            {@link MediaBrowserService#getCurrentBrowserInfo()}.
+     * @return {@code true} if the remote user is trusted and its package name matches with the UID.
+     *            {@code false} otherwise.
      */
     public boolean isTrustedForMediaControl(RemoteUserInfo userInfo) {
         if (userInfo.getPackageName() == null) {
@@ -814,6 +819,11 @@
                     && mPid == otherUserInfo.mPid
                     && mUid == otherUserInfo.mUid;
         }
+
+        @Override
+        public int hashCode() {
+            return Objects.hash(mPackageName, mPid, mUid);
+        }
     }
 
     private static final class SessionsChangedWrapper {
diff --git a/android/net/IpSecManager.java b/android/net/IpSecManager.java
index 1525508..a61ea50 100644
--- a/android/net/IpSecManager.java
+++ b/android/net/IpSecManager.java
@@ -20,7 +20,6 @@
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.RequiresPermission;
-import android.annotation.SystemApi;
 import android.annotation.SystemService;
 import android.annotation.TestApi;
 import android.content.Context;
@@ -140,6 +139,7 @@
         }
     }
 
+    private final Context mContext;
     private final IIpSecService mService;
 
     /**
@@ -336,6 +336,9 @@
      */
     public void applyTransportModeTransform(@NonNull Socket socket,
             @PolicyDirection int direction, @NonNull IpSecTransform transform) throws IOException {
+        // Ensure creation of FD. See b/77548890 for more details.
+        socket.getSoLinger();
+
         applyTransportModeTransform(socket.getFileDescriptor$(), direction, transform);
     }
 
@@ -440,6 +443,9 @@
      * @throws IOException indicating that the transform could not be removed from the socket
      */
     public void removeTransportModeTransforms(@NonNull Socket socket) throws IOException {
+        // Ensure creation of FD. See b/77548890 for more details.
+        socket.getSoLinger();
+
         removeTransportModeTransforms(socket.getFileDescriptor$());
     }
 
@@ -659,8 +665,8 @@
      * to create Network objects which are accessible to the Android system.
      * @hide
      */
-    @SystemApi
     public static final class IpSecTunnelInterface implements AutoCloseable {
+        private final String mOpPackageName;
         private final IIpSecService mService;
         private final InetAddress mRemoteAddress;
         private final InetAddress mLocalAddress;
@@ -682,13 +688,14 @@
          * tunneled traffic.
          *
          * @param address the local address for traffic inside the tunnel
+         * @param prefixLen length of the InetAddress prefix
          * @hide
          */
-        @SystemApi
         @RequiresPermission(android.Manifest.permission.MANAGE_IPSEC_TUNNELS)
-        public void addAddress(@NonNull LinkAddress address) throws IOException {
+        public void addAddress(@NonNull InetAddress address, int prefixLen) throws IOException {
             try {
-                mService.addAddressToTunnelInterface(mResourceId, address);
+                mService.addAddressToTunnelInterface(
+                        mResourceId, new LinkAddress(address, prefixLen), mOpPackageName);
             } catch (RemoteException e) {
                 throw e.rethrowFromSystemServer();
             }
@@ -700,22 +707,24 @@
          * <p>Remove an address which was previously added to the IpSecTunnelInterface
          *
          * @param address to be removed
+         * @param prefixLen length of the InetAddress prefix
          * @hide
          */
-        @SystemApi
         @RequiresPermission(android.Manifest.permission.MANAGE_IPSEC_TUNNELS)
-        public void removeAddress(@NonNull LinkAddress address) throws IOException {
+        public void removeAddress(@NonNull InetAddress address, int prefixLen) throws IOException {
             try {
-                mService.removeAddressFromTunnelInterface(mResourceId, address);
+                mService.removeAddressFromTunnelInterface(
+                        mResourceId, new LinkAddress(address, prefixLen), mOpPackageName);
             } catch (RemoteException e) {
                 throw e.rethrowFromSystemServer();
             }
         }
 
-        private IpSecTunnelInterface(@NonNull IIpSecService service,
+        private IpSecTunnelInterface(@NonNull Context ctx, @NonNull IIpSecService service,
                 @NonNull InetAddress localAddress, @NonNull InetAddress remoteAddress,
                 @NonNull Network underlyingNetwork)
                 throws ResourceUnavailableException, IOException {
+            mOpPackageName = ctx.getOpPackageName();
             mService = service;
             mLocalAddress = localAddress;
             mRemoteAddress = remoteAddress;
@@ -727,7 +736,8 @@
                                 localAddress.getHostAddress(),
                                 remoteAddress.getHostAddress(),
                                 underlyingNetwork,
-                                new Binder());
+                                new Binder(),
+                                mOpPackageName);
                 switch (result.status) {
                     case Status.OK:
                         break;
@@ -756,7 +766,7 @@
         @Override
         public void close() {
             try {
-                mService.deleteTunnelInterface(mResourceId);
+                mService.deleteTunnelInterface(mResourceId, mOpPackageName);
                 mResourceId = INVALID_RESOURCE_ID;
             } catch (RemoteException e) {
                 throw e.rethrowFromSystemServer();
@@ -795,13 +805,13 @@
      * @throws ResourceUnavailableException indicating that too many encapsulation sockets are open
      * @hide
      */
-    @SystemApi
     @NonNull
     @RequiresPermission(android.Manifest.permission.MANAGE_IPSEC_TUNNELS)
     public IpSecTunnelInterface createIpSecTunnelInterface(@NonNull InetAddress localAddress,
             @NonNull InetAddress remoteAddress, @NonNull Network underlyingNetwork)
             throws ResourceUnavailableException, IOException {
-        return new IpSecTunnelInterface(mService, localAddress, remoteAddress, underlyingNetwork);
+        return new IpSecTunnelInterface(
+                mContext, mService, localAddress, remoteAddress, underlyingNetwork);
     }
 
     /**
@@ -821,13 +831,13 @@
      *         layer failure.
      * @hide
      */
-    @SystemApi
     @RequiresPermission(android.Manifest.permission.MANAGE_IPSEC_TUNNELS)
     public void applyTunnelModeTransform(@NonNull IpSecTunnelInterface tunnel,
             @PolicyDirection int direction, @NonNull IpSecTransform transform) throws IOException {
         try {
             mService.applyTunnelModeTransform(
-                    tunnel.getResourceId(), direction, transform.getResourceId());
+                    tunnel.getResourceId(), direction,
+                    transform.getResourceId(), mContext.getOpPackageName());
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -839,7 +849,8 @@
      * @param context the application context for this manager
      * @hide
      */
-    public IpSecManager(IIpSecService service) {
+    public IpSecManager(Context ctx, IIpSecService service) {
+        mContext = ctx;
         mService = checkNotNull(service, "missing service");
     }
 }
diff --git a/android/net/IpSecTransform.java b/android/net/IpSecTransform.java
index 099fe02..62f7996 100644
--- a/android/net/IpSecTransform.java
+++ b/android/net/IpSecTransform.java
@@ -22,7 +22,6 @@
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.RequiresPermission;
-import android.annotation.SystemApi;
 import android.content.Context;
 import android.os.Binder;
 import android.os.Handler;
@@ -130,7 +129,8 @@
         synchronized (this) {
             try {
                 IIpSecService svc = getIpSecService();
-                IpSecTransformResponse result = svc.createTransform(mConfig, new Binder());
+                IpSecTransformResponse result = svc.createTransform(
+                        mConfig, new Binder(), mContext.getOpPackageName());
                 int status = result.status;
                 checkResultStatus(status);
                 mResourceId = result.resourceId;
@@ -249,7 +249,6 @@
      *
      * @hide
      */
-    @SystemApi
     public static class NattKeepaliveCallback {
         /** The specified {@code Network} is not connected. */
         public static final int ERROR_INVALID_NETWORK = 1;
@@ -280,7 +279,6 @@
      *
      * @hide
      */
-    @SystemApi
     @RequiresPermission(anyOf = {
             android.Manifest.permission.MANAGE_IPSEC_TUNNELS,
             android.Manifest.permission.PACKET_KEEPALIVE_OFFLOAD
@@ -323,7 +321,6 @@
      *
      * @hide
      */
-    @SystemApi
     @RequiresPermission(anyOf = {
             android.Manifest.permission.MANAGE_IPSEC_TUNNELS,
             android.Manifest.permission.PACKET_KEEPALIVE_OFFLOAD
@@ -476,7 +473,6 @@
          * @throws IOException indicating other errors
          * @hide
          */
-        @SystemApi
         @NonNull
         @RequiresPermission(android.Manifest.permission.MANAGE_IPSEC_TUNNELS)
         public IpSecTransform buildTunnelModeTransform(
diff --git a/android/net/NetworkCapabilities.java b/android/net/NetworkCapabilities.java
index 374b3ab..a8e8179 100644
--- a/android/net/NetworkCapabilities.java
+++ b/android/net/NetworkCapabilities.java
@@ -254,9 +254,8 @@
     /**
      * Indicates that this network is not congested.
      * <p>
-     * When a network is congested, the device should defer network traffic that
-     * can be done at a later time without breaking developer contracts.
-     * @hide
+     * When a network is congested, applications should defer network traffic
+     * that can be done at a later time, such as uploading analytics.
      */
     public static final int NET_CAPABILITY_NOT_CONGESTED = 20;
 
@@ -318,7 +317,7 @@
 
     /**
      * Capabilities that suggest that a network is restricted.
-     * {@see #maybeMarkCapabilitiesRestricted}.
+     * {@see #maybeMarkCapabilitiesRestricted}, {@see #FORCE_RESTRICTED_CAPABILITIES}
      */
     @VisibleForTesting
     /* package */ static final long RESTRICTED_CAPABILITIES =
@@ -329,7 +328,13 @@
             (1 << NET_CAPABILITY_IA) |
             (1 << NET_CAPABILITY_IMS) |
             (1 << NET_CAPABILITY_RCS) |
-            (1 << NET_CAPABILITY_XCAP) |
+            (1 << NET_CAPABILITY_XCAP);
+
+    /**
+     * Capabilities that force network to be restricted.
+     * {@see #maybeMarkCapabilitiesRestricted}.
+     */
+    private static final long FORCE_RESTRICTED_CAPABILITIES =
             (1 << NET_CAPABILITY_OEM_PAID);
 
     /**
@@ -533,16 +538,21 @@
      * @hide
      */
     public void maybeMarkCapabilitiesRestricted() {
+        // Check if we have any capability that forces the network to be restricted.
+        final boolean forceRestrictedCapability =
+                (mNetworkCapabilities & FORCE_RESTRICTED_CAPABILITIES) != 0;
+
         // Verify there aren't any unrestricted capabilities.  If there are we say
-        // the whole thing is unrestricted.
+        // the whole thing is unrestricted unless it is forced to be restricted.
         final boolean hasUnrestrictedCapabilities =
-                ((mNetworkCapabilities & UNRESTRICTED_CAPABILITIES) != 0);
+                (mNetworkCapabilities & UNRESTRICTED_CAPABILITIES) != 0;
 
         // Must have at least some restricted capabilities.
         final boolean hasRestrictedCapabilities =
-                ((mNetworkCapabilities & RESTRICTED_CAPABILITIES) != 0);
+                (mNetworkCapabilities & RESTRICTED_CAPABILITIES) != 0;
 
-        if (hasRestrictedCapabilities && !hasUnrestrictedCapabilities) {
+        if (forceRestrictedCapability
+                || (hasRestrictedCapabilities && !hasUnrestrictedCapabilities)) {
             removeCapability(NET_CAPABILITY_NOT_RESTRICTED);
         }
     }
diff --git a/android/net/NetworkPolicy.java b/android/net/NetworkPolicy.java
index 1a28732..e84c85e 100644
--- a/android/net/NetworkPolicy.java
+++ b/android/net/NetworkPolicy.java
@@ -19,7 +19,7 @@
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.util.BackupUtils;
-import android.util.Pair;
+import android.util.Range;
 import android.util.RecurrenceRule;
 
 import com.android.internal.util.Preconditions;
@@ -136,7 +136,7 @@
         return 0;
     }
 
-    public Iterator<Pair<ZonedDateTime, ZonedDateTime>> cycleIterator() {
+    public Iterator<Range<ZonedDateTime>> cycleIterator() {
         return cycleRule.cycleIterator();
     }
 
diff --git a/android/net/NetworkPolicyManager.java b/android/net/NetworkPolicyManager.java
index bf6b7e0..6546c39 100644
--- a/android/net/NetworkPolicyManager.java
+++ b/android/net/NetworkPolicyManager.java
@@ -31,6 +31,7 @@
 import android.os.UserHandle;
 import android.util.DebugUtils;
 import android.util.Pair;
+import android.util.Range;
 
 import com.google.android.collect.Sets;
 
@@ -258,8 +259,21 @@
     }
 
     /** {@hide} */
+    @Deprecated
     public static Iterator<Pair<ZonedDateTime, ZonedDateTime>> cycleIterator(NetworkPolicy policy) {
-        return policy.cycleIterator();
+        final Iterator<Range<ZonedDateTime>> it = policy.cycleIterator();
+        return new Iterator<Pair<ZonedDateTime, ZonedDateTime>>() {
+            @Override
+            public boolean hasNext() {
+                return it.hasNext();
+            }
+
+            @Override
+            public Pair<ZonedDateTime, ZonedDateTime> next() {
+                final Range<ZonedDateTime> r = it.next();
+                return Pair.create(r.getLower(), r.getUpper());
+            }
+        };
     }
 
     /**
diff --git a/android/net/NetworkRequest.java b/android/net/NetworkRequest.java
index 3d9d6e2..bd4a27c 100644
--- a/android/net/NetworkRequest.java
+++ b/android/net/NetworkRequest.java
@@ -168,9 +168,6 @@
          * the requested network's required capabilities.  Note that when searching
          * for a network to satisfy a request, all capabilities requested must be
          * satisfied.
-         * <p>
-         * If the given capability was previously added to the list of unwanted capabilities
-         * then the capability will also be removed from the list of unwanted capabilities.
          *
          * @param capability The capability to add.
          * @return The builder to facilitate chaining
@@ -182,8 +179,7 @@
         }
 
         /**
-         * Removes (if found) the given capability from this builder instance from both required
-         * and unwanted capabilities lists.
+         * Removes (if found) the given capability from this builder instance.
          *
          * @param capability The capability to remove.
          * @return The builder to facilitate chaining.
@@ -231,6 +227,8 @@
          *
          * @param capability The capability to add to unwanted capability list.
          * @return The builder to facilitate chaining.
+         *
+         * @removed
          */
         public Builder addUnwantedCapability(@NetworkCapabilities.NetCapability int capability) {
             mNetworkCapabilities.addUnwantedCapability(capability);
@@ -436,6 +434,15 @@
     }
 
     /**
+     * @see Builder#addUnwantedCapability(int)
+     *
+     * @removed
+     */
+    public boolean hasUnwantedCapability(@NetCapability int capability) {
+        return networkCapabilities.hasUnwantedCapability(capability);
+    }
+
+    /**
      * @see Builder#addTransportType(int)
      */
     public boolean hasTransport(@Transport int transportType) {
diff --git a/android/net/NetworkState.java b/android/net/NetworkState.java
index b00cb48..321f971 100644
--- a/android/net/NetworkState.java
+++ b/android/net/NetworkState.java
@@ -26,6 +26,8 @@
  * @hide
  */
 public class NetworkState implements Parcelable {
+    private static final boolean SANITY_CHECK_ROAMING = false;
+
     public static final NetworkState EMPTY = new NetworkState(null, null, null, null, null, null);
 
     public final NetworkInfo networkInfo;
@@ -47,7 +49,7 @@
 
         // This object is an atomic view of a network, so the various components
         // should always agree on roaming state.
-        if (networkInfo != null && networkCapabilities != null) {
+        if (SANITY_CHECK_ROAMING && networkInfo != null && networkCapabilities != null) {
             if (networkInfo.isRoaming() == networkCapabilities
                     .hasCapability(NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING)) {
                 Slog.wtf("NetworkState", "Roaming state disagreement between " + networkInfo
diff --git a/android/net/apf/ApfFilter.java b/android/net/apf/ApfFilter.java
index d190432..d5ff2dd 100644
--- a/android/net/apf/ApfFilter.java
+++ b/android/net/apf/ApfFilter.java
@@ -16,21 +16,21 @@
 
 package android.net.apf;
 
+import static android.net.util.NetworkConstants.*;
 import static android.system.OsConstants.*;
-
 import static com.android.internal.util.BitUtils.bytesToBEInt;
 import static com.android.internal.util.BitUtils.getUint16;
 import static com.android.internal.util.BitUtils.getUint32;
 import static com.android.internal.util.BitUtils.getUint8;
-import static com.android.internal.util.BitUtils.uint16;
 import static com.android.internal.util.BitUtils.uint32;
-import static com.android.internal.util.BitUtils.uint8;
 
-import android.os.SystemClock;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
 import android.net.LinkAddress;
 import android.net.LinkProperties;
 import android.net.NetworkUtils;
-import android.net.apf.ApfGenerator;
 import android.net.apf.ApfGenerator.IllegalInstructionException;
 import android.net.apf.ApfGenerator.Register;
 import android.net.ip.IpClient;
@@ -39,31 +39,29 @@
 import android.net.metrics.IpConnectivityLog;
 import android.net.metrics.RaEvent;
 import android.net.util.InterfaceParams;
+import android.os.PowerManager;
+import android.os.SystemClock;
 import android.system.ErrnoException;
 import android.system.Os;
 import android.system.PacketSocketAddress;
 import android.text.format.DateUtils;
 import android.util.Log;
 import android.util.Pair;
-
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.HexDump;
 import com.android.internal.util.IndentingPrintWriter;
-
 import java.io.FileDescriptor;
 import java.io.IOException;
-import java.lang.Thread;
 import java.net.Inet4Address;
 import java.net.Inet6Address;
 import java.net.InetAddress;
 import java.net.SocketException;
 import java.net.UnknownHostException;
-import java.nio.ByteBuffer;
 import java.nio.BufferUnderflowException;
+import java.nio.ByteBuffer;
 import java.util.ArrayList;
 import java.util.Arrays;
-
 import libcore.io.IoBridge;
 
 /**
@@ -215,10 +213,6 @@
             { (byte) 0xff, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 };
 
     private static final int ICMP6_TYPE_OFFSET = ETH_HEADER_LEN + IPV6_HEADER_LEN;
-    private static final int ICMP6_ROUTER_SOLICITATION = 133;
-    private static final int ICMP6_ROUTER_ADVERTISEMENT = 134;
-    private static final int ICMP6_NEIGHBOR_SOLICITATION = 135;
-    private static final int ICMP6_NEIGHBOR_ANNOUNCEMENT = 136;
 
     // NOTE: this must be added to the IPv4 header length in IPV4_HEADER_SIZE_MEMORY_SLOT
     private static final int UDP_DESTINATION_PORT_OFFSET = ETH_HEADER_LEN + 2;
@@ -258,9 +252,26 @@
     private long mUniqueCounter;
     @GuardedBy("this")
     private boolean mMulticastFilter;
+    @GuardedBy("this")
+    private boolean mInDozeMode;
     private final boolean mDrop802_3Frames;
     private final int[] mEthTypeBlackList;
 
+    // Detects doze mode state transitions.
+    private final BroadcastReceiver mDeviceIdleReceiver = new BroadcastReceiver() {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            String action = intent.getAction();
+            if (action.equals(PowerManager.ACTION_DEVICE_IDLE_MODE_CHANGED)) {
+                PowerManager powerManager =
+                        (PowerManager) context.getSystemService(Context.POWER_SERVICE);
+                final boolean deviceIdle = powerManager.isDeviceIdleMode();
+                setDozeMode(deviceIdle);
+            }
+        }
+    };
+    private final Context mContext;
+
     // Our IPv4 address, if we have just one, otherwise null.
     @GuardedBy("this")
     private byte[] mIPv4Address;
@@ -269,13 +280,14 @@
     private int mIPv4PrefixLength;
 
     @VisibleForTesting
-    ApfFilter(ApfConfiguration config, InterfaceParams ifParams,
+    ApfFilter(Context context, ApfConfiguration config, InterfaceParams ifParams,
             IpClient.Callback ipClientCallback, IpConnectivityLog log) {
         mApfCapabilities = config.apfCapabilities;
         mIpClientCallback = ipClientCallback;
         mInterfaceParams = ifParams;
         mMulticastFilter = config.multicastFilter;
         mDrop802_3Frames = config.ieee802_3Filter;
+        mContext = context;
 
         // Now fill the black list from the passed array
         mEthTypeBlackList = filterEthTypeBlackList(config.ethTypeBlackList);
@@ -284,6 +296,10 @@
 
         // TODO: ApfFilter should not generate programs until IpClient sends provisioning success.
         maybeStartFilter();
+
+        // Listen for doze-mode transition changes to enable/disable the IPv6 multicast filter.
+        mContext.registerReceiver(mDeviceIdleReceiver,
+                new IntentFilter(PowerManager.ACTION_DEVICE_IDLE_MODE_CHANGED));
     }
 
     private void log(String s) {
@@ -522,7 +538,7 @@
             // to our packet socket. b/29586253
             if (getUint16(mPacket, ETH_ETHERTYPE_OFFSET) != ETH_P_IPV6 ||
                     getUint8(mPacket, IPV6_NEXT_HEADER_OFFSET) != IPPROTO_ICMPV6 ||
-                    getUint8(mPacket, ICMP6_TYPE_OFFSET) != ICMP6_ROUTER_ADVERTISEMENT) {
+                    getUint8(mPacket, ICMP6_TYPE_OFFSET) != ICMPV6_ROUTER_ADVERTISEMENT) {
                 throw new InvalidRaException("Not an ICMP6 router advertisement");
             }
 
@@ -889,10 +905,11 @@
     private void generateIPv6FilterLocked(ApfGenerator gen) throws IllegalInstructionException {
         // Here's a basic summary of what the IPv6 filter program does:
         //
-        // if it's not ICMPv6:
-        //   if it's multicast and we're dropping multicast:
-        //     drop
-        //   pass
+        // if we're dropping multicast
+        //   if it's not IPCMv6 or it's ICMPv6 but we're in doze mode:
+        //     if it's multicast:
+        //       drop
+        //     pass
         // if it's ICMPv6 RS to any:
         //   drop
         // if it's ICMPv6 NA to ff02::1:
@@ -902,28 +919,44 @@
 
         // Drop multicast if the multicast filter is enabled.
         if (mMulticastFilter) {
-            // Don't touch ICMPv6 multicast here, we deal with it in more detail later.
-            String skipIpv6MulticastFilterLabel = "skipIPv6MulticastFilter";
-            gen.addJumpIfR0Equals(IPPROTO_ICMPV6, skipIpv6MulticastFilterLabel);
+            final String skipIPv6MulticastFilterLabel = "skipIPv6MulticastFilter";
+            final String dropAllIPv6MulticastsLabel = "dropAllIPv6Multicast";
 
-            // Drop all other packets sent to ff00::/8.
+            // While in doze mode, drop ICMPv6 multicast pings, let the others pass.
+            // While awake, let all ICMPv6 multicasts through.
+            if (mInDozeMode) {
+                // Not ICMPv6? -> Proceed to multicast filtering
+                gen.addJumpIfR0NotEquals(IPPROTO_ICMPV6, dropAllIPv6MulticastsLabel);
+
+                // ICMPv6 but not ECHO? -> Skip the multicast filter.
+                // (ICMPv6 ECHO requests will go through the multicast filter below).
+                gen.addLoad8(Register.R0, ICMP6_TYPE_OFFSET);
+                gen.addJumpIfR0NotEquals(ICMPV6_ECHO_REQUEST_TYPE, skipIPv6MulticastFilterLabel);
+            } else {
+                gen.addJumpIfR0Equals(IPPROTO_ICMPV6, skipIPv6MulticastFilterLabel);
+            }
+
+            // Drop all other packets sent to ff00::/8 (multicast prefix).
+            gen.defineLabel(dropAllIPv6MulticastsLabel);
             gen.addLoad8(Register.R0, IPV6_DEST_ADDR_OFFSET);
             gen.addJumpIfR0Equals(0xff, gen.DROP_LABEL);
-            // Not multicast and not ICMPv6. Pass.
+            // Not multicast. Pass.
             gen.addJump(gen.PASS_LABEL);
-            gen.defineLabel(skipIpv6MulticastFilterLabel);
+            gen.defineLabel(skipIPv6MulticastFilterLabel);
         } else {
             // If not ICMPv6, pass.
             gen.addJumpIfR0NotEquals(IPPROTO_ICMPV6, gen.PASS_LABEL);
         }
 
+        // If we got this far, the packet is ICMPv6.  Drop some specific types.
+
         // Add unsolicited multicast neighbor announcements filter
         String skipUnsolicitedMulticastNALabel = "skipUnsolicitedMulticastNA";
         gen.addLoad8(Register.R0, ICMP6_TYPE_OFFSET);
         // Drop all router solicitations (b/32833400)
-        gen.addJumpIfR0Equals(ICMP6_ROUTER_SOLICITATION, gen.DROP_LABEL);
+        gen.addJumpIfR0Equals(ICMPV6_ROUTER_SOLICITATION, gen.DROP_LABEL);
         // If not neighbor announcements, skip filter.
-        gen.addJumpIfR0NotEquals(ICMP6_NEIGHBOR_ANNOUNCEMENT, skipUnsolicitedMulticastNALabel);
+        gen.addJumpIfR0NotEquals(ICMPV6_NEIGHBOR_ADVERTISEMENT, skipUnsolicitedMulticastNALabel);
         // If to ff02::1, drop.
         // TODO: Drop only if they don't contain the address of on-link neighbours.
         gen.addLoadImmediate(Register.R0, IPV6_DEST_ADDR_OFFSET);
@@ -1168,9 +1201,9 @@
      * Create an {@link ApfFilter} if {@code apfCapabilities} indicates support for packet
      * filtering using APF programs.
      */
-    public static ApfFilter maybeCreate(ApfConfiguration config,
+    public static ApfFilter maybeCreate(Context context, ApfConfiguration config,
             InterfaceParams ifParams, IpClient.Callback ipClientCallback) {
-        if (config == null || ifParams == null) return null;
+        if (context == null || config == null || ifParams == null) return null;
         ApfCapabilities apfCapabilities =  config.apfCapabilities;
         if (apfCapabilities == null) return null;
         if (apfCapabilities.apfVersionSupported == 0) return null;
@@ -1187,7 +1220,8 @@
             Log.e(TAG, "Unsupported APF version: " + apfCapabilities.apfVersionSupported);
             return null;
         }
-        return new ApfFilter(config, ifParams, ipClientCallback, new IpConnectivityLog());
+
+        return new ApfFilter(context, config, ifParams, ipClientCallback, new IpConnectivityLog());
     }
 
     public synchronized void shutdown() {
@@ -1197,12 +1231,11 @@
             mReceiveThread = null;
         }
         mRas.clear();
+        mContext.unregisterReceiver(mDeviceIdleReceiver);
     }
 
     public synchronized void setMulticastFilter(boolean isEnabled) {
-        if (mMulticastFilter == isEnabled) {
-            return;
-        }
+        if (mMulticastFilter == isEnabled) return;
         mMulticastFilter = isEnabled;
         if (!isEnabled) {
             mNumProgramUpdatesAllowingMulticast++;
@@ -1210,6 +1243,13 @@
         installNewProgramLocked();
     }
 
+    @VisibleForTesting
+    public synchronized void setDozeMode(boolean isEnabled) {
+        if (mInDozeMode == isEnabled) return;
+        mInDozeMode = isEnabled;
+        installNewProgramLocked();
+    }
+
     /** Find the single IPv4 LinkAddress if there is one, otherwise return null. */
     private static LinkAddress findIPv4LinkAddress(LinkProperties lp) {
         LinkAddress ipv4Address = null;
diff --git a/android/net/dns/ResolvUtil.java b/android/net/dns/ResolvUtil.java
new file mode 100644
index 0000000..97d20f4
--- /dev/null
+++ b/android/net/dns/ResolvUtil.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.dns;
+
+import android.net.Network;
+import android.net.NetworkUtils;
+import android.system.GaiException;
+import android.system.OsConstants;
+import android.system.StructAddrinfo;
+
+import libcore.io.Libcore;
+
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+
+
+/**
+ * DNS resolution utility class.
+ *
+ * @hide
+ */
+public class ResolvUtil {
+    // Non-portable DNS resolution flag.
+    private static final long NETID_USE_LOCAL_NAMESERVERS = 0x80000000L;
+
+    private ResolvUtil() {}
+
+    public static InetAddress[] blockingResolveAllLocally(Network network, String name)
+            throws UnknownHostException {
+        final StructAddrinfo hints = new StructAddrinfo();
+        // Unnecessary, but expressly no AI_ADDRCONFIG.
+        hints.ai_flags = 0;
+        // Fetch all IP addresses at once to minimize re-resolution.
+        hints.ai_family = OsConstants.AF_UNSPEC;
+        hints.ai_socktype = OsConstants.SOCK_DGRAM;
+
+        final Network networkForResolv = getNetworkWithUseLocalNameserversFlag(network);
+
+        try {
+            return Libcore.os.android_getaddrinfo(name, hints, (int) networkForResolv.netId);
+        } catch (GaiException gai) {
+            gai.rethrowAsUnknownHostException(name + ": TLS-bypass resolution failed");
+            return null;  // keep compiler quiet
+        }
+    }
+
+    public static Network getNetworkWithUseLocalNameserversFlag(Network network) {
+        final long netidForResolv = NETID_USE_LOCAL_NAMESERVERS | (long) network.netId;
+        return new Network((int) netidForResolv);
+    }
+}
diff --git a/android/net/http/X509TrustManagerExtensions.java b/android/net/http/X509TrustManagerExtensions.java
index e0fa63a..f9b6dfc 100644
--- a/android/net/http/X509TrustManagerExtensions.java
+++ b/android/net/http/X509TrustManagerExtensions.java
@@ -21,7 +21,6 @@
 
 import com.android.org.conscrypt.TrustManagerImpl;
 
-import java.lang.reflect.Field;
 import java.lang.reflect.InvocationTargetException;
 import java.lang.reflect.Method;
 import java.security.cert.CertificateException;
@@ -133,8 +132,6 @@
     /**
      * Returns {@code true} if the TrustManager uses the same trust configuration for the provided
      * hostnames.
-     *
-     * @hide
      */
     @SystemApi
     public boolean isSameTrustConfiguration(String hostname1, String hostname2) {
diff --git a/android/net/ip/IpClient.java b/android/net/ip/IpClient.java
index 9863370..87249df 100644
--- a/android/net/ip/IpClient.java
+++ b/android/net/ip/IpClient.java
@@ -40,6 +40,7 @@
 import android.net.util.NetdService;
 import android.net.util.NetworkConstants;
 import android.net.util.SharedLog;
+import android.os.ConditionVariable;
 import android.os.INetworkManagementService;
 import android.os.Message;
 import android.os.RemoteException;
@@ -150,6 +151,28 @@
         public void setNeighborDiscoveryOffload(boolean enable) {}
     }
 
+    public static class WaitForProvisioningCallback extends Callback {
+        private final ConditionVariable mCV = new ConditionVariable();
+        private LinkProperties mCallbackLinkProperties;
+
+        public LinkProperties waitForProvisioning() {
+            mCV.block();
+            return mCallbackLinkProperties;
+        }
+
+        @Override
+        public void onProvisioningSuccess(LinkProperties newLp) {
+            mCallbackLinkProperties = newLp;
+            mCV.open();
+        }
+
+        @Override
+        public void onProvisioningFailure(LinkProperties newLp) {
+            mCallbackLinkProperties = null;
+            mCV.open();
+        }
+    }
+
     // Use a wrapper class to log in order to ensure complete and detailed
     // logging. This method is lighter weight than annotations/reflection
     // and has the following benefits:
@@ -281,6 +304,11 @@
                 return this;
             }
 
+            public Builder withoutMultinetworkPolicyTracker() {
+                mConfig.mUsingMultinetworkPolicyTracker = false;
+                return this;
+            }
+
             public Builder withoutIpReachabilityMonitor() {
                 mConfig.mUsingIpReachabilityMonitor = false;
                 return this;
@@ -343,6 +371,7 @@
 
         /* package */ boolean mEnableIPv4 = true;
         /* package */ boolean mEnableIPv6 = true;
+        /* package */ boolean mUsingMultinetworkPolicyTracker = true;
         /* package */ boolean mUsingIpReachabilityMonitor = true;
         /* package */ int mRequestedPreDhcpActionMs;
         /* package */ InitialConfiguration mInitialConfig;
@@ -374,6 +403,7 @@
             return new StringJoiner(", ", getClass().getSimpleName() + "{", "}")
                     .add("mEnableIPv4: " + mEnableIPv4)
                     .add("mEnableIPv6: " + mEnableIPv6)
+                    .add("mUsingMultinetworkPolicyTracker: " + mUsingMultinetworkPolicyTracker)
                     .add("mUsingIpReachabilityMonitor: " + mUsingIpReachabilityMonitor)
                     .add("mRequestedPreDhcpActionMs: " + mRequestedPreDhcpActionMs)
                     .add("mInitialConfig: " + mInitialConfig)
@@ -559,7 +589,6 @@
     private final NetlinkTracker mNetlinkTracker;
     private final WakeupMessage mProvisioningTimeoutAlarm;
     private final WakeupMessage mDhcpActionTimeoutAlarm;
-    private final MultinetworkPolicyTracker mMultinetworkPolicyTracker;
     private final SharedLog mLog;
     private final LocalLog mConnectivityPacketLog;
     private final MessageHandlingLogger mMsgStateLogger;
@@ -573,6 +602,7 @@
      */
     private LinkProperties mLinkProperties;
     private ProvisioningConfiguration mConfiguration;
+    private MultinetworkPolicyTracker mMultinetworkPolicyTracker;
     private IpReachabilityMonitor mIpReachabilityMonitor;
     private DhcpClient mDhcpClient;
     private DhcpResults mDhcpResults;
@@ -685,9 +715,6 @@
         mLinkProperties = new LinkProperties();
         mLinkProperties.setInterfaceName(mInterfaceName);
 
-        mMultinetworkPolicyTracker = new MultinetworkPolicyTracker(mContext, getHandler(),
-                () -> { mLog.log("OBSERVED AvoidBadWifi changed"); });
-
         mProvisioningTimeoutAlarm = new WakeupMessage(mContext, getHandler(),
                 mTag + ".EVENT_PROVISIONING_TIMEOUT", EVENT_PROVISIONING_TIMEOUT);
         mDhcpActionTimeoutAlarm = new WakeupMessage(mContext, getHandler(),
@@ -719,8 +746,6 @@
         } catch (RemoteException e) {
             logError("Couldn't register NetlinkTracker: %s", e);
         }
-
-        mMultinetworkPolicyTracker.start();
     }
 
     private void stopStateMachineUpdaters() {
@@ -729,8 +754,6 @@
         } catch (RemoteException e) {
             logError("Couldn't unregister NetlinkTracker: %s", e);
         }
-
-        mMultinetworkPolicyTracker.shutdown();
     }
 
     @Override
@@ -1028,7 +1051,8 @@
         // Note that we can still be disconnected by IpReachabilityMonitor
         // if the IPv6 default gateway (but not the IPv6 DNS servers; see
         // accompanying code in IpReachabilityMonitor) is unreachable.
-        final boolean ignoreIPv6ProvisioningLoss = !mMultinetworkPolicyTracker.getAvoidBadWifi();
+        final boolean ignoreIPv6ProvisioningLoss = (mMultinetworkPolicyTracker != null)
+                && !mMultinetworkPolicyTracker.getAvoidBadWifi();
 
         // Additionally:
         //
@@ -1490,7 +1514,7 @@
                     mContext.getResources().getBoolean(R.bool.config_apfDrop802_3Frames);
             apfConfig.ethTypeBlackList =
                     mContext.getResources().getIntArray(R.array.config_apfEthTypeBlackList);
-            mApfFilter = ApfFilter.maybeCreate(apfConfig, mInterfaceParams, mCallback);
+            mApfFilter = ApfFilter.maybeCreate(mContext, apfConfig, mInterfaceParams, mCallback);
             // TODO: investigate the effects of any multicast filtering racing/interfering with the
             // rest of this IP configuration startup.
             if (mApfFilter == null) {
@@ -1520,6 +1544,13 @@
                 return;
             }
 
+            if (mConfiguration.mUsingMultinetworkPolicyTracker) {
+                mMultinetworkPolicyTracker = new MultinetworkPolicyTracker(
+                        mContext, getHandler(),
+                        () -> { mLog.log("OBSERVED AvoidBadWifi changed"); });
+                mMultinetworkPolicyTracker.start();
+            }
+
             if (mConfiguration.mUsingIpReachabilityMonitor && !startIpReachabilityMonitor()) {
                 doImmediateProvisioningFailure(
                         IpManagerEvent.ERROR_STARTING_IPREACHABILITYMONITOR);
@@ -1537,6 +1568,11 @@
                 mIpReachabilityMonitor = null;
             }
 
+            if (mMultinetworkPolicyTracker != null) {
+                mMultinetworkPolicyTracker.shutdown();
+                mMultinetworkPolicyTracker = null;
+            }
+
             if (mDhcpClient != null) {
                 mDhcpClient.sendMessage(DhcpClient.CMD_STOP_DHCP);
                 mDhcpClient.doQuit();
diff --git a/android/net/ip/IpManager.java b/android/net/ip/IpManager.java
index 508a43d..2eb36a2 100644
--- a/android/net/ip/IpManager.java
+++ b/android/net/ip/IpManager.java
@@ -114,35 +114,6 @@
     public static class Callback extends IpClient.Callback {
     }
 
-    public static class WaitForProvisioningCallback extends Callback {
-        private LinkProperties mCallbackLinkProperties;
-
-        public LinkProperties waitForProvisioning() {
-            synchronized (this) {
-                try {
-                    wait();
-                } catch (InterruptedException e) {}
-                return mCallbackLinkProperties;
-            }
-        }
-
-        @Override
-        public void onProvisioningSuccess(LinkProperties newLp) {
-            synchronized (this) {
-                mCallbackLinkProperties = newLp;
-                notify();
-            }
-        }
-
-        @Override
-        public void onProvisioningFailure(LinkProperties newLp) {
-            synchronized (this) {
-                mCallbackLinkProperties = null;
-                notify();
-            }
-        }
-    }
-
     public IpManager(Context context, String ifName, Callback callback) {
         super(context, ifName, callback);
     }
diff --git a/android/net/metrics/ApfStats.java b/android/net/metrics/ApfStats.java
index 3b0dc7e..76a781d 100644
--- a/android/net/metrics/ApfStats.java
+++ b/android/net/metrics/ApfStats.java
@@ -20,7 +20,7 @@
 import android.os.Parcelable;
 
 /**
- * An event logged for an interface with APF capabilities when its IpManager state machine exits.
+ * An event logged for an interface with APF capabilities when its IpClient state machine exits.
  * {@hide}
  */
 public final class ApfStats implements Parcelable {
diff --git a/android/net/util/NetworkConstants.java b/android/net/util/NetworkConstants.java
index 984c9f8..53fd01f 100644
--- a/android/net/util/NetworkConstants.java
+++ b/android/net/util/NetworkConstants.java
@@ -136,6 +136,8 @@
      *     - https://tools.ietf.org/html/rfc4861
      */
     public static final int ICMPV6_HEADER_MIN_LEN = 4;
+    public static final int ICMPV6_ECHO_REQUEST_TYPE = 128;
+    public static final int ICMPV6_ECHO_REPLY_TYPE = 129;
     public static final int ICMPV6_ROUTER_SOLICITATION    = 133;
     public static final int ICMPV6_ROUTER_ADVERTISEMENT   = 134;
     public static final int ICMPV6_NEIGHBOR_SOLICITATION  = 135;
@@ -147,7 +149,6 @@
     public static final int ICMPV6_ND_OPTION_TLLA = 2;
     public static final int ICMPV6_ND_OPTION_MTU  = 5;
 
-    public static final int ICMPV6_ECHO_REQUEST_TYPE = 128;
 
     /**
      * UDP constants.
diff --git a/android/net/wifi/WifiConfiguration.java b/android/net/wifi/WifiConfiguration.java
index b77b1ad..f6c67c9 100644
--- a/android/net/wifi/WifiConfiguration.java
+++ b/android/net/wifi/WifiConfiguration.java
@@ -28,10 +28,12 @@
 import android.net.wifi.WifiInfo;
 import android.os.Parcel;
 import android.os.Parcelable;
+import android.os.SystemClock;
 import android.os.UserHandle;
 import android.text.TextUtils;
 import android.util.BackupUtils;
 import android.util.Log;
+import android.util.TimeUtils;
 
 import java.io.ByteArrayOutputStream;
 import java.io.DataInputStream;
@@ -611,37 +613,6 @@
 
     /**
      * @hide
-     * Last time the system tried to connect and failed.
-     */
-    public long lastConnectionFailure;
-
-    /**
-     * @hide
-     * Last time the system tried to roam and failed because of authentication failure or DHCP
-     * RENEW failure.
-     */
-    public long lastRoamingFailure;
-
-    /** @hide */
-    public static int ROAMING_FAILURE_IP_CONFIG = 1;
-    /** @hide */
-    public static int ROAMING_FAILURE_AUTH_FAILURE = 2;
-
-    /**
-     * @hide
-     * Initial amount of time this Wifi configuration gets blacklisted for network switching
-     * because of roaming failure
-     */
-    public long roamingFailureBlackListTimeMilli = 1000;
-
-    /**
-     * @hide
-     * Last roaming failure reason code
-     */
-    public int lastRoamingFailureReason;
-
-    /**
-     * @hide
      * Last time the system was disconnected to this configuration.
      */
     public long lastDisconnected;
@@ -1620,8 +1591,9 @@
         }
         if (mNetworkSelectionStatus.getConnectChoice() != null) {
             sbuf.append(" connect choice: ").append(mNetworkSelectionStatus.getConnectChoice());
-            sbuf.append(" connect choice set time: ").append(mNetworkSelectionStatus
-                    .getConnectChoiceTimestamp());
+            sbuf.append(" connect choice set time: ")
+                    .append(TimeUtils.logTimeOfDay(
+                            mNetworkSelectionStatus.getConnectChoiceTimestamp()));
         }
         sbuf.append(" hasEverConnected: ")
                 .append(mNetworkSelectionStatus.getHasEverConnected()).append("\n");
@@ -1724,7 +1696,7 @@
             sbuf.append(" networkSelectionBSSID="
                     + mNetworkSelectionStatus.getNetworkSelectionBSSID());
         }
-        long now_ms = System.currentTimeMillis();
+        long now_ms = SystemClock.elapsedRealtime();
         if (mNetworkSelectionStatus.getDisableTime() != NetworkSelectionStatus
                 .INVALID_NETWORK_SELECTION_DISABLE_TIMESTAMP) {
             sbuf.append('\n');
@@ -1746,35 +1718,9 @@
 
         if (this.lastConnected != 0) {
             sbuf.append('\n');
-            long diff = now_ms - this.lastConnected;
-            if (diff <= 0) {
-                sbuf.append("lastConnected since <incorrect>");
-            } else {
-                sbuf.append("lastConnected: ").append(Long.toString(diff / 1000)).append("sec ");
-            }
+            sbuf.append("lastConnected: ").append(TimeUtils.logTimeOfDay(this.lastConnected));
+            sbuf.append(" ");
         }
-        if (this.lastConnectionFailure != 0) {
-            sbuf.append('\n');
-            long diff = now_ms - this.lastConnectionFailure;
-            if (diff <= 0) {
-                sbuf.append("lastConnectionFailure since <incorrect> ");
-            } else {
-                sbuf.append("lastConnectionFailure: ").append(Long.toString(diff / 1000));
-                sbuf.append("sec ");
-            }
-        }
-        if (this.lastRoamingFailure != 0) {
-            sbuf.append('\n');
-            long diff = now_ms - this.lastRoamingFailure;
-            if (diff <= 0) {
-                sbuf.append("lastRoamingFailure since <incorrect> ");
-            } else {
-                sbuf.append("lastRoamingFailure: ").append(Long.toString(diff / 1000));
-                sbuf.append("sec ");
-            }
-        }
-        sbuf.append("roamingFailureBlackListTimeMilli: ").
-                append(Long.toString(this.roamingFailureBlackListTimeMilli));
         sbuf.append('\n');
         if (this.linkedConfigurations != null) {
             for (String key : this.linkedConfigurations.keySet()) {
@@ -2119,10 +2065,6 @@
 
             lastConnected = source.lastConnected;
             lastDisconnected = source.lastDisconnected;
-            lastConnectionFailure = source.lastConnectionFailure;
-            lastRoamingFailure = source.lastRoamingFailure;
-            lastRoamingFailureReason = source.lastRoamingFailureReason;
-            roamingFailureBlackListTimeMilli = source.roamingFailureBlackListTimeMilli;
             numScorerOverride = source.numScorerOverride;
             numScorerOverrideAndSwitchedNetwork = source.numScorerOverrideAndSwitchedNetwork;
             numAssociation = source.numAssociation;
@@ -2188,10 +2130,6 @@
         dest.writeInt(lastUpdateUid);
         dest.writeString(creatorName);
         dest.writeString(lastUpdateName);
-        dest.writeLong(lastConnectionFailure);
-        dest.writeLong(lastRoamingFailure);
-        dest.writeInt(lastRoamingFailureReason);
-        dest.writeLong(roamingFailureBlackListTimeMilli);
         dest.writeInt(numScorerOverride);
         dest.writeInt(numScorerOverrideAndSwitchedNetwork);
         dest.writeInt(numAssociation);
@@ -2257,10 +2195,6 @@
                 config.lastUpdateUid = in.readInt();
                 config.creatorName = in.readString();
                 config.lastUpdateName = in.readString();
-                config.lastConnectionFailure = in.readLong();
-                config.lastRoamingFailure = in.readLong();
-                config.lastRoamingFailureReason = in.readInt();
-                config.roamingFailureBlackListTimeMilli = in.readLong();
                 config.numScorerOverride = in.readInt();
                 config.numScorerOverrideAndSwitchedNetwork = in.readInt();
                 config.numAssociation = in.readInt();
diff --git a/android/net/wifi/WifiManager.java b/android/net/wifi/WifiManager.java
index 433285b..9c6c8a9 100644
--- a/android/net/wifi/WifiManager.java
+++ b/android/net/wifi/WifiManager.java
@@ -2141,7 +2141,8 @@
     }
 
     /**
-     * Sets the Wi-Fi AP Configuration.
+     * Sets the Wi-Fi AP Configuration.  The AP configuration must either be open or
+     * WPA2 PSK networks.
      * @return {@code true} if the operation succeeded, {@code false} otherwise
      *
      * @hide
@@ -2150,8 +2151,7 @@
     @RequiresPermission(android.Manifest.permission.CHANGE_WIFI_STATE)
     public boolean setWifiApConfiguration(WifiConfiguration wifiConfig) {
         try {
-            mService.setWifiApConfiguration(wifiConfig, mContext.getOpPackageName());
-            return true;
+            return mService.setWifiApConfiguration(wifiConfig, mContext.getOpPackageName());
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
diff --git a/android/os/BatteryManager.java b/android/os/BatteryManager.java
index 6363161..954071a 100644
--- a/android/os/BatteryManager.java
+++ b/android/os/BatteryManager.java
@@ -353,4 +353,20 @@
     public static boolean isPlugWired(int plugType) {
         return plugType == BATTERY_PLUGGED_USB || plugType == BATTERY_PLUGGED_AC;
     }
+
+    /**
+     * Compute an approximation for how much time (in milliseconds) remains until the battery is
+     * fully charged. Returns -1 if no time can be computed: either there is not enough current
+     * data to make a decision or the battery is currently discharging.
+     *
+     * @return how much time is left, in milliseconds, until the battery is fully charged or -1 if
+     *         the computation fails
+     */
+    public long computeChargeTimeRemaining() {
+        try {
+            return mBatteryStats.computeChargeTimeRemaining();
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
 }
diff --git a/android/os/BatteryStats.java b/android/os/BatteryStats.java
index 6ebb102..1d232bf 100644
--- a/android/os/BatteryStats.java
+++ b/android/os/BatteryStats.java
@@ -3814,6 +3814,9 @@
                 final BatterySipper bs = sippers.get(i);
                 String label;
                 switch (bs.drainType) {
+                    case AMBIENT_DISPLAY:
+                        label = "ambi";
+                        break;
                     case IDLE:
                         label="idle";
                         break;
@@ -4975,6 +4978,9 @@
                 final BatterySipper bs = sippers.get(i);
                 pw.print(prefix);
                 switch (bs.drainType) {
+                    case AMBIENT_DISPLAY:
+                        pw.print("    Ambient display: ");
+                        break;
                     case IDLE:
                         pw.print("    Idle: ");
                         break;
@@ -7777,6 +7783,9 @@
                 int n = SystemProto.PowerUseItem.UNKNOWN_SIPPER;
                 int uid = 0;
                 switch (bs.drainType) {
+                    case AMBIENT_DISPLAY:
+                        n = SystemProto.PowerUseItem.AMBIENT_DISPLAY;
+                        break;
                     case IDLE:
                         n = SystemProto.PowerUseItem.IDLE;
                         break;
diff --git a/android/os/Build.java b/android/os/Build.java
index 8378a82..7162b8a 100644
--- a/android/os/Build.java
+++ b/android/os/Build.java
@@ -907,6 +907,8 @@
          * <li>{@link android.app.Service#startForeground Service.startForeground} requires
          * that apps hold the permission
          * {@link android.Manifest.permission#FOREGROUND_SERVICE}.</li>
+         * <li>{@link android.widget.LinearLayout} will always remeasure weighted children,
+         * even if there is no excess space.</li>
          * </ul>
          */
         public static final int P = CUR_DEVELOPMENT; // STOPSHIP Replace with the real version.
diff --git a/android/os/DeviceIdleManager.java b/android/os/DeviceIdleManager.java
new file mode 100644
index 0000000..9039f92
--- /dev/null
+++ b/android/os/DeviceIdleManager.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.os;
+
+import android.annotation.NonNull;
+import android.annotation.SystemService;
+import android.annotation.TestApi;
+import android.content.Context;
+
+/**
+ * Access to the service that keeps track of device idleness and drives low power mode based on
+ * that.
+ *
+ * @hide
+ */
+@TestApi
+@SystemService(Context.DEVICE_IDLE_CONTROLLER)
+public class DeviceIdleManager {
+    private final Context mContext;
+    private final IDeviceIdleController mService;
+
+    /**
+     * @hide
+     */
+    public DeviceIdleManager(@NonNull Context context, @NonNull IDeviceIdleController service) {
+        mContext = context;
+        mService = service;
+    }
+
+    /**
+     * @return package names the system has white-listed to opt out of power save restrictions,
+     * except for device idle mode.
+     */
+    public @NonNull String[] getSystemPowerWhitelistExceptIdle() {
+        try {
+            return mService.getSystemPowerWhitelistExceptIdle();
+        } catch (RemoteException e) {
+            e.rethrowFromSystemServer();
+            return new String[0];
+        }
+    }
+
+    /**
+     * @return package names the system has white-listed to opt out of power save restrictions for
+     * all modes.
+     */
+    public @NonNull String[] getSystemPowerWhitelist() {
+        try {
+            return mService.getSystemPowerWhitelist();
+        } catch (RemoteException e) {
+            e.rethrowFromSystemServer();
+            return new String[0];
+        }
+    }
+}
diff --git a/android/os/Environment.java b/android/os/Environment.java
index 03203d0..213260f 100644
--- a/android/os/Environment.java
+++ b/android/os/Environment.java
@@ -16,6 +16,7 @@
 
 package android.os;
 
+import android.annotation.TestApi;
 import android.app.admin.DevicePolicyManager;
 import android.content.Context;
 import android.os.storage.StorageManager;
@@ -1033,6 +1034,7 @@
      *
      * @hide
      */
+    @TestApi
     public static File buildPath(File base, String... segments) {
         File cur = base;
         for (String segment : segments) {
diff --git a/android/os/MessageQueue.java b/android/os/MessageQueue.java
index 96e7a59..b1c33c2 100644
--- a/android/os/MessageQueue.java
+++ b/android/os/MessageQueue.java
@@ -254,6 +254,7 @@
         } else if (record != null) {
             record.mEvents = 0;
             mFileDescriptorRecords.removeAt(index);
+            nativeSetFileDescriptorEvents(mPtr, fdNum, 0);
         }
     }
 
diff --git a/android/os/Parcel.java b/android/os/Parcel.java
index e3c4870..5142928 100644
--- a/android/os/Parcel.java
+++ b/android/os/Parcel.java
@@ -1857,26 +1857,7 @@
         int code = readExceptionCode();
         if (code != 0) {
             String msg = readString();
-            String remoteStackTrace = null;
-            final int remoteStackPayloadSize = readInt();
-            if (remoteStackPayloadSize > 0) {
-                remoteStackTrace = readString();
-            }
-            Exception e = createException(code, msg);
-            // Attach remote stack trace if availalble
-            if (remoteStackTrace != null) {
-                RemoteException cause = new RemoteException(
-                        "Remote stack trace:\n" + remoteStackTrace, null, false, false);
-                try {
-                    Throwable rootCause = ExceptionUtils.getRootCause(e);
-                    if (rootCause != null) {
-                        rootCause.initCause(cause);
-                    }
-                } catch (RuntimeException ex) {
-                    Log.e(TAG, "Cannot set cause " + cause + " for " + e, ex);
-                }
-            }
-            SneakyThrow.sneakyThrow(e);
+            readException(code, msg);
         }
     }
 
@@ -1921,7 +1902,26 @@
      * @param msg The exception message.
      */
     public final void readException(int code, String msg) {
-        SneakyThrow.sneakyThrow(createException(code, msg));
+        String remoteStackTrace = null;
+        final int remoteStackPayloadSize = readInt();
+        if (remoteStackPayloadSize > 0) {
+            remoteStackTrace = readString();
+        }
+        Exception e = createException(code, msg);
+        // Attach remote stack trace if availalble
+        if (remoteStackTrace != null) {
+            RemoteException cause = new RemoteException(
+                    "Remote stack trace:\n" + remoteStackTrace, null, false, false);
+            try {
+                Throwable rootCause = ExceptionUtils.getRootCause(e);
+                if (rootCause != null) {
+                    rootCause.initCause(cause);
+                }
+            } catch (RuntimeException ex) {
+                Log.e(TAG, "Cannot set cause " + cause + " for " + e, ex);
+            }
+        }
+        SneakyThrow.sneakyThrow(e);
     }
 
     /**
diff --git a/android/os/ServiceManager.java b/android/os/ServiceManager.java
index 34c7845..165276d 100644
--- a/android/os/ServiceManager.java
+++ b/android/os/ServiceManager.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2009 The Android Open Source Project
+ * Copyright (C) 2007 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -16,9 +16,98 @@
 
 package android.os;
 
+import android.util.Log;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.os.BinderInternal;
+import com.android.internal.util.StatLogger;
+
+import java.util.HashMap;
 import java.util.Map;
 
+/** @hide */
 public final class ServiceManager {
+    private static final String TAG = "ServiceManager";
+    private static final Object sLock = new Object();
+
+    private static IServiceManager sServiceManager;
+
+    /**
+     * Cache for the "well known" services, such as WM and AM.
+     */
+    private static HashMap<String, IBinder> sCache = new HashMap<String, IBinder>();
+
+    /**
+     * We do the "slow log" at most once every this interval.
+     */
+    private static final int SLOW_LOG_INTERVAL_MS = 5000;
+
+    /**
+     * We do the "stats log" at most once every this interval.
+     */
+    private static final int STATS_LOG_INTERVAL_MS = 5000;
+
+    /**
+     * Threshold in uS for a "slow" call, used on core UIDs. We use a more relax value to
+     * avoid logspam.
+     */
+    private static final long GET_SERVICE_SLOW_THRESHOLD_US_CORE =
+            SystemProperties.getInt("debug.servicemanager.slow_call_core_ms", 10) * 1000;
+
+    /**
+     * Threshold in uS for a "slow" call, used on non-core UIDs. We use a more relax value to
+     * avoid logspam.
+     */
+    private static final long GET_SERVICE_SLOW_THRESHOLD_US_NON_CORE =
+            SystemProperties.getInt("debug.servicemanager.slow_call_ms", 50) * 1000;
+
+    /**
+     * We log stats logging ever this many getService() calls.
+     */
+    private static final int GET_SERVICE_LOG_EVERY_CALLS_CORE =
+            SystemProperties.getInt("debug.servicemanager.log_calls_core", 100);
+
+    /**
+     * We log stats logging ever this many getService() calls.
+     */
+    private static final int GET_SERVICE_LOG_EVERY_CALLS_NON_CORE =
+            SystemProperties.getInt("debug.servicemanager.log_calls", 200);
+
+    @GuardedBy("sLock")
+    private static int sGetServiceAccumulatedUs;
+
+    @GuardedBy("sLock")
+    private static int sGetServiceAccumulatedCallCount;
+
+    @GuardedBy("sLock")
+    private static long sLastStatsLogUptime;
+
+    @GuardedBy("sLock")
+    private static long sLastSlowLogUptime;
+
+    @GuardedBy("sLock")
+    private static long sLastSlowLogActualTime;
+
+    interface Stats {
+        int GET_SERVICE = 0;
+
+        int COUNT = GET_SERVICE + 1;
+    }
+
+    public static final StatLogger sStatLogger = new StatLogger(new String[] {
+            "getService()",
+    });
+
+    private static IServiceManager getIServiceManager() {
+        if (sServiceManager != null) {
+            return sServiceManager;
+        }
+
+        // Find the service manager
+        sServiceManager = ServiceManagerNative
+                .asInterface(Binder.allowBlocking(BinderInternal.getContextObject()));
+        return sServiceManager;
+    }
 
     /**
      * Returns a reference to a service with the given name.
@@ -27,14 +116,32 @@
      * @return a reference to the service, or <code>null</code> if the service doesn't exist
      */
     public static IBinder getService(String name) {
+        try {
+            IBinder service = sCache.get(name);
+            if (service != null) {
+                return service;
+            } else {
+                return Binder.allowBlocking(rawGetService(name));
+            }
+        } catch (RemoteException e) {
+            Log.e(TAG, "error in getService", e);
+        }
         return null;
     }
 
     /**
-     * Is not supposed to return null, but that is fine for layoutlib.
+     * Returns a reference to a service with the given name, or throws
+     * {@link NullPointerException} if none is found.
+     *
+     * @hide
      */
     public static IBinder getServiceOrThrow(String name) throws ServiceNotFoundException {
-        throw new ServiceNotFoundException(name);
+        final IBinder binder = getService(name);
+        if (binder != null) {
+            return binder;
+        } else {
+            throw new ServiceNotFoundException(name);
+        }
     }
 
     /**
@@ -45,7 +152,39 @@
      * @param service the service object
      */
     public static void addService(String name, IBinder service) {
-        // pass
+        addService(name, service, false, IServiceManager.DUMP_FLAG_PRIORITY_DEFAULT);
+    }
+
+    /**
+     * Place a new @a service called @a name into the service
+     * manager.
+     *
+     * @param name the name of the new service
+     * @param service the service object
+     * @param allowIsolated set to true to allow isolated sandboxed processes
+     * to access this service
+     */
+    public static void addService(String name, IBinder service, boolean allowIsolated) {
+        addService(name, service, allowIsolated, IServiceManager.DUMP_FLAG_PRIORITY_DEFAULT);
+    }
+
+    /**
+     * Place a new @a service called @a name into the service
+     * manager.
+     *
+     * @param name the name of the new service
+     * @param service the service object
+     * @param allowIsolated set to true to allow isolated sandboxed processes
+     * @param dumpPriority supported dump priority levels as a bitmask
+     * to access this service
+     */
+    public static void addService(String name, IBinder service, boolean allowIsolated,
+            int dumpPriority) {
+        try {
+            getIServiceManager().addService(name, service, allowIsolated, dumpPriority);
+        } catch (RemoteException e) {
+            Log.e(TAG, "error in addService", e);
+        }
     }
 
     /**
@@ -53,7 +192,17 @@
      * service manager.  Non-blocking.
      */
     public static IBinder checkService(String name) {
-        return null;
+        try {
+            IBinder service = sCache.get(name);
+            if (service != null) {
+                return service;
+            } else {
+                return Binder.allowBlocking(getIServiceManager().checkService(name));
+            }
+        } catch (RemoteException e) {
+            Log.e(TAG, "error in checkService", e);
+            return null;
+        }
     }
 
     /**
@@ -62,9 +211,12 @@
      * case of an exception
      */
     public static String[] listServices() {
-        // actual implementation returns null sometimes, so it's ok
-        // to return null instead of an empty list.
-        return null;
+        try {
+            return getIServiceManager().listServices(IServiceManager.DUMP_FLAG_PRIORITY_ALL);
+        } catch (RemoteException e) {
+            Log.e(TAG, "error in listServices", e);
+            return null;
+        }
     }
 
     /**
@@ -76,7 +228,10 @@
      * @hide
      */
     public static void initServiceCache(Map<String, IBinder> cache) {
-        // pass
+        if (sCache.size() != 0) {
+            throw new IllegalStateException("setServiceCache may only be called once");
+        }
+        sCache.putAll(cache);
     }
 
     /**
@@ -87,9 +242,63 @@
      * @hide
      */
     public static class ServiceNotFoundException extends Exception {
-        // identical to the original implementation
         public ServiceNotFoundException(String name) {
             super("No service published for: " + name);
         }
     }
+
+    private static IBinder rawGetService(String name) throws RemoteException {
+        final long start = sStatLogger.getTime();
+
+        final IBinder binder = getIServiceManager().getService(name);
+
+        final int time = (int) sStatLogger.logDurationStat(Stats.GET_SERVICE, start);
+
+        final int myUid = Process.myUid();
+        final boolean isCore = UserHandle.isCore(myUid);
+
+        final long slowThreshold = isCore
+                ? GET_SERVICE_SLOW_THRESHOLD_US_CORE
+                : GET_SERVICE_SLOW_THRESHOLD_US_NON_CORE;
+
+        synchronized (sLock) {
+            sGetServiceAccumulatedUs += time;
+            sGetServiceAccumulatedCallCount++;
+
+            final long nowUptime = SystemClock.uptimeMillis();
+
+            // Was a slow call?
+            if (time >= slowThreshold) {
+                // We do a slow log:
+                // - At most once in every SLOW_LOG_INTERVAL_MS
+                // - OR it was slower than the previously logged slow call.
+                if ((nowUptime > (sLastSlowLogUptime + SLOW_LOG_INTERVAL_MS))
+                        || (sLastSlowLogActualTime < time)) {
+                    EventLogTags.writeServiceManagerSlow(time / 1000, name);
+
+                    sLastSlowLogUptime = nowUptime;
+                    sLastSlowLogActualTime = time;
+                }
+            }
+
+            // Every GET_SERVICE_LOG_EVERY_CALLS calls, log the total time spent in getService().
+
+            final int logInterval = isCore
+                    ? GET_SERVICE_LOG_EVERY_CALLS_CORE
+                    : GET_SERVICE_LOG_EVERY_CALLS_NON_CORE;
+
+            if ((sGetServiceAccumulatedCallCount >= logInterval)
+                    && (nowUptime >= (sLastStatsLogUptime + STATS_LOG_INTERVAL_MS))) {
+
+                EventLogTags.writeServiceManagerStats(
+                        sGetServiceAccumulatedCallCount, // Total # of getService() calls.
+                        sGetServiceAccumulatedUs / 1000, // Total time spent in getService() calls.
+                        (int) (nowUptime - sLastStatsLogUptime)); // Uptime duration since last log.
+                sGetServiceAccumulatedCallCount = 0;
+                sGetServiceAccumulatedUs = 0;
+                sLastStatsLogUptime = nowUptime;
+            }
+        }
+        return binder;
+    }
 }
diff --git a/android/os/StrictMode.java b/android/os/StrictMode.java
index a93e25a..59380fd 100644
--- a/android/os/StrictMode.java
+++ b/android/os/StrictMode.java
@@ -39,6 +39,7 @@
 import android.os.strictmode.IntentReceiverLeakedViolation;
 import android.os.strictmode.LeakedClosableViolation;
 import android.os.strictmode.NetworkViolation;
+import android.os.strictmode.NonSdkApiUsedViolation;
 import android.os.strictmode.ResourceMismatchViolation;
 import android.os.strictmode.ServiceConnectionLeakedViolation;
 import android.os.strictmode.SqliteObjectLeakedViolation;
@@ -76,6 +77,7 @@
 import java.util.concurrent.Executor;
 import java.util.concurrent.RejectedExecutionException;
 import java.util.concurrent.atomic.AtomicInteger;
+import java.util.function.Consumer;
 
 /**
  * StrictMode is a developer tool which detects things you might be doing by accident and brings
@@ -262,6 +264,9 @@
     /** @hide */
     @TestApi public static final int DETECT_VM_UNTAGGED_SOCKET = 0x80 << 24; // for VmPolicy
 
+    /** @hide */
+    @TestApi public static final int DETECT_VM_NON_SDK_API_USAGE = 0x40 << 24; // for VmPolicy
+
     private static final int ALL_VM_DETECT_BITS =
             DETECT_VM_CURSOR_LEAKS
                     | DETECT_VM_CLOSABLE_LEAKS
@@ -271,7 +276,9 @@
                     | DETECT_VM_FILE_URI_EXPOSURE
                     | DETECT_VM_CLEARTEXT_NETWORK
                     | DETECT_VM_CONTENT_URI_WITHOUT_PERMISSION
-                    | DETECT_VM_UNTAGGED_SOCKET;
+                    | DETECT_VM_UNTAGGED_SOCKET
+                    | DETECT_VM_NON_SDK_API_USAGE;
+
 
     // Byte 3: Penalty
 
@@ -413,6 +420,13 @@
      */
     private static final AtomicInteger sDropboxCallsInFlight = new AtomicInteger(0);
 
+    /**
+     * Callback supplied to dalvik / libcore to get informed of usages of java API that are not
+     * a part of the public SDK.
+     */
+    private static final Consumer<String> sNonSdkApiUsageConsumer =
+            message -> onVmPolicyViolation(new NonSdkApiUsedViolation(message));
+
     private StrictMode() {}
 
     /**
@@ -796,6 +810,23 @@
             }
 
             /**
+             * Detect reflective usage of APIs that are not part of the public Android SDK.
+             */
+            public Builder detectNonSdkApiUsage() {
+                return enable(DETECT_VM_NON_SDK_API_USAGE);
+            }
+
+            /**
+             * Permit reflective usage of APIs that are not part of the public Android SDK. Note
+             * that this <b>only</b> affects {@code StrictMode}, the underlying runtime may
+             * continue to restrict or warn on access to methods that are not part of the
+             * public SDK.
+             */
+            public Builder permitNonSdkApiUsage() {
+                return disable(DETECT_VM_NON_SDK_API_USAGE);
+            }
+
+            /**
              * Detect everything that's potentially suspect.
              *
              * <p>In the Honeycomb release this includes leaks of SQLite cursors, Activities, and
@@ -826,6 +857,8 @@
                     detectContentUriWithoutPermission();
                     detectUntaggedSockets();
                 }
+
+                // TODO: Decide whether to detect non SDK API usage beyond a certain API level.
                 return this;
             }
 
@@ -1848,6 +1881,13 @@
             } else if (networkPolicy != NETWORK_POLICY_ACCEPT) {
                 Log.w(TAG, "Dropping requested network policy due to missing service!");
             }
+
+
+            if ((sVmPolicy.mask & DETECT_VM_NON_SDK_API_USAGE) != 0) {
+                VMRuntime.setNonSdkApiUsageConsumer(sNonSdkApiUsageConsumer);
+            } else {
+                VMRuntime.setNonSdkApiUsageConsumer(null);
+            }
         }
     }
 
@@ -2576,6 +2616,8 @@
                 return DETECT_VM_CONTENT_URI_WITHOUT_PERMISSION;
             } else if (mViolation instanceof UntaggedSocketViolation) {
                 return DETECT_VM_UNTAGGED_SOCKET;
+            } else if (mViolation instanceof NonSdkApiUsedViolation) {
+                return DETECT_VM_NON_SDK_API_USAGE;
             }
             throw new IllegalStateException("missing violation bit");
         }
diff --git a/android/os/SystemProperties.java b/android/os/SystemProperties.java
index 8eb39c0..7d3ba6a 100644
--- a/android/os/SystemProperties.java
+++ b/android/os/SystemProperties.java
@@ -19,6 +19,7 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.SystemApi;
+import android.annotation.TestApi;
 import android.util.Log;
 import android.util.MutableInt;
 
@@ -35,6 +36,7 @@
  * {@hide}
  */
 @SystemApi
+@TestApi
 public class SystemProperties {
     private static final String TAG = "SystemProperties";
     private static final boolean TRACK_KEY_ACCESS = false;
@@ -110,6 +112,7 @@
      */
     @NonNull
     @SystemApi
+    @TestApi
     public static String get(@NonNull String key, @Nullable String def) {
         if (TRACK_KEY_ACCESS) onKeyAccess(key);
         return native_get(key, def);
diff --git a/android/os/UserHandle.java b/android/os/UserHandle.java
index 094f004..4d4f31d 100644
--- a/android/os/UserHandle.java
+++ b/android/os/UserHandle.java
@@ -82,6 +82,7 @@
     public static final int USER_SERIAL_SYSTEM = 0;
 
     /** @hide A user handle to indicate the "system" user of the device */
+    @TestApi
     public static final UserHandle SYSTEM = new UserHandle(USER_SYSTEM);
 
     /**
diff --git a/android/os/UserManager.java b/android/os/UserManager.java
index a9eb360..9b20ed2 100644
--- a/android/os/UserManager.java
+++ b/android/os/UserManager.java
@@ -1149,6 +1149,7 @@
      * primary user are two separate users. Previously system user and primary user are combined as
      * a single owner user.  see @link {android.os.UserHandle#USER_OWNER}
      */
+    @TestApi
     public static boolean isSplitSystemUser() {
         return RoSystemProperties.FW_SYSTEM_USER_SPLIT;
     }
diff --git a/android/os/WorkSource.java b/android/os/WorkSource.java
index 17d83db..3270719 100644
--- a/android/os/WorkSource.java
+++ b/android/os/WorkSource.java
@@ -924,13 +924,17 @@
         /** @hide */
         @VisibleForTesting
         public int[] getUids() {
-            return mUids;
+            int[] uids = new int[mSize];
+            System.arraycopy(mUids, 0, uids, 0, mSize);
+            return uids;
         }
 
         /** @hide */
         @VisibleForTesting
         public String[] getTags() {
-            return mTags;
+            String[] tags = new String[mSize];
+            System.arraycopy(mTags, 0, tags, 0, mSize);
+            return tags;
         }
 
         /** @hide */
diff --git a/android/os/ZygoteProcess.java b/android/os/ZygoteProcess.java
index b9dd376..673da50 100644
--- a/android/os/ZygoteProcess.java
+++ b/android/os/ZygoteProcess.java
@@ -166,6 +166,11 @@
     private List<String> mApiBlacklistExemptions = Collections.emptyList();
 
     /**
+     * Proportion of hidden API accesses that should be logged to the event log; 0 - 0x10000.
+     */
+    private int mHiddenApiAccessLogSampleRate;
+
+    /**
      * The state of the connection to the primary zygote.
      */
     private ZygoteState primaryZygoteState;
@@ -467,7 +472,8 @@
      * <p>The list of exemptions will take affect for all new processes forked from the zygote after
      * this call.
      *
-     * @param exemptions List of hidden API exemption prefixes.
+     * @param exemptions List of hidden API exemption prefixes. Any matching members are treated as
+     *        whitelisted/public APIs (i.e. allowed, no logging of usage).
      */
     public void setApiBlacklistExemptions(List<String> exemptions) {
         synchronized (mLock) {
@@ -477,6 +483,21 @@
         }
     }
 
+    /**
+     * Set the precentage of detected hidden API accesses that are logged to the event log.
+     *
+     * <p>This rate will take affect for all new processes forked from the zygote after this call.
+     *
+     * @param rate An integer between 0 and 0x10000 inclusive. 0 means no event logging.
+     */
+    public void setHiddenApiAccessLogSampleRate(int rate) {
+        synchronized (mLock) {
+            mHiddenApiAccessLogSampleRate = rate;
+            maybeSetHiddenApiAccessLogSampleRate(primaryZygoteState);
+            maybeSetHiddenApiAccessLogSampleRate(secondaryZygoteState);
+        }
+    }
+
     @GuardedBy("mLock")
     private void maybeSetApiBlacklistExemptions(ZygoteState state, boolean sendIfEmpty) {
         if (state == null || state.isClosed()) {
@@ -504,6 +525,29 @@
         }
     }
 
+    private void maybeSetHiddenApiAccessLogSampleRate(ZygoteState state) {
+        if (state == null || state.isClosed()) {
+            return;
+        }
+        if (mHiddenApiAccessLogSampleRate == -1) {
+            return;
+        }
+        try {
+            state.writer.write(Integer.toString(1));
+            state.writer.newLine();
+            state.writer.write("--hidden-api-log-sampling-rate="
+                    + Integer.toString(mHiddenApiAccessLogSampleRate));
+            state.writer.newLine();
+            state.writer.flush();
+            int status = state.inputStream.readInt();
+            if (status != 0) {
+                Slog.e(LOG_TAG, "Failed to set hidden API log sampling rate; status " + status);
+            }
+        } catch (IOException ioe) {
+            Slog.e(LOG_TAG, "Failed to set hidden API log sampling rate", ioe);
+        }
+    }
+
     /**
      * Tries to open socket to Zygote process if not already open. If
      * already open, does nothing.  May block and retry.  Requires that mLock be held.
@@ -519,6 +563,7 @@
                 throw new ZygoteStartFailedEx("Error connecting to primary zygote", ioe);
             }
             maybeSetApiBlacklistExemptions(primaryZygoteState, false);
+            maybeSetHiddenApiAccessLogSampleRate(primaryZygoteState);
         }
         if (primaryZygoteState.matches(abi)) {
             return primaryZygoteState;
@@ -532,6 +577,7 @@
                 throw new ZygoteStartFailedEx("Error connecting to secondary zygote", ioe);
             }
             maybeSetApiBlacklistExemptions(secondaryZygoteState, false);
+            maybeSetHiddenApiAccessLogSampleRate(secondaryZygoteState);
         }
 
         if (secondaryZygoteState.matches(abi)) {
diff --git a/android/os/storage/DiskInfo.java b/android/os/storage/DiskInfo.java
index 9114107..d493cce 100644
--- a/android/os/storage/DiskInfo.java
+++ b/android/os/storage/DiskInfo.java
@@ -17,6 +17,7 @@
 package android.os.storage;
 
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.content.res.Resources;
 import android.os.Parcel;
 import android.os.Parcelable;
@@ -93,7 +94,7 @@
         return true;
     }
 
-    public String getDescription() {
+    public @Nullable String getDescription() {
         final Resources res = Resources.getSystem();
         if ((flags & FLAG_SD) != 0) {
             if (isInteresting(label)) {
@@ -112,6 +113,17 @@
         }
     }
 
+    public @Nullable String getShortDescription() {
+        final Resources res = Resources.getSystem();
+        if (isSd()) {
+            return res.getString(com.android.internal.R.string.storage_sd_card);
+        } else if (isUsb()) {
+            return res.getString(com.android.internal.R.string.storage_usb_drive);
+        } else {
+            return null;
+        }
+    }
+
     public boolean isAdoptable() {
         return (flags & FLAG_ADOPTABLE) != 0;
     }
diff --git a/android/security/keystore/SessionExpiredException.java b/android/os/strictmode/NonSdkApiUsedViolation.java
similarity index 69%
rename from android/security/keystore/SessionExpiredException.java
rename to android/os/strictmode/NonSdkApiUsedViolation.java
index 7c8d5e4..2f0cb50 100644
--- a/android/security/keystore/SessionExpiredException.java
+++ b/android/os/strictmode/NonSdkApiUsedViolation.java
@@ -14,14 +14,15 @@
  * limitations under the License.
  */
 
-package android.security.keystore;
+package android.os.strictmode;
 
 /**
- * @deprecated Use {@link android.security.keystore.recovery.SessionExpiredException}.
- * @hide
+ * Subclass of {@code Violation} that is used when a process accesses
+ * a non SDK API.
  */
-public class SessionExpiredException extends RecoveryControllerException {
-    public SessionExpiredException(String msg) {
-        super(msg);
+public final class NonSdkApiUsedViolation extends Violation {
+    /** @hide */
+    public NonSdkApiUsedViolation(String message) {
+        super(message);
     }
 }
diff --git a/android/provider/Settings.java b/android/provider/Settings.java
index 68fc6c1..5b7adf0 100644
--- a/android/provider/Settings.java
+++ b/android/provider/Settings.java
@@ -1179,6 +1179,23 @@
     public static final String ACTION_ZEN_MODE_SETTINGS = "android.settings.ZEN_MODE_SETTINGS";
 
     /**
+     * Activity Action: Show Zen Mode visual effects configuration settings.
+     *
+     * @hide
+     */
+    @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+    public static final String ZEN_MODE_BLOCKED_EFFECTS_SETTINGS =
+            "android.settings.ZEN_MODE_BLOCKED_EFFECTS_SETTINGS";
+
+    /**
+     * Activity Action: Show Zen Mode onboarding activity.
+     *
+     * @hide
+     */
+    @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+    public static final String ZEN_MODE_ONBOARDING = "android.settings.ZEN_MODE_ONBOARDING";
+
+    /**
      * Activity Action: Show Zen Mode (aka Do Not Disturb) priority configuration settings.
      */
     @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
@@ -3113,6 +3130,9 @@
          */
         public static final String DISPLAY_COLOR_MODE = "display_color_mode";
 
+        private static final Validator DISPLAY_COLOR_MODE_VALIDATOR =
+                new SettingsValidators.InclusiveIntegerRangeValidator(0, 2);
+
         /**
          * The amount of time in milliseconds before the device goes to sleep or begins
          * to dream after a period of inactivity.  This value is also known as the
@@ -3133,9 +3153,6 @@
          */
         public static final String SCREEN_BRIGHTNESS = "screen_brightness";
 
-        private static final Validator SCREEN_BRIGHTNESS_VALIDATOR =
-                new SettingsValidators.InclusiveIntegerRangeValidator(0, 255);
-
         /**
          * The screen backlight brightness between 0 and 255.
          * @hide
@@ -3753,17 +3770,6 @@
                 new SettingsValidators.InclusiveIntegerRangeValidator(0, 3);
 
         /**
-         * User-selected RTT mode. When on, outgoing and incoming calls will be answered as RTT
-         * calls when supported by the device and carrier. Boolean value.
-         * 0 = OFF
-         * 1 = ON
-         */
-        public static final String RTT_CALLING_MODE = "rtt_calling_mode";
-
-        /** @hide */
-        public static final Validator RTT_CALLING_MODE_VALIDATOR = BOOLEAN_VALIDATOR;
-
-        /**
          * Whether the sounds effects (key clicks, lid open ...) are enabled. The value is
          * boolean (1 or 0).
          */
@@ -4071,7 +4077,6 @@
             FONT_SCALE,
             DIM_SCREEN,
             SCREEN_OFF_TIMEOUT,
-            SCREEN_BRIGHTNESS,
             SCREEN_BRIGHTNESS_MODE,
             SCREEN_AUTO_BRIGHTNESS_ADJ,
             SCREEN_BRIGHTNESS_FOR_VR,
@@ -4088,7 +4093,6 @@
             DTMF_TONE_WHEN_DIALING,
             DTMF_TONE_TYPE_WHEN_DIALING,
             HEARING_AID,
-            RTT_CALLING_MODE,
             TTY_MODE,
             MASTER_MONO,
             SOUND_EFFECTS_ENABLED,
@@ -4108,6 +4112,7 @@
             SHOW_BATTERY_PERCENT,
             NOTIFICATION_VIBRATION_INTENSITY,
             HAPTIC_FEEDBACK_INTENSITY,
+            DISPLAY_COLOR_MODE
         };
 
         /**
@@ -4220,6 +4225,7 @@
             PRIVATE_SETTINGS.add(LOCK_TO_APP_ENABLED);
             PRIVATE_SETTINGS.add(EGG_MODE);
             PRIVATE_SETTINGS.add(SHOW_BATTERY_PERCENT);
+            PRIVATE_SETTINGS.add(DISPLAY_COLOR_MODE);
         }
 
         /**
@@ -4241,8 +4247,8 @@
             VALIDATORS.put(NEXT_ALARM_FORMATTED, NEXT_ALARM_FORMATTED_VALIDATOR);
             VALIDATORS.put(FONT_SCALE, FONT_SCALE_VALIDATOR);
             VALIDATORS.put(DIM_SCREEN, DIM_SCREEN_VALIDATOR);
+            VALIDATORS.put(DISPLAY_COLOR_MODE, DISPLAY_COLOR_MODE_VALIDATOR);
             VALIDATORS.put(SCREEN_OFF_TIMEOUT, SCREEN_OFF_TIMEOUT_VALIDATOR);
-            VALIDATORS.put(SCREEN_BRIGHTNESS, SCREEN_BRIGHTNESS_VALIDATOR);
             VALIDATORS.put(SCREEN_BRIGHTNESS_FOR_VR, SCREEN_BRIGHTNESS_FOR_VR_VALIDATOR);
             VALIDATORS.put(SCREEN_BRIGHTNESS_MODE, SCREEN_BRIGHTNESS_MODE_VALIDATOR);
             VALIDATORS.put(MODE_RINGER_STREAMS_AFFECTED, MODE_RINGER_STREAMS_AFFECTED_VALIDATOR);
@@ -4287,7 +4293,6 @@
             VALIDATORS.put(DTMF_TONE_TYPE_WHEN_DIALING, DTMF_TONE_TYPE_WHEN_DIALING_VALIDATOR);
             VALIDATORS.put(HEARING_AID, HEARING_AID_VALIDATOR);
             VALIDATORS.put(TTY_MODE, TTY_MODE_VALIDATOR);
-            VALIDATORS.put(RTT_CALLING_MODE, RTT_CALLING_MODE_VALIDATOR);
             VALIDATORS.put(NOTIFICATION_LIGHT_PULSE, NOTIFICATION_LIGHT_PULSE_VALIDATOR);
             VALIDATORS.put(POINTER_LOCATION, POINTER_LOCATION_VALIDATOR);
             VALIDATORS.put(SHOW_TOUCHES, SHOW_TOUCHES_VALIDATOR);
@@ -6660,6 +6665,17 @@
         private static final Validator TTY_MODE_ENABLED_VALIDATOR = BOOLEAN_VALIDATOR;
 
         /**
+         * User-selected RTT mode. When on, outgoing and incoming calls will be answered as RTT
+         * calls when supported by the device and carrier. Boolean value.
+         * 0 = OFF
+         * 1 = ON
+         */
+        public static final String RTT_CALLING_MODE = "rtt_calling_mode";
+
+        private static final Validator RTT_CALLING_MODE_VALIDATOR = BOOLEAN_VALIDATOR;
+
+        /**
+        /**
          * Controls whether settings backup is enabled.
          * Type: int ( 0 = disabled, 1 = enabled )
          * @hide
@@ -7383,6 +7399,17 @@
                 BOOLEAN_VALIDATOR;
 
         /**
+         * Whether the swipe up gesture to switch apps should be enabled.
+         *
+         * @hide
+         */
+        public static final String SWIPE_UP_TO_SWITCH_APPS_ENABLED =
+                "swipe_up_to_switch_apps_enabled";
+
+        private static final Validator SWIPE_UP_TO_SWITCH_APPS_ENABLED_VALIDATOR =
+                BOOLEAN_VALIDATOR;
+
+        /**
          * Whether or not the smart camera lift trigger that launches the camera when the user moves
          * the phone into a position for taking photos should be enabled.
          *
@@ -7885,6 +7912,7 @@
             PREFERRED_TTY_MODE,
             ENHANCED_VOICE_PRIVACY_ENABLED,
             TTY_MODE_ENABLED,
+            RTT_CALLING_MODE,
             INCALL_POWER_BUTTON_BEHAVIOR,
             NIGHT_DISPLAY_CUSTOM_START_TIME,
             NIGHT_DISPLAY_CUSTOM_END_TIME,
@@ -7892,6 +7920,7 @@
             NIGHT_DISPLAY_AUTO_MODE,
             SYNC_PARENT_SOUNDS,
             CAMERA_DOUBLE_TWIST_TO_FLIP_ENABLED,
+            SWIPE_UP_TO_SWITCH_APPS_ENABLED,
             CAMERA_DOUBLE_TAP_POWER_GESTURE_DISABLED,
             SYSTEM_NAVIGATION_KEYS_ENABLED,
             QS_TILES,
@@ -8014,6 +8043,7 @@
             VALIDATORS.put(ENHANCED_VOICE_PRIVACY_ENABLED,
                     ENHANCED_VOICE_PRIVACY_ENABLED_VALIDATOR);
             VALIDATORS.put(TTY_MODE_ENABLED, TTY_MODE_ENABLED_VALIDATOR);
+            VALIDATORS.put(RTT_CALLING_MODE, RTT_CALLING_MODE_VALIDATOR);
             VALIDATORS.put(INCALL_POWER_BUTTON_BEHAVIOR, INCALL_POWER_BUTTON_BEHAVIOR_VALIDATOR);
             VALIDATORS.put(NIGHT_DISPLAY_CUSTOM_START_TIME,
                     NIGHT_DISPLAY_CUSTOM_START_TIME_VALIDATOR);
@@ -8024,6 +8054,8 @@
             VALIDATORS.put(SYNC_PARENT_SOUNDS, SYNC_PARENT_SOUNDS_VALIDATOR);
             VALIDATORS.put(CAMERA_DOUBLE_TWIST_TO_FLIP_ENABLED,
                     CAMERA_DOUBLE_TWIST_TO_FLIP_ENABLED_VALIDATOR);
+            VALIDATORS.put(SWIPE_UP_TO_SWITCH_APPS_ENABLED,
+                    SWIPE_UP_TO_SWITCH_APPS_ENABLED_VALIDATOR);
             VALIDATORS.put(CAMERA_DOUBLE_TAP_POWER_GESTURE_DISABLED,
                     CAMERA_DOUBLE_TAP_POWER_GESTURE_DISABLED_VALIDATOR);
             VALIDATORS.put(SYSTEM_NAVIGATION_KEYS_ENABLED,
@@ -8935,6 +8967,20 @@
        /** {@hide} */
        public static final String NETSTATS_UID_TAG_DELETE_AGE = "netstats_uid_tag_delete_age";
 
+       /** {@hide} */
+       public static final String NETPOLICY_QUOTA_ENABLED = "netpolicy_quota_enabled";
+       /** {@hide} */
+       public static final String NETPOLICY_QUOTA_UNLIMITED = "netpolicy_quota_unlimited";
+       /** {@hide} */
+       public static final String NETPOLICY_QUOTA_LIMITED = "netpolicy_quota_limited";
+       /** {@hide} */
+       public static final String NETPOLICY_QUOTA_FRAC_JOBS = "netpolicy_quota_frac_jobs";
+       /** {@hide} */
+       public static final String NETPOLICY_QUOTA_FRAC_MULTIPATH = "netpolicy_quota_frac_multipath";
+
+       /** {@hide} */
+       public static final String NETPOLICY_OVERRIDE_ENABLED = "netpolicy_override_enabled";
+
        /**
         * User preference for which network(s) should be used. Only the
         * connectivity service should touch this.
@@ -9309,6 +9355,15 @@
                "network_metered_multipath_preference";
 
         /**
+         * Default daily multipath budget used by ConnectivityManager.getMultipathPreference()
+         * on metered networks. This default quota is only used if quota could not be determined
+         * from data plan or data limit/warning set by the user.
+         * @hide
+         */
+        public static final String NETWORK_DEFAULT_DAILY_MULTIPATH_QUOTA_BYTES =
+                "network_default_daily_multipath_quota_bytes";
+
+        /**
          * Network watchlist last report time.
          * @hide
          */
@@ -10797,6 +10852,15 @@
                 = "time_only_mode_constants";
 
         /**
+         * Whether of not to send keycode sleep for ungaze when Home is the foreground activity on
+         * watch type devices.
+         * Type: int (0 for false, 1 for true)
+         * Default: 0
+         * @hide
+         */
+        public static final String UNGAZE_SLEEP_ENABLED = "ungaze_sleep_enabled";
+
+        /**
          * Whether or not Network Watchlist feature is enabled.
          * Type: int (0 for false, 1 for true)
          * Default: 0
@@ -11076,6 +11140,14 @@
         public static final String ALWAYS_FINISH_ACTIVITIES = "always_finish_activities";
 
         /**
+         * If nonzero, all system error dialogs will be hidden.  For example, the
+         * crash and ANR dialogs will not be shown, and the system will just proceed
+         * as if they had been accepted by the user.
+         * @hide
+         */
+        public static final String HIDE_ERROR_DIALOGS = "hide_error_dialogs";
+
+        /**
          * Use Dock audio output for media:
          *      0 = disabled
          *      1 = enabled
@@ -11694,6 +11766,38 @@
                 "hidden_api_blacklist_exemptions";
 
         /**
+         * Sampling rate for hidden API access event logs, as an integer in the range 0 to 0x10000
+         * inclusive.
+         *
+         * @hide
+         */
+        public static final String HIDDEN_API_ACCESS_LOG_SAMPLING_RATE =
+                "hidden_api_access_log_sampling_rate";
+
+        /**
+         * Hidden API enforcement policy for apps targeting SDK versions prior to the latest
+         * version.
+         *
+         * Values correspond to @{@link
+         * android.content.pm.ApplicationInfo.HiddenApiEnforcementPolicy}
+         *
+         * @hide
+         */
+        public static final String HIDDEN_API_POLICY_PRE_P_APPS =
+                "hidden_api_policy_pre_p_apps";
+
+        /**
+         * Hidden API enforcement policy for apps targeting the current SDK version.
+         *
+         * Values correspond to @{@link
+         * android.content.pm.ApplicationInfo.HiddenApiEnforcementPolicy}
+         *
+         * @hide
+         */
+        public static final String HIDDEN_API_POLICY_P_APPS =
+                "hidden_api_policy_p_apps";
+
+        /**
          * Timeout for a single {@link android.media.soundtrigger.SoundTriggerDetectionService}
          * operation (in ms).
          *
@@ -12532,6 +12636,19 @@
          */
          public static final String SWAP_ENABLED = "swap_enabled";
 
+        /**
+         * Blacklist of GNSS satellites.
+         *
+         * This is a list of integers separated by commas to represent pairs of (constellation,
+         * svid). Thus, the number of integers should be even.
+         *
+         * E.g.: "3,0,5,24" denotes (constellation=3, svid=0) and (constellation=5, svid=24) are
+         * blacklisted. Note that svid=0 denotes all svids in the
+         * constellation are blacklisted.
+         *
+         * @hide
+         */
+        public static final String GNSS_SATELLITE_BLACKLIST = "gnss_satellite_blacklist";
     }
 
     /**
diff --git a/android/se/omapi/Channel.java b/android/se/omapi/Channel.java
index c8efede..5db3c1a 100644
--- a/android/se/omapi/Channel.java
+++ b/android/se/omapi/Channel.java
@@ -39,7 +39,7 @@
  *
  * @see <a href="http://globalplatform.org">GlobalPlatform Open Mobile API</a>
  */
-public class Channel {
+public final class Channel implements java.nio.channels.Channel {
 
     private static final String TAG = "OMAPI.Channel";
     private Session mSession;
@@ -64,7 +64,7 @@
      * before closing the channel.
      */
     public void close() {
-        if (!isClosed()) {
+        if (isOpen()) {
             synchronized (mLock) {
                 try {
                     mChannel.close();
@@ -76,21 +76,21 @@
     }
 
     /**
-     * Tells if this channel is closed.
+     * Tells if this channel is open.
      *
-     * @return <code>true</code> if the channel is closed or in case of an error.
-     *         <code>false</code> otherwise.
+     * @return <code>false</code> if the channel is closed or in case of an error.
+     *         <code>true</code> otherwise.
      */
-    public boolean isClosed() {
+    public boolean isOpen() {
         if (!mService.isConnected()) {
             Log.e(TAG, "service not connected to system");
-            return true;
+            return false;
         }
         try {
-            return mChannel.isClosed();
+            return !mChannel.isClosed();
         } catch (RemoteException e) {
             Log.e(TAG, "Exception in isClosed()");
-            return true;
+            return false;
         }
     }
 
diff --git a/android/se/omapi/Reader.java b/android/se/omapi/Reader.java
index 9be3da6..80262f7 100644
--- a/android/se/omapi/Reader.java
+++ b/android/se/omapi/Reader.java
@@ -37,7 +37,7 @@
  *
  * @see <a href="http://globalplatform.org">GlobalPlatform Open Mobile API</a>
  */
-public class Reader {
+public final class Reader {
 
     private static final String TAG = "OMAPI.Reader";
     private final String mName;
diff --git a/android/se/omapi/SEService.java b/android/se/omapi/SEService.java
index 311dc4c..14727f0 100644
--- a/android/se/omapi/SEService.java
+++ b/android/se/omapi/SEService.java
@@ -32,6 +32,7 @@
 import android.util.Log;
 
 import java.util.HashMap;
+import java.util.concurrent.Executor;
 
 /**
  * The SEService realises the communication to available Secure Elements on the
@@ -40,7 +41,7 @@
  *
  * @see <a href="http://simalliance.org">SIMalliance Open Mobile API  v3.0</a>
  */
-public class SEService {
+public final class SEService {
 
     /**
      * Error code used with ServiceSpecificException.
@@ -62,11 +63,11 @@
     /**
      * Interface to send call-backs to the application when the service is connected.
      */
-    public interface SecureElementListener {
+    public interface OnConnectedListener {
         /**
          * Called by the framework when the service is connected.
          */
-        void onServiceConnected();
+        void onConnected();
     }
 
     /**
@@ -74,16 +75,22 @@
      * SEService could be bound to the backend.
      */
     private class SEListener extends ISecureElementListener.Stub {
-        public SecureElementListener mListener = null;
+        public OnConnectedListener mListener = null;
+        public Executor mExecutor = null;
 
         @Override
         public IBinder asBinder() {
             return this;
         }
 
-        public void onServiceConnected() {
-            if (mListener != null) {
-                mListener.onServiceConnected();
+        public void onConnected() {
+            if (mListener != null && mExecutor != null) {
+                mExecutor.execute(new Runnable() {
+                    @Override
+                    public void run() {
+                        mListener.onConnected();
+                    }
+                });
             }
         }
     }
@@ -116,22 +123,26 @@
      * the specified listener is called or if isConnected() returns
      * <code>true</code>. <br>
      * The call-back object passed as a parameter will have its
-     * onServiceConnected() method called when the connection actually happen.
+     * onConnected() method called when the connection actually happen.
      *
      * @param context
      *            the context of the calling application. Cannot be
      *            <code>null</code>.
      * @param listener
-     *            a SecureElementListener object.
+     *            a OnConnectedListener object.
+     * @param executor
+     *            an Executor which will be used when invoking the callback.
      */
-    public SEService(@NonNull Context context, @NonNull SecureElementListener listener) {
+    public SEService(@NonNull Context context, @NonNull Executor executor,
+            @NonNull OnConnectedListener listener) {
 
-        if (context == null) {
-            throw new NullPointerException("context must not be null");
+        if (context == null || listener == null || executor == null) {
+            throw new NullPointerException("Arguments must not be null");
         }
 
         mContext = context;
         mSEListener.mListener = listener;
+        mSEListener.mExecutor = executor;
 
         mConnection = new ServiceConnection() {
 
@@ -140,7 +151,7 @@
 
                 mSecureElementService = ISecureElementService.Stub.asInterface(service);
                 if (mSEListener != null) {
-                    mSEListener.onServiceConnected();
+                    mSEListener.onConnected();
                 }
                 Log.i(TAG, "Service onServiceConnected");
             }
@@ -171,12 +182,12 @@
     }
 
     /**
-     * Returns the list of available Secure Element readers.
+     * Returns an array of available Secure Element readers.
      * There must be no duplicated objects in the returned list.
      * All available readers shall be listed even if no card is inserted.
      *
-     * @return The readers list, as an array of Readers. If there are no
-     * readers the returned array is of length 0.
+     * @return An array of Readers. If there are no readers the returned array
+     * is of length 0.
      */
     public @NonNull Reader[] getReaders() {
         if (mSecureElementService == null) {
@@ -212,7 +223,8 @@
      * (including any binding to an underlying service).
      * As a result isConnected() will return false after shutdown() was called.
      * After this method call, the SEService object is not connected.
-     * It is recommended to call this method in the termination method of the calling application
+     * This method should be called when connection to the Secure Element is not needed
+     * or in the termination method of the calling application
      * (or part of this application) which is bound to this SEService.
      */
     public void shutdown() {
diff --git a/android/se/omapi/Session.java b/android/se/omapi/Session.java
index adfeddd..d5f8c82 100644
--- a/android/se/omapi/Session.java
+++ b/android/se/omapi/Session.java
@@ -39,7 +39,7 @@
  *
  * @see <a href="http://simalliance.org">SIMalliance Open Mobile API  v3.0</a>
  */
-public class Session {
+public final class Session {
 
     private final Object mLock = new Object();
     private final SEService mService;
@@ -225,6 +225,32 @@
     }
 
     /**
+     * This method is provided to ease the development of mobile application and for compliancy
+     * with existing applications.
+     * This method is equivalent to openBasicChannel(aid, P2=0x00)
+     *
+     * @param aid the AID of the Applet to be selected on this channel, as a
+     *            byte array, or null if no Applet is to be selected.
+     * @throws IOException if there is a communication problem to the reader or
+     *             the Secure Element.
+     * @throws IllegalStateException if the Secure Element session is used after
+     *             being closed.
+     * @throws IllegalArgumentException if the aid's length is not within 5 to
+     *             16 (inclusive).
+     * @throws SecurityException if the calling application cannot be granted
+     *             access to this AID or the default Applet on this
+     *             session.
+     * @throws NoSuchElementException if the AID on the Secure Element is not available or cannot be
+     *             selected.
+     * @throws UnsupportedOperationException if the given P2 parameter is not
+     *             supported by the device
+     * @return an instance of Channel if available or null.
+     */
+    public @Nullable Channel openBasicChannel(@Nullable byte[] aid) throws IOException {
+        return openBasicChannel(aid, (byte) 0x00);
+    }
+
+    /**
      * Open a logical channel with the Secure Element, selecting the Applet represented by
      * the given AID. If the AID is null, which means no Applet is to be selected on this
      * channel, the default Applet is used. It's up to the Secure Element to choose which
@@ -304,4 +330,32 @@
             }
         }
     }
+
+    /**
+     * This method is provided to ease the development of mobile application and for compliancy
+     * with existing applications.
+     * This method is equivalent to openLogicalChannel(aid, P2=0x00)
+     *
+     * @param aid the AID of the Applet to be selected on this channel, as a
+     *            byte array.
+     * @throws IOException if there is a communication problem to the reader or
+     *             the Secure Element.
+     * @throws IllegalStateException if the Secure Element is used after being
+     *             closed.
+     * @throws IllegalArgumentException if the aid's length is not within 5 to
+     *             16 (inclusive).
+     * @throws SecurityException if the calling application cannot be granted
+     *             access to this AID or the default Applet on this
+     *             session.
+     * @throws NoSuchElementException if the AID on the Secure Element is not
+     *             available or cannot be selected or a logical channel is already
+     *             open to a non-multiselectable Applet.
+     * @throws UnsupportedOperationException if the given P2 parameter is not
+     *             supported by the device.
+     * @return an instance of Channel. Null if the Secure Element is unable to
+     *         provide a new logical channel.
+     */
+    public @Nullable Channel openLogicalChannel(@Nullable byte[] aid) throws IOException {
+        return openLogicalChannel(aid, (byte) 0x00);
+    }
 }
diff --git a/android/security/ConfirmationCallback.java b/android/security/ConfirmationCallback.java
index 4670bce..fd027f0 100644
--- a/android/security/ConfirmationCallback.java
+++ b/android/security/ConfirmationCallback.java
@@ -33,22 +33,22 @@
      *
      * @param dataThatWasConfirmed the data that was confirmed, see above for the format.
      */
-    public void onConfirmedByUser(@NonNull byte[] dataThatWasConfirmed) {}
+    public void onConfirmed(@NonNull byte[] dataThatWasConfirmed) {}
 
     /**
      * Called when the requested prompt was dismissed (not accepted) by the user.
      */
-    public void onDismissedByUser() {}
+    public void onDismissed() {}
 
     /**
      * Called when the requested prompt was dismissed by the application.
      */
-    public void onDismissedByApplication() {}
+    public void onCanceled() {}
 
     /**
      * Called when the requested prompt was dismissed because of a low-level error.
      *
-     * @param e an exception representing the error.
+     * @param e a throwable representing the error.
      */
-    public void onError(Exception e) {}
+    public void onError(Throwable e) {}
 }
diff --git a/android/security/ConfirmationDialog.java b/android/security/ConfirmationPrompt.java
similarity index 88%
rename from android/security/ConfirmationDialog.java
rename to android/security/ConfirmationPrompt.java
index 1697106..5330cff 100644
--- a/android/security/ConfirmationDialog.java
+++ b/android/security/ConfirmationPrompt.java
@@ -68,7 +68,7 @@
  * {@link #presentPrompt presentPrompt()} method. The <i>Relying Party</i> stores the nonce locally
  * since it'll use it in a later step.
  * <li> If the user approves the prompt a <i>Confirmation Response</i> is returned in the
- * {@link ConfirmationCallback#onConfirmedByUser onConfirmedByUser(byte[])} callback as the
+ * {@link ConfirmationCallback#onConfirmed onConfirmed(byte[])} callback as the
  * <code>dataThatWasConfirmed</code> parameter. This blob contains the text that was shown to the
  * user, the <code>extraData</code> parameter, and possibly other data.
  * <li> The application signs the <i>Confirmation Response</i> with the previously created key and
@@ -82,8 +82,8 @@
  * last bullet, is to have the <i>Relying Party</i> generate <code>promptText</code> and store it
  * along the nonce in the <code>extraData</code> blob.
  */
-public class ConfirmationDialog {
-    private static final String TAG = "ConfirmationDialog";
+public class ConfirmationPrompt {
+    private static final String TAG = "ConfirmationPrompt";
 
     private CharSequence mPromptText;
     private byte[] mExtraData;
@@ -97,15 +97,15 @@
             ConfirmationCallback callback) {
         switch (responseCode) {
             case KeyStore.CONFIRMATIONUI_OK:
-                callback.onConfirmedByUser(dataThatWasConfirmed);
+                callback.onConfirmed(dataThatWasConfirmed);
                 break;
 
             case KeyStore.CONFIRMATIONUI_CANCELED:
-                callback.onDismissedByUser();
+                callback.onDismissed();
                 break;
 
             case KeyStore.CONFIRMATIONUI_ABORTED:
-                callback.onDismissedByApplication();
+                callback.onCanceled();
                 break;
 
             case KeyStore.CONFIRMATIONUI_SYSTEM_ERROR:
@@ -145,21 +145,25 @@
             };
 
     /**
-     * A builder that collects arguments, to be shown on the system-provided confirmation dialog.
+     * A builder that collects arguments, to be shown on the system-provided confirmation prompt.
      */
-    public static class Builder {
+    public static final class Builder {
 
+        private Context mContext;
         private CharSequence mPromptText;
         private byte[] mExtraData;
 
         /**
-         * Creates a builder for the confirmation dialog.
+         * Creates a builder for the confirmation prompt.
+         *
+         * @param context the application context
          */
-        public Builder() {
+        public Builder(Context context) {
+            mContext = context;
         }
 
         /**
-         * Sets the prompt text for the dialog.
+         * Sets the prompt text for the prompt.
          *
          * @param promptText the text to present in the prompt.
          * @return the builder.
@@ -170,7 +174,7 @@
         }
 
         /**
-         * Sets the extra data for the dialog.
+         * Sets the extra data for the prompt.
          *
          * @param extraData data to include in the response data.
          * @return the builder.
@@ -181,24 +185,23 @@
         }
 
         /**
-         * Creates a {@link ConfirmationDialog} with the arguments supplied to this builder.
+         * Creates a {@link ConfirmationPrompt} with the arguments supplied to this builder.
          *
-         * @param context the application context
-         * @return a {@link ConfirmationDialog}
+         * @return a {@link ConfirmationPrompt}
          * @throws IllegalArgumentException if any of the required fields are not set.
          */
-        public ConfirmationDialog build(Context context) {
+        public ConfirmationPrompt build() {
             if (TextUtils.isEmpty(mPromptText)) {
                 throw new IllegalArgumentException("prompt text must be set and non-empty");
             }
             if (mExtraData == null) {
                 throw new IllegalArgumentException("extraData must be set");
             }
-            return new ConfirmationDialog(context, mPromptText, mExtraData);
+            return new ConfirmationPrompt(mContext, mPromptText, mExtraData);
         }
     }
 
-    private ConfirmationDialog(Context context, CharSequence promptText, byte[] extraData) {
+    private ConfirmationPrompt(Context context, CharSequence promptText, byte[] extraData) {
         mContext = context;
         mPromptText = promptText;
         mExtraData = extraData;
@@ -227,10 +230,10 @@
         return uiOptionsAsFlags;
     }
 
-    private boolean isAccessibilityServiceRunning() {
+    private static boolean isAccessibilityServiceRunning(Context context) {
         boolean serviceRunning = false;
         try {
-            ContentResolver contentResolver = mContext.getContentResolver();
+            ContentResolver contentResolver = context.getContentResolver();
             int a11yEnabled = Settings.Secure.getInt(contentResolver,
                     Settings.Secure.ACCESSIBILITY_ENABLED);
             if (a11yEnabled == 1) {
@@ -249,12 +252,12 @@
      * When the prompt is no longer being presented, one of the methods in
      * {@link ConfirmationCallback} is called on the supplied callback object.
      *
-     * Confirmation dialogs may not be available when accessibility services are running so this
+     * Confirmation prompts may not be available when accessibility services are running so this
      * may fail with a {@link ConfirmationNotAvailableException} exception even if
      * {@link #isSupported} returns {@code true}.
      *
      * @param executor the executor identifying the thread that will receive the callback.
-     * @param callback the callback to use when the dialog is done showing.
+     * @param callback the callback to use when the prompt is done showing.
      * @throws IllegalArgumentException if the prompt text is too long or malfomed.
      * @throws ConfirmationAlreadyPresentingException if another prompt is being presented.
      * @throws ConfirmationNotAvailableException if confirmation prompts are not supported.
@@ -265,7 +268,7 @@
         if (mCallback != null) {
             throw new ConfirmationAlreadyPresentingException();
         }
-        if (isAccessibilityServiceRunning()) {
+        if (isAccessibilityServiceRunning(mContext)) {
             throw new ConfirmationNotAvailableException();
         }
         mCallback = callback;
@@ -301,7 +304,7 @@
      * Cancels a prompt currently being displayed.
      *
      * On success, the
-     * {@link ConfirmationCallback#onDismissedByApplication onDismissedByApplication()} method on
+     * {@link ConfirmationCallback#onCanceled onCanceled()} method on
      * the supplied callback object will be called asynchronously.
      *
      * @throws IllegalStateException if no prompt is currently being presented.
@@ -324,9 +327,13 @@
     /**
      * Checks if the device supports confirmation prompts.
      *
+     * @param context the application context.
      * @return true if confirmation prompts are supported by the device.
      */
-    public static boolean isSupported() {
+    public static boolean isSupported(Context context) {
+        if (isAccessibilityServiceRunning(context)) {
+            return false;
+        }
         return KeyStore.getInstance().isConfirmationPromptSupported();
     }
 }
diff --git a/android/security/KeyStoreException.java b/android/security/KeyStoreException.java
index 88e768c..30389a2 100644
--- a/android/security/KeyStoreException.java
+++ b/android/security/KeyStoreException.java
@@ -16,12 +16,15 @@
 
 package android.security;
 
+import android.annotation.TestApi;
+
 /**
  * KeyStore/keymaster exception with positive error codes coming from the KeyStore and negative
  * ones from keymaster.
  *
  * @hide
  */
+@TestApi
 public class KeyStoreException extends Exception {
 
     private final int mErrorCode;
diff --git a/android/security/keystore/BackwardsCompat.java b/android/security/keystore/BackwardsCompat.java
deleted file mode 100644
index cf5fe1f..0000000
--- a/android/security/keystore/BackwardsCompat.java
+++ /dev/null
@@ -1,127 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.security.keystore;
-
-import java.util.ArrayList;
-import java.util.List;
-import java.util.function.Function;
-
-/**
- * Helpers for converting classes between old and new API, so we can preserve backwards
- * compatibility while teamfooding. This will be removed soon.
- *
- * @hide
- */
-class BackwardsCompat {
-
-
-    static KeychainProtectionParams toLegacyKeychainProtectionParams(
-            android.security.keystore.recovery.KeyChainProtectionParams keychainProtectionParams
-    ) {
-        return new KeychainProtectionParams.Builder()
-                .setUserSecretType(keychainProtectionParams.getUserSecretType())
-                .setSecret(keychainProtectionParams.getSecret())
-                .setLockScreenUiFormat(keychainProtectionParams.getLockScreenUiFormat())
-                .setKeyDerivationParams(
-                        toLegacyKeyDerivationParams(
-                                keychainProtectionParams.getKeyDerivationParams()))
-                .build();
-    }
-
-    static KeyDerivationParams toLegacyKeyDerivationParams(
-            android.security.keystore.recovery.KeyDerivationParams keyDerivationParams
-    ) {
-        return new KeyDerivationParams(
-                keyDerivationParams.getAlgorithm(), keyDerivationParams.getSalt());
-    }
-
-    static WrappedApplicationKey toLegacyWrappedApplicationKey(
-            android.security.keystore.recovery.WrappedApplicationKey wrappedApplicationKey
-    ) {
-        return new WrappedApplicationKey.Builder()
-                .setAlias(wrappedApplicationKey.getAlias())
-                .setEncryptedKeyMaterial(wrappedApplicationKey.getEncryptedKeyMaterial())
-                .build();
-    }
-
-    static android.security.keystore.recovery.KeyDerivationParams fromLegacyKeyDerivationParams(
-            KeyDerivationParams keyDerivationParams
-    ) {
-        return android.security.keystore.recovery.KeyDerivationParams.createSha256Params(
-                keyDerivationParams.getSalt());
-    }
-
-    static android.security.keystore.recovery.WrappedApplicationKey fromLegacyWrappedApplicationKey(
-            WrappedApplicationKey wrappedApplicationKey
-    ) {
-        return new android.security.keystore.recovery.WrappedApplicationKey.Builder()
-                .setAlias(wrappedApplicationKey.getAlias())
-                .setEncryptedKeyMaterial(wrappedApplicationKey.getEncryptedKeyMaterial())
-                .build();
-    }
-
-    static List<android.security.keystore.recovery.WrappedApplicationKey>
-            fromLegacyWrappedApplicationKeys(List<WrappedApplicationKey> wrappedApplicationKeys
-    ) {
-        return map(wrappedApplicationKeys, BackwardsCompat::fromLegacyWrappedApplicationKey);
-    }
-
-    static List<android.security.keystore.recovery.KeyChainProtectionParams>
-            fromLegacyKeychainProtectionParams(
-                    List<KeychainProtectionParams> keychainProtectionParams) {
-        return map(keychainProtectionParams, BackwardsCompat::fromLegacyKeychainProtectionParam);
-    }
-
-    static android.security.keystore.recovery.KeyChainProtectionParams
-            fromLegacyKeychainProtectionParam(KeychainProtectionParams keychainProtectionParams) {
-        return new android.security.keystore.recovery.KeyChainProtectionParams.Builder()
-                .setUserSecretType(keychainProtectionParams.getUserSecretType())
-                .setSecret(keychainProtectionParams.getSecret())
-                .setLockScreenUiFormat(keychainProtectionParams.getLockScreenUiFormat())
-                .setKeyDerivationParams(
-                        fromLegacyKeyDerivationParams(
-                                keychainProtectionParams.getKeyDerivationParams()))
-                .build();
-    }
-
-    static KeychainSnapshot toLegacyKeychainSnapshot(
-            android.security.keystore.recovery.KeyChainSnapshot keychainSnapshot
-    ) {
-        return new KeychainSnapshot.Builder()
-                .setCounterId(keychainSnapshot.getCounterId())
-                .setEncryptedRecoveryKeyBlob(keychainSnapshot.getEncryptedRecoveryKeyBlob())
-                .setTrustedHardwarePublicKey(keychainSnapshot.getTrustedHardwarePublicKey())
-                .setSnapshotVersion(keychainSnapshot.getSnapshotVersion())
-                .setMaxAttempts(keychainSnapshot.getMaxAttempts())
-                .setServerParams(keychainSnapshot.getServerParams())
-                .setKeychainProtectionParams(
-                        map(keychainSnapshot.getKeyChainProtectionParams(),
-                                BackwardsCompat::toLegacyKeychainProtectionParams))
-                .setWrappedApplicationKeys(
-                        map(keychainSnapshot.getWrappedApplicationKeys(),
-                                BackwardsCompat::toLegacyWrappedApplicationKey))
-                .build();
-    }
-
-    static <A, B> List<B> map(List<A> as, Function<A, B> f) {
-        ArrayList<B> bs = new ArrayList<>(as.size());
-        for (A a : as) {
-            bs.add(f.apply(a));
-        }
-        return bs;
-    }
-}
diff --git a/android/security/keystore/BadCertificateFormatException.java b/android/security/keystore/BadCertificateFormatException.java
deleted file mode 100644
index c51b773..0000000
--- a/android/security/keystore/BadCertificateFormatException.java
+++ /dev/null
@@ -1,27 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.security.keystore;
-
-/**
- * @deprecated Use {@link android.security.keystore.recovery.BadCertificateFormatException}.
- * @hide
- */
-public class BadCertificateFormatException extends RecoveryControllerException {
-    public BadCertificateFormatException(String msg) {
-        super(msg);
-    }
-}
diff --git a/android/security/keystore/DecryptionFailedException.java b/android/security/keystore/DecryptionFailedException.java
deleted file mode 100644
index c0b52f7..0000000
--- a/android/security/keystore/DecryptionFailedException.java
+++ /dev/null
@@ -1,28 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.security.keystore;
-
-/**
- * @deprecated Use {@link android.security.keystore.recovery.DecryptionFailedException}.
- * @hide
- */
-public class DecryptionFailedException extends RecoveryControllerException {
-
-    public DecryptionFailedException(String msg) {
-        super(msg);
-    }
-}
diff --git a/android/security/keystore/InternalRecoveryServiceException.java b/android/security/keystore/InternalRecoveryServiceException.java
deleted file mode 100644
index 40076f7..0000000
--- a/android/security/keystore/InternalRecoveryServiceException.java
+++ /dev/null
@@ -1,31 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.security.keystore;
-
-/**
- * @deprecated Use {@link android.security.keystore.recovery.InternalRecoveryServiceException}.
- * @hide
- */
-public class InternalRecoveryServiceException extends RecoveryControllerException {
-    public InternalRecoveryServiceException(String msg) {
-        super(msg);
-    }
-
-    public InternalRecoveryServiceException(String message, Throwable cause) {
-        super(message, cause);
-    }
-}
diff --git a/android/security/keystore/KeyDerivationParams.java b/android/security/keystore/KeyDerivationParams.java
deleted file mode 100644
index e475dc3..0000000
--- a/android/security/keystore/KeyDerivationParams.java
+++ /dev/null
@@ -1,113 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.security.keystore;
-
-import android.annotation.IntDef;
-import android.annotation.NonNull;
-import android.os.Parcel;
-import android.os.Parcelable;
-
-import com.android.internal.util.Preconditions;
-
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-
-/**
- * @deprecated Use {@link android.security.keystore.recovery.KeyDerivationParams}.
- * @hide
- */
-public final class KeyDerivationParams implements Parcelable {
-    private final int mAlgorithm;
-    private byte[] mSalt;
-
-    /** @hide */
-    @Retention(RetentionPolicy.SOURCE)
-    @IntDef(prefix = {"ALGORITHM_"}, value = {ALGORITHM_SHA256, ALGORITHM_ARGON2ID})
-    public @interface KeyDerivationAlgorithm {
-    }
-
-    /**
-     * Salted SHA256
-     */
-    public static final int ALGORITHM_SHA256 = 1;
-
-    /**
-     * Argon2ID
-     * @hide
-     */
-    // TODO: add Argon2ID support.
-    public static final int ALGORITHM_ARGON2ID = 2;
-
-    /**
-     * Creates instance of the class to to derive key using salted SHA256 hash.
-     */
-    public static KeyDerivationParams createSha256Params(@NonNull byte[] salt) {
-        return new KeyDerivationParams(ALGORITHM_SHA256, salt);
-    }
-
-    KeyDerivationParams(@KeyDerivationAlgorithm int algorithm, @NonNull byte[] salt) {
-        mAlgorithm = algorithm;
-        mSalt = Preconditions.checkNotNull(salt);
-    }
-
-    /**
-     * Gets algorithm.
-     */
-    public @KeyDerivationAlgorithm int getAlgorithm() {
-        return mAlgorithm;
-    }
-
-    /**
-     * Gets salt.
-     */
-    public @NonNull byte[] getSalt() {
-        return mSalt;
-    }
-
-    public static final Parcelable.Creator<KeyDerivationParams> CREATOR =
-            new Parcelable.Creator<KeyDerivationParams>() {
-        public KeyDerivationParams createFromParcel(Parcel in) {
-                return new KeyDerivationParams(in);
-        }
-
-        public KeyDerivationParams[] newArray(int length) {
-            return new KeyDerivationParams[length];
-        }
-    };
-
-    /**
-     * @hide
-     */
-    @Override
-    public void writeToParcel(Parcel out, int flags) {
-        out.writeInt(mAlgorithm);
-        out.writeByteArray(mSalt);
-    }
-
-    /**
-     * @hide
-     */
-    protected KeyDerivationParams(Parcel in) {
-        mAlgorithm = in.readInt();
-        mSalt = in.createByteArray();
-    }
-
-    @Override
-    public int describeContents() {
-        return 0;
-    }
-}
diff --git a/android/security/keystore/KeyGenParameterSpec.java b/android/security/keystore/KeyGenParameterSpec.java
index c0d0fb0..b2e0f67 100644
--- a/android/security/keystore/KeyGenParameterSpec.java
+++ b/android/security/keystore/KeyGenParameterSpec.java
@@ -19,6 +19,7 @@
 import android.annotation.IntRange;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.annotation.TestApi;
 import android.app.KeyguardManager;
 import android.hardware.fingerprint.FingerprintManager;
 import android.security.GateKeeper;
@@ -594,6 +595,14 @@
     /**
      * Returns {@code true} if the key is authorized to be used only if a test of user presence has
      * been performed between the {@code Signature.initSign()} and {@code Signature.sign()} calls.
+     * It requires that the KeyStore implementation have a direct way to validate the user presence
+     * for example a KeyStore hardware backed strongbox can use a button press that is observable
+     * in hardware. A test for user presence is tangential to authentication. The test can be part
+     * of an authentication step as long as this step can be validated by the hardware protecting
+     * the key and cannot be spoofed. For example, a physical button press can be used as a test of
+     * user presence if the other pins connected to the button are not able to simulate a button
+     * press. There must be no way for the primary processor to fake a button press, or that
+     * button must not be used as a test of user presence.
      */
     public boolean isUserPresenceRequired() {
         return mUserPresenceRequired;
@@ -673,8 +682,8 @@
     }
 
     /**
-     * Returns {@code true} if the screen must be unlocked for this key to be used for encryption or
-     * signing. Decryption and signature verification will still be available when the screen is
+     * Returns {@code true} if the screen must be unlocked for this key to be used for decryption or
+     * signing. Encryption and signature verification will still be available when the screen is
      * locked.
      *
      * @see Builder#setUnlockedDeviceRequired(boolean)
@@ -1180,6 +1189,14 @@
         /**
          * Sets whether a test of user presence is required to be performed between the
          * {@code Signature.initSign()} and {@code Signature.sign()} method calls.
+         * It requires that the KeyStore implementation have a direct way to validate the user
+         * presence for example a KeyStore hardware backed strongbox can use a button press that
+         * is observable in hardware. A test for user presence is tangential to authentication. The
+         * test can be part of an authentication step as long as this step can be validated by the
+         * hardware protecting the key and cannot be spoofed. For example, a physical button press
+         * can be used as a test of user presence if the other pins connected to the button are not
+         * able to simulate a button press.There must be no way for the primary processor to fake a
+         * button press, or that button must not be used as a test of user presence.
          */
         @NonNull
         public Builder setUserPresenceRequired(boolean required) {
@@ -1227,6 +1244,7 @@
          *
          * Sets whether to include a temporary unique ID field in the attestation certificate.
          */
+        @TestApi
         @NonNull
         public Builder setUniqueIdIncluded(boolean uniqueIdIncluded) {
             mUniqueIdIncluded = uniqueIdIncluded;
diff --git a/android/security/keystore/KeyProtection.java b/android/security/keystore/KeyProtection.java
index 4daf30c..fdcad85 100644
--- a/android/security/keystore/KeyProtection.java
+++ b/android/security/keystore/KeyProtection.java
@@ -19,6 +19,7 @@
 import android.annotation.IntRange;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.annotation.TestApi;
 import android.app.KeyguardManager;
 import android.hardware.fingerprint.FingerprintManager;
 import android.security.GateKeeper;
@@ -445,6 +446,14 @@
     /**
      * Returns {@code true} if the key is authorized to be used only if a test of user presence has
      * been performed between the {@code Signature.initSign()} and {@code Signature.sign()} calls.
+     * It requires that the KeyStore implementation have a direct way to validate the user presence
+     * for example a KeyStore hardware backed strongbox can use a button press that is observable
+     * in hardware. A test for user presence is tangential to authentication. The test can be part
+     * of an authentication step as long as this step can be validated by the hardware protecting
+     * the key and cannot be spoofed. For example, a physical button press can be used as a test of
+     * user presence if the other pins connected to the button are not able to simulate a button
+     * press. There must be no way for the primary processor to fake a button press, or that
+     * button must not be used as a test of user presence.
      */
     public boolean isUserPresenceRequired() {
         return mUserPresenceRequred;
@@ -493,6 +502,7 @@
      * @see KeymasterUtils#addUserAuthArgs
      * @hide
      */
+    @TestApi
     public long getBoundToSpecificSecureUserId() {
         return mBoundToSecureUserId;
     }
@@ -508,8 +518,8 @@
     }
 
     /**
-     * Returns {@code true} if the screen must be unlocked for this key to be used for encryption or
-     * signing. Decryption and signature verification will still be available when the screen is
+     * Returns {@code true} if the screen must be unlocked for this key to be used for decryption or
+     * signing. Encryption and signature verification will still be available when the screen is
      * locked.
      *
      * @see Builder#setUnlockedDeviceRequired(boolean)
@@ -840,7 +850,15 @@
 
         /**
          * Sets whether a test of user presence is required to be performed between the
-         * {@code Signature.initSign()} and {@code Signature.sign()} method calls.
+         * {@code Signature.initSign()} and {@code Signature.sign()} method calls. It requires that
+         * the KeyStore implementation have a direct way to validate the user presence for example
+         * a KeyStore hardware backed strongbox can use a button press that is observable in
+         * hardware. A test for user presence is tangential to authentication. The test can be part
+         * of an authentication step as long as this step can be validated by the hardware
+         * protecting the key and cannot be spoofed. For example, a physical button press can be
+         * used as a test of user presence if the other pins connected to the button are not able
+         * to simulate a button press. There must be no way for the primary processor to fake a
+         * button press, or that button must not be used as a test of user presence.
          */
         @NonNull
         public Builder setUserPresenceRequired(boolean required) {
@@ -910,6 +928,7 @@
          * @see KeyProtection#getBoundToSpecificSecureUserId()
          * @hide
          */
+        @TestApi
         public Builder setBoundToSpecificSecureUserId(long secureUserId) {
             mBoundToSecureUserId = secureUserId;
             return this;
diff --git a/android/security/keystore/KeychainProtectionParams.java b/android/security/keystore/KeychainProtectionParams.java
deleted file mode 100644
index 19a087d..0000000
--- a/android/security/keystore/KeychainProtectionParams.java
+++ /dev/null
@@ -1,269 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.security.keystore;
-
-import android.annotation.IntDef;
-import android.annotation.NonNull;
-import android.os.Parcel;
-import android.os.Parcelable;
-
-import com.android.internal.util.Preconditions;
-
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.util.Arrays;
-
-/**
- * @deprecated Use {@link android.security.keystore.recovery.KeyChainProtectionParams}.
- * @hide
- */
-public final class KeychainProtectionParams implements Parcelable {
-    /** @hide */
-    @Retention(RetentionPolicy.SOURCE)
-    @IntDef({TYPE_LOCKSCREEN, TYPE_CUSTOM_PASSWORD})
-    public @interface UserSecretType {
-    }
-
-    /**
-     * Lockscreen secret is required to recover KeyStore.
-     */
-    public static final int TYPE_LOCKSCREEN = 100;
-
-    /**
-     * Custom passphrase, unrelated to lock screen, is required to recover KeyStore.
-     */
-    public static final int TYPE_CUSTOM_PASSWORD = 101;
-
-    /** @hide */
-    @Retention(RetentionPolicy.SOURCE)
-    @IntDef({TYPE_PIN, TYPE_PASSWORD, TYPE_PATTERN})
-    public @interface LockScreenUiFormat {
-    }
-
-    /**
-     * Pin with digits only.
-     */
-    public static final int TYPE_PIN = 1;
-
-    /**
-     * Password. String with latin-1 characters only.
-     */
-    public static final int TYPE_PASSWORD = 2;
-
-    /**
-     * Pattern with 3 by 3 grid.
-     */
-    public static final int TYPE_PATTERN = 3;
-
-    @UserSecretType
-    private Integer mUserSecretType;
-
-    @LockScreenUiFormat
-    private Integer mLockScreenUiFormat;
-
-    /**
-     * Parameters of the key derivation function, including algorithm, difficulty, salt.
-     */
-    private KeyDerivationParams mKeyDerivationParams;
-    private byte[] mSecret; // Derived from user secret. The field must have limited visibility.
-
-    /**
-     * @param secret Constructor creates a reference to the secret. Caller must use
-     * @link {#clearSecret} to overwrite its value in memory.
-     * @hide
-     */
-    public KeychainProtectionParams(@UserSecretType int userSecretType,
-            @LockScreenUiFormat int lockScreenUiFormat,
-            @NonNull KeyDerivationParams keyDerivationParams,
-            @NonNull byte[] secret) {
-        mUserSecretType = userSecretType;
-        mLockScreenUiFormat = lockScreenUiFormat;
-        mKeyDerivationParams = Preconditions.checkNotNull(keyDerivationParams);
-        mSecret = Preconditions.checkNotNull(secret);
-    }
-
-    private KeychainProtectionParams() {
-
-    }
-
-    /**
-     * @see TYPE_LOCKSCREEN
-     * @see TYPE_CUSTOM_PASSWORD
-     */
-    public @UserSecretType int getUserSecretType() {
-        return mUserSecretType;
-    }
-
-    /**
-     * Specifies UX shown to user during recovery.
-     * Default value is {@code TYPE_LOCKSCREEN}
-     *
-     * @see TYPE_PIN
-     * @see TYPE_PASSWORD
-     * @see TYPE_PATTERN
-     */
-    public @LockScreenUiFormat int getLockScreenUiFormat() {
-        return mLockScreenUiFormat;
-    }
-
-    /**
-     * Specifies function used to derive symmetric key from user input
-     * Format is defined in separate util class.
-     */
-    public @NonNull KeyDerivationParams getKeyDerivationParams() {
-        return mKeyDerivationParams;
-    }
-
-    /**
-     * Secret derived from user input.
-     * Default value is empty array
-     *
-     * @return secret or empty array
-     */
-    public @NonNull byte[] getSecret() {
-        return mSecret;
-    }
-
-    /**
-     * Builder for creating {@link KeychainProtectionParams}.
-     */
-    public static class Builder {
-        private KeychainProtectionParams mInstance = new KeychainProtectionParams();
-
-        /**
-         * Sets user secret type.
-         *
-         * @see TYPE_LOCKSCREEN
-         * @see TYPE_CUSTOM_PASSWORD
-         * @param userSecretType The secret type
-         * @return This builder.
-         */
-        public Builder setUserSecretType(@UserSecretType int userSecretType) {
-            mInstance.mUserSecretType = userSecretType;
-            return this;
-        }
-
-        /**
-         * Sets UI format.
-         *
-         * @see TYPE_PIN
-         * @see TYPE_PASSWORD
-         * @see TYPE_PATTERN
-         * @param lockScreenUiFormat The UI format
-         * @return This builder.
-         */
-        public Builder setLockScreenUiFormat(@LockScreenUiFormat int lockScreenUiFormat) {
-            mInstance.mLockScreenUiFormat = lockScreenUiFormat;
-            return this;
-        }
-
-        /**
-         * Sets parameters of the key derivation function.
-         *
-         * @param keyDerivationParams Key derivation Params
-         * @return This builder.
-         */
-        public Builder setKeyDerivationParams(@NonNull KeyDerivationParams
-                keyDerivationParams) {
-            mInstance.mKeyDerivationParams = keyDerivationParams;
-            return this;
-        }
-
-        /**
-         * Secret derived from user input, or empty array.
-         *
-         * @param secret The secret.
-         * @return This builder.
-         */
-        public Builder setSecret(@NonNull byte[] secret) {
-            mInstance.mSecret = secret;
-            return this;
-        }
-
-
-        /**
-         * Creates a new {@link KeychainProtectionParams} instance.
-         * The instance will include default values, if {@link setSecret}
-         * or {@link setUserSecretType} were not called.
-         *
-         * @return new instance
-         * @throws NullPointerException if some required fields were not set.
-         */
-        @NonNull public KeychainProtectionParams build() {
-            if (mInstance.mUserSecretType == null) {
-                mInstance.mUserSecretType = TYPE_LOCKSCREEN;
-            }
-            Preconditions.checkNotNull(mInstance.mLockScreenUiFormat);
-            Preconditions.checkNotNull(mInstance.mKeyDerivationParams);
-            if (mInstance.mSecret == null) {
-                mInstance.mSecret = new byte[]{};
-            }
-            return mInstance;
-        }
-    }
-
-    /**
-     * Removes secret from memory than object is no longer used.
-     * Since finalizer call is not reliable, please use @link {#clearSecret} directly.
-     */
-    @Override
-    protected void finalize() throws Throwable {
-        clearSecret();
-        super.finalize();
-    }
-
-    /**
-     * Fills mSecret with zeroes.
-     */
-    public void clearSecret() {
-        Arrays.fill(mSecret, (byte) 0);
-    }
-
-    public static final Parcelable.Creator<KeychainProtectionParams> CREATOR =
-            new Parcelable.Creator<KeychainProtectionParams>() {
-        public KeychainProtectionParams createFromParcel(Parcel in) {
-            return new KeychainProtectionParams(in);
-        }
-
-        public KeychainProtectionParams[] newArray(int length) {
-            return new KeychainProtectionParams[length];
-        }
-    };
-
-    @Override
-    public void writeToParcel(Parcel out, int flags) {
-        out.writeInt(mUserSecretType);
-        out.writeInt(mLockScreenUiFormat);
-        out.writeTypedObject(mKeyDerivationParams, flags);
-        out.writeByteArray(mSecret);
-    }
-
-    /**
-     * @hide
-     */
-    protected KeychainProtectionParams(Parcel in) {
-        mUserSecretType = in.readInt();
-        mLockScreenUiFormat = in.readInt();
-        mKeyDerivationParams = in.readTypedObject(KeyDerivationParams.CREATOR);
-        mSecret = in.createByteArray();
-    }
-
-    @Override
-    public int describeContents() {
-        return 0;
-    }
-}
diff --git a/android/security/keystore/KeychainSnapshot.java b/android/security/keystore/KeychainSnapshot.java
deleted file mode 100644
index cf18fd1..0000000
--- a/android/security/keystore/KeychainSnapshot.java
+++ /dev/null
@@ -1,276 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.security.keystore;
-
-import android.annotation.NonNull;
-import android.os.Parcel;
-import android.os.Parcelable;
-
-import com.android.internal.util.Preconditions;
-
-import java.util.List;
-
-/**
- * @deprecated Use {@link android.security.keystore.recovery.KeyChainSnapshot}.
- * @hide
- */
-public final class KeychainSnapshot implements Parcelable {
-    private static final int DEFAULT_MAX_ATTEMPTS = 10;
-    private static final long DEFAULT_COUNTER_ID = 1L;
-
-    private int mSnapshotVersion;
-    private int mMaxAttempts = DEFAULT_MAX_ATTEMPTS;
-    private long mCounterId = DEFAULT_COUNTER_ID;
-    private byte[] mServerParams;
-    private byte[] mPublicKey;
-    private List<KeychainProtectionParams> mKeychainProtectionParams;
-    private List<WrappedApplicationKey> mEntryRecoveryData;
-    private byte[] mEncryptedRecoveryKeyBlob;
-
-    /**
-     * @hide
-     * Deprecated, consider using builder.
-     */
-    public KeychainSnapshot(
-            int snapshotVersion,
-            @NonNull List<KeychainProtectionParams> keychainProtectionParams,
-            @NonNull List<WrappedApplicationKey> wrappedApplicationKeys,
-            @NonNull byte[] encryptedRecoveryKeyBlob) {
-        mSnapshotVersion = snapshotVersion;
-        mKeychainProtectionParams =
-                Preconditions.checkCollectionElementsNotNull(keychainProtectionParams,
-                        "keychainProtectionParams");
-        mEntryRecoveryData = Preconditions.checkCollectionElementsNotNull(wrappedApplicationKeys,
-                "wrappedApplicationKeys");
-        mEncryptedRecoveryKeyBlob = Preconditions.checkNotNull(encryptedRecoveryKeyBlob);
-    }
-
-    private KeychainSnapshot() {
-
-    }
-
-    /**
-     * Snapshot version for given account. It is incremented when user secret or list of application
-     * keys changes.
-     */
-    public int getSnapshotVersion() {
-        return mSnapshotVersion;
-    }
-
-    /**
-     * Number of user secret guesses allowed during Keychain recovery.
-     */
-    public int getMaxAttempts() {
-        return mMaxAttempts;
-    }
-
-    /**
-     * CounterId which is rotated together with user secret.
-     */
-    public long getCounterId() {
-        return mCounterId;
-    }
-
-    /**
-     * Server parameters.
-     */
-    public @NonNull byte[] getServerParams() {
-        return mServerParams;
-    }
-
-    /**
-     * Public key used to encrypt {@code encryptedRecoveryKeyBlob}.
-     *
-     * See implementation for binary key format
-     */
-    // TODO: document key format.
-    public @NonNull byte[] getTrustedHardwarePublicKey() {
-        return mPublicKey;
-    }
-
-    /**
-     * UI and key derivation parameters. Note that combination of secrets may be used.
-     */
-    public @NonNull List<KeychainProtectionParams> getKeychainProtectionParams() {
-        return mKeychainProtectionParams;
-    }
-
-    /**
-     * List of application keys, with key material encrypted by
-     * the recovery key ({@link #getEncryptedRecoveryKeyBlob}).
-     */
-    public @NonNull List<WrappedApplicationKey> getWrappedApplicationKeys() {
-        return mEntryRecoveryData;
-    }
-
-    /**
-     * Recovery key blob, encrypted by user secret and recovery service public key.
-     */
-    public @NonNull byte[] getEncryptedRecoveryKeyBlob() {
-        return mEncryptedRecoveryKeyBlob;
-    }
-
-    public static final Parcelable.Creator<KeychainSnapshot> CREATOR =
-            new Parcelable.Creator<KeychainSnapshot>() {
-        public KeychainSnapshot createFromParcel(Parcel in) {
-            return new KeychainSnapshot(in);
-        }
-
-        public KeychainSnapshot[] newArray(int length) {
-            return new KeychainSnapshot[length];
-        }
-    };
-
-    /**
-     * Builder for creating {@link KeychainSnapshot}.
-     *
-     * @hide
-     */
-    public static class Builder {
-        private KeychainSnapshot mInstance = new KeychainSnapshot();
-
-        /**
-         * Snapshot version for given account.
-         *
-         * @param snapshotVersion The snapshot version
-         * @return This builder.
-         */
-        public Builder setSnapshotVersion(int snapshotVersion) {
-            mInstance.mSnapshotVersion = snapshotVersion;
-            return this;
-        }
-
-        /**
-         * Sets the number of user secret guesses allowed during Keychain recovery.
-         *
-         * @param maxAttempts The maximum number of guesses.
-         * @return This builder.
-         */
-        public Builder setMaxAttempts(int maxAttempts) {
-            mInstance.mMaxAttempts = maxAttempts;
-            return this;
-        }
-
-        /**
-         * Sets counter id.
-         *
-         * @param counterId The counter id.
-         * @return This builder.
-         */
-        public Builder setCounterId(long counterId) {
-            mInstance.mCounterId = counterId;
-            return this;
-        }
-
-        /**
-         * Sets server parameters.
-         *
-         * @param serverParams The server parameters
-         * @return This builder.
-         */
-        public Builder setServerParams(byte[] serverParams) {
-            mInstance.mServerParams = serverParams;
-            return this;
-        }
-
-        /**
-         * Sets public key used to encrypt recovery blob.
-         *
-         * @param publicKey The public key
-         * @return This builder.
-         */
-        public Builder setTrustedHardwarePublicKey(byte[] publicKey) {
-            mInstance.mPublicKey = publicKey;
-            return this;
-        }
-
-        /**
-         * Sets UI and key derivation parameters
-         *
-         * @param recoveryMetadata The UI and key derivation parameters
-         * @return This builder.
-         */
-        public Builder setKeychainProtectionParams(
-                @NonNull List<KeychainProtectionParams> recoveryMetadata) {
-            mInstance.mKeychainProtectionParams = recoveryMetadata;
-            return this;
-        }
-
-        /**
-         * List of application keys.
-         *
-         * @param entryRecoveryData List of application keys
-         * @return This builder.
-         */
-        public Builder setWrappedApplicationKeys(List<WrappedApplicationKey> entryRecoveryData) {
-            mInstance.mEntryRecoveryData = entryRecoveryData;
-            return this;
-        }
-
-        /**
-         * Sets recovery key blob
-         *
-         * @param encryptedRecoveryKeyBlob The recovery key blob.
-         * @return This builder.
-         */
-        public Builder setEncryptedRecoveryKeyBlob(@NonNull byte[] encryptedRecoveryKeyBlob) {
-            mInstance.mEncryptedRecoveryKeyBlob = encryptedRecoveryKeyBlob;
-            return this;
-        }
-
-
-        /**
-         * Creates a new {@link KeychainSnapshot} instance.
-         *
-         * @return new instance
-         * @throws NullPointerException if some required fields were not set.
-         */
-        @NonNull public KeychainSnapshot build() {
-            Preconditions.checkCollectionElementsNotNull(mInstance.mKeychainProtectionParams,
-                    "recoveryMetadata");
-            Preconditions.checkCollectionElementsNotNull(mInstance.mEntryRecoveryData,
-                    "entryRecoveryData");
-            Preconditions.checkNotNull(mInstance.mEncryptedRecoveryKeyBlob);
-            Preconditions.checkNotNull(mInstance.mServerParams);
-            Preconditions.checkNotNull(mInstance.mPublicKey);
-            return mInstance;
-        }
-    }
-
-    @Override
-    public void writeToParcel(Parcel out, int flags) {
-        out.writeInt(mSnapshotVersion);
-        out.writeTypedList(mKeychainProtectionParams);
-        out.writeByteArray(mEncryptedRecoveryKeyBlob);
-        out.writeTypedList(mEntryRecoveryData);
-    }
-
-    /**
-     * @hide
-     */
-    protected KeychainSnapshot(Parcel in) {
-        mSnapshotVersion = in.readInt();
-        mKeychainProtectionParams = in.createTypedArrayList(KeychainProtectionParams.CREATOR);
-        mEncryptedRecoveryKeyBlob = in.createByteArray();
-        mEntryRecoveryData = in.createTypedArrayList(WrappedApplicationKey.CREATOR);
-    }
-
-    @Override
-    public int describeContents() {
-        return 0;
-    }
-}
diff --git a/android/security/keystore/LockScreenRequiredException.java b/android/security/keystore/LockScreenRequiredException.java
deleted file mode 100644
index 0970284..0000000
--- a/android/security/keystore/LockScreenRequiredException.java
+++ /dev/null
@@ -1,27 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.security.keystore;
-
-/**
- * @deprecated Use {@link android.security.keystore.recovery.LockScreenRequiredException}.
- * @hide
- */
-public class LockScreenRequiredException extends RecoveryControllerException {
-    public LockScreenRequiredException(String msg) {
-        super(msg);
-    }
-}
diff --git a/android/security/keystore/RecoveryClaim.java b/android/security/keystore/RecoveryClaim.java
deleted file mode 100644
index 12be607..0000000
--- a/android/security/keystore/RecoveryClaim.java
+++ /dev/null
@@ -1,53 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.security.keystore;
-
-/**
- * @deprecated Use {@link android.security.keystore.recovery.RecoverySession}.
- * @hide
- */
-public class RecoveryClaim {
-
-    private final RecoverySession mRecoverySession;
-    private final byte[] mClaimBytes;
-
-    RecoveryClaim(RecoverySession recoverySession, byte[] claimBytes) {
-        mRecoverySession = recoverySession;
-        mClaimBytes = claimBytes;
-    }
-
-    /**
-     * Returns the session associated with the recovery attempt. This is used to match the symmetric
-     * key, which remains internal to the framework, for decrypting the claim response.
-     *
-     * @return The session data.
-     */
-    public RecoverySession getRecoverySession() {
-        return mRecoverySession;
-    }
-
-    /**
-     * Returns the encrypted claim's bytes.
-     *
-     * <p>This should be sent by the recovery agent to the remote secure hardware, which will use
-     * it to decrypt the keychain, before sending it re-encrypted with the session's symmetric key
-     * to the device.
-     */
-    public byte[] getClaimBytes() {
-        return mClaimBytes;
-    }
-}
diff --git a/android/security/keystore/RecoveryController.java b/android/security/keystore/RecoveryController.java
deleted file mode 100644
index ca67e35..0000000
--- a/android/security/keystore/RecoveryController.java
+++ /dev/null
@@ -1,467 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.security.keystore;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.app.PendingIntent;
-import android.content.pm.PackageManager.NameNotFoundException;
-import android.os.RemoteException;
-import android.os.ServiceSpecificException;
-import android.util.Log;
-
-import com.android.internal.widget.ILockSettings;
-
-import java.util.List;
-import java.util.Map;
-
-/**
- * @deprecated Use {@link android.security.keystore.recovery.RecoveryController}.
- * @hide
- */
-public class RecoveryController {
-    private static final String TAG = "RecoveryController";
-
-    /** Key has been successfully synced. */
-    public static final int RECOVERY_STATUS_SYNCED = 0;
-    /** Waiting for recovery agent to sync the key. */
-    public static final int RECOVERY_STATUS_SYNC_IN_PROGRESS = 1;
-    /** Recovery account is not available. */
-    public static final int RECOVERY_STATUS_MISSING_ACCOUNT = 2;
-    /** Key cannot be synced. */
-    public static final int RECOVERY_STATUS_PERMANENT_FAILURE = 3;
-
-    /**
-     * Failed because no snapshot is yet pending to be synced for the user.
-     *
-     * @hide
-     */
-    public static final int ERROR_NO_SNAPSHOT_PENDING = 21;
-
-    /**
-     * Failed due to an error internal to the recovery service. This is unexpected and indicates
-     * either a problem with the logic in the service, or a problem with a dependency of the
-     * service (such as AndroidKeyStore).
-     *
-     * @hide
-     */
-    public static final int ERROR_SERVICE_INTERNAL_ERROR = 22;
-
-    /**
-     * Failed because the user does not have a lock screen set.
-     *
-     * @hide
-     */
-    public static final int ERROR_INSECURE_USER = 23;
-
-    /**
-     * Error thrown when attempting to use a recovery session that has since been closed.
-     *
-     * @hide
-     */
-    public static final int ERROR_SESSION_EXPIRED = 24;
-
-    /**
-     * Failed because the provided certificate was not a valid X509 certificate.
-     *
-     * @hide
-     */
-    public static final int ERROR_BAD_CERTIFICATE_FORMAT = 25;
-
-    /**
-     * Error thrown if decryption failed. This might be because the tag is wrong, the key is wrong,
-     * the data has become corrupted, the data has been tampered with, etc.
-     *
-     * @hide
-     */
-    public static final int ERROR_DECRYPTION_FAILED = 26;
-
-
-    private final ILockSettings mBinder;
-
-    private RecoveryController(ILockSettings binder) {
-        mBinder = binder;
-    }
-
-    /**
-     * Deprecated.
-     * Gets a new instance of the class.
-     */
-    public static RecoveryController getInstance() {
-        throw new UnsupportedOperationException("using Deprecated RecoveryController version");
-    }
-
-    /**
-     * Initializes key recovery service for the calling application. RecoveryController
-     * randomly chooses one of the keys from the list and keeps it to use for future key export
-     * operations. Collection of all keys in the list must be signed by the provided {@code
-     * rootCertificateAlias}, which must also be present in the list of root certificates
-     * preinstalled on the device. The random selection allows RecoveryController to select
-     * which of a set of remote recovery service devices will be used.
-     *
-     * <p>In addition, RecoveryController enforces a delay of three months between
-     * consecutive initialization attempts, to limit the ability of an attacker to often switch
-     * remote recovery devices and significantly increase number of recovery attempts.
-     *
-     * @param rootCertificateAlias alias of a root certificate preinstalled on the device
-     * @param signedPublicKeyList binary blob a list of X509 certificates and signature
-     * @throws BadCertificateFormatException if the {@code signedPublicKeyList} is in a bad format.
-     * @throws InternalRecoveryServiceException if an unexpected error occurred in the recovery
-     *     service.
-     */
-    public void initRecoveryService(
-            @NonNull String rootCertificateAlias, @NonNull byte[] signedPublicKeyList)
-            throws BadCertificateFormatException, InternalRecoveryServiceException {
-        throw new UnsupportedOperationException("Deprecated initRecoveryService method called");
-
-    }
-
-    /**
-     * Returns data necessary to store all recoverable keys for given account. Key material is
-     * encrypted with user secret and recovery public key.
-     *
-     * @param account specific to Recovery agent.
-     * @return Data necessary to recover keystore.
-     * @throws InternalRecoveryServiceException if an unexpected error occurred in the recovery
-     *     service.
-     */
-    public @NonNull KeychainSnapshot getRecoveryData(@NonNull byte[] account)
-            throws InternalRecoveryServiceException {
-        try {
-            return BackwardsCompat.toLegacyKeychainSnapshot(mBinder.getKeyChainSnapshot());
-        } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
-        } catch (ServiceSpecificException e) {
-            if (e.errorCode == ERROR_NO_SNAPSHOT_PENDING) {
-                return null;
-            }
-            throw wrapUnexpectedServiceSpecificException(e);
-        }
-    }
-
-    /**
-     * Sets a listener which notifies recovery agent that new recovery snapshot is available. {@link
-     * #getRecoveryData} can be used to get the snapshot. Note that every recovery agent can have at
-     * most one registered listener at any time.
-     *
-     * @param intent triggered when new snapshot is available. Unregisters listener if the value is
-     *     {@code null}.
-     * @throws InternalRecoveryServiceException if an unexpected error occurred in the recovery
-     *     service.
-     */
-    public void setSnapshotCreatedPendingIntent(@Nullable PendingIntent intent)
-            throws InternalRecoveryServiceException {
-        try {
-            mBinder.setSnapshotCreatedPendingIntent(intent);
-        } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
-        } catch (ServiceSpecificException e) {
-            throw wrapUnexpectedServiceSpecificException(e);
-        }
-    }
-
-    /**
-     * Returns a map from recovery agent accounts to corresponding KeyStore recovery snapshot
-     * version. Version zero is used, if no snapshots were created for the account.
-     *
-     * @return Map from recovery agent accounts to snapshot versions.
-     * @see KeychainSnapshot#getSnapshotVersion
-     * @throws InternalRecoveryServiceException if an unexpected error occurred in the recovery
-     *     service.
-     */
-    public @NonNull Map<byte[], Integer> getRecoverySnapshotVersions()
-            throws InternalRecoveryServiceException {
-        throw new UnsupportedOperationException();
-    }
-
-    /**
-     * Server parameters used to generate new recovery key blobs. This value will be included in
-     * {@code KeychainSnapshot.getEncryptedRecoveryKeyBlob()}. The same value must be included
-     * in vaultParams {@link #startRecoverySession}
-     *
-     * @param serverParams included in recovery key blob.
-     * @see #getRecoveryData
-     * @throws InternalRecoveryServiceException if an unexpected error occurred in the recovery
-     *     service.
-     */
-    public void setServerParams(byte[] serverParams) throws InternalRecoveryServiceException {
-        try {
-            mBinder.setServerParams(serverParams);
-        } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
-        } catch (ServiceSpecificException e) {
-            throw wrapUnexpectedServiceSpecificException(e);
-        }
-    }
-
-    /**
-     * Updates recovery status for given keys. It is used to notify keystore that key was
-     * successfully stored on the server or there were an error. Application can check this value
-     * using {@code getRecoveyStatus}.
-     *
-     * @param packageName Application whose recoverable keys' statuses are to be updated.
-     * @param aliases List of application-specific key aliases. If the array is empty, updates the
-     *     status for all existing recoverable keys.
-     * @param status Status specific to recovery agent.
-     * @throws InternalRecoveryServiceException if an unexpected error occurred in the recovery
-     *     service.
-     */
-    public void setRecoveryStatus(
-            @NonNull String packageName, @Nullable String[] aliases, int status)
-            throws NameNotFoundException, InternalRecoveryServiceException {
-        try {
-            for (String alias : aliases) {
-                mBinder.setRecoveryStatus(alias, status);
-            }
-        } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
-        } catch (ServiceSpecificException e) {
-            throw wrapUnexpectedServiceSpecificException(e);
-        }
-    }
-
-    /**
-     * Returns a {@code Map} from Application's KeyStore key aliases to their recovery status.
-     * Negative status values are reserved for recovery agent specific codes. List of common codes:
-     *
-     * <ul>
-     *   <li>{@link #RECOVERY_STATUS_SYNCED}
-     *   <li>{@link #RECOVERY_STATUS_SYNC_IN_PROGRESS}
-     *   <li>{@link #RECOVERY_STATUS_MISSING_ACCOUNT}
-     *   <li>{@link #RECOVERY_STATUS_PERMANENT_FAILURE}
-     * </ul>
-     *
-     * @return {@code Map} from KeyStore alias to recovery status.
-     * @see #setRecoveryStatus
-     * @throws InternalRecoveryServiceException if an unexpected error occurred in the recovery
-     *     service.
-     */
-    public Map<String, Integer> getRecoveryStatus() throws InternalRecoveryServiceException {
-        try {
-            // IPC doesn't support generic Maps.
-            @SuppressWarnings("unchecked")
-            Map<String, Integer> result =
-                    (Map<String, Integer>) mBinder.getRecoveryStatus();
-            return result;
-        } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
-        } catch (ServiceSpecificException e) {
-            throw wrapUnexpectedServiceSpecificException(e);
-        }
-    }
-
-    /**
-     * Specifies a set of secret types used for end-to-end keystore encryption. Knowing all of them
-     * is necessary to recover data.
-     *
-     * @param secretTypes {@link KeychainProtectionParams#TYPE_LOCKSCREEN} or {@link
-     *     KeychainProtectionParams#TYPE_CUSTOM_PASSWORD}
-     * @throws InternalRecoveryServiceException if an unexpected error occurred in the recovery
-     *     service.
-     */
-    public void setRecoverySecretTypes(
-            @NonNull @KeychainProtectionParams.UserSecretType int[] secretTypes)
-            throws InternalRecoveryServiceException {
-        try {
-            mBinder.setRecoverySecretTypes(secretTypes);
-        } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
-        } catch (ServiceSpecificException e) {
-            throw wrapUnexpectedServiceSpecificException(e);
-        }
-    }
-
-    /**
-     * Defines a set of secret types used for end-to-end keystore encryption. Knowing all of them is
-     * necessary to generate KeychainSnapshot.
-     *
-     * @return list of recovery secret types
-     * @see KeychainSnapshot
-     * @throws InternalRecoveryServiceException if an unexpected error occurred in the recovery
-     *     service.
-     */
-    public @NonNull @KeychainProtectionParams.UserSecretType int[] getRecoverySecretTypes()
-            throws InternalRecoveryServiceException {
-        try {
-            return mBinder.getRecoverySecretTypes();
-        } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
-        } catch (ServiceSpecificException e) {
-            throw wrapUnexpectedServiceSpecificException(e);
-        }
-    }
-
-    /**
-     * Returns a list of recovery secret types, necessary to create a pending recovery snapshot.
-     * When user enters a secret of a pending type {@link #recoverySecretAvailable} should be
-     * called.
-     *
-     * @return list of recovery secret types
-     * @throws InternalRecoveryServiceException if an unexpected error occurred in the recovery
-     *     service.
-     */
-    @NonNull
-    public @KeychainProtectionParams.UserSecretType int[] getPendingRecoverySecretTypes()
-            throws InternalRecoveryServiceException {
-        throw new UnsupportedOperationException();
-    }
-
-    /**
-     * Initializes recovery session and returns a blob with proof of recovery secrets possession.
-     * The method generates symmetric key for a session, which trusted remote device can use to
-     * return recovery key.
-     *
-     * @param verifierPublicKey Encoded {@code java.security.cert.X509Certificate} with Public key
-     * used to create the recovery blob on the source device.
-     * Keystore will verify the certificate using root of trust.
-     * @param vaultParams Must match the parameters in the corresponding field in the recovery blob.
-     *     Used to limit number of guesses.
-     * @param vaultChallenge Data passed from server for this recovery session and used to prevent
-     *     replay attacks
-     * @param secrets Secrets provided by user, the method only uses type and secret fields.
-     * @return The recovery claim. Claim provides a b binary blob with recovery claim. It is
-     *     encrypted with verifierPublicKey and contains a proof of user secrets, session symmetric
-     *     key and parameters necessary to identify the counter with the number of failed recovery
-     *     attempts.
-     * @throws BadCertificateFormatException if the {@code verifierPublicKey} is in an incorrect
-     *     format.
-     * @throws InternalRecoveryServiceException if an unexpected error occurred in the recovery
-     *     service.
-     */
-    @NonNull public RecoveryClaim startRecoverySession(
-            @NonNull byte[] verifierPublicKey,
-            @NonNull byte[] vaultParams,
-            @NonNull byte[] vaultChallenge,
-            @NonNull List<KeychainProtectionParams> secrets)
-            throws BadCertificateFormatException, InternalRecoveryServiceException {
-        try {
-            RecoverySession recoverySession = RecoverySession.newInstance(this);
-            byte[] recoveryClaim =
-                    mBinder.startRecoverySession(
-                            recoverySession.getSessionId(),
-                            verifierPublicKey,
-                            vaultParams,
-                            vaultChallenge,
-                            BackwardsCompat.fromLegacyKeychainProtectionParams(secrets));
-            return new RecoveryClaim(recoverySession, recoveryClaim);
-        } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
-        } catch (ServiceSpecificException e) {
-            if (e.errorCode == ERROR_BAD_CERTIFICATE_FORMAT) {
-                throw new BadCertificateFormatException(e.getMessage());
-            }
-            throw wrapUnexpectedServiceSpecificException(e);
-        }
-    }
-
-    /**
-     * Imports keys.
-     *
-     * @param session Related recovery session, as originally created by invoking
-     *        {@link #startRecoverySession(byte[], byte[], byte[], List)}.
-     * @param recoveryKeyBlob Recovery blob encrypted by symmetric key generated for this session.
-     * @param applicationKeys Application keys. Key material can be decrypted using recoveryKeyBlob
-     *     and session. KeyStore only uses package names from the application info in {@link
-     *     WrappedApplicationKey}. Caller is responsibility to perform certificates check.
-     * @return Map from alias to raw key material.
-     * @throws SessionExpiredException if {@code session} has since been closed.
-     * @throws DecryptionFailedException if unable to decrypt the snapshot.
-     * @throws InternalRecoveryServiceException if an error occurs internal to the recovery service.
-     */
-    public Map<String, byte[]> recoverKeys(
-            @NonNull RecoverySession session,
-            @NonNull byte[] recoveryKeyBlob,
-            @NonNull List<WrappedApplicationKey> applicationKeys)
-            throws SessionExpiredException, DecryptionFailedException,
-            InternalRecoveryServiceException {
-        try {
-            return (Map<String, byte[]>) mBinder.recoverKeys(
-                    session.getSessionId(),
-                    recoveryKeyBlob,
-                    BackwardsCompat.fromLegacyWrappedApplicationKeys(applicationKeys));
-        } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
-        } catch (ServiceSpecificException e) {
-            if (e.errorCode == ERROR_DECRYPTION_FAILED) {
-                throw new DecryptionFailedException(e.getMessage());
-            }
-            if (e.errorCode == ERROR_SESSION_EXPIRED) {
-                throw new SessionExpiredException(e.getMessage());
-            }
-            throw wrapUnexpectedServiceSpecificException(e);
-        }
-    }
-
-    /**
-     * Deletes all data associated with {@code session}. Should not be invoked directly but via
-     * {@link RecoverySession#close()}.
-     *
-     * @hide
-     */
-    void closeSession(RecoverySession session) {
-        try {
-            mBinder.closeSession(session.getSessionId());
-        } catch (RemoteException | ServiceSpecificException e) {
-            Log.e(TAG, "Unexpected error trying to close session", e);
-        }
-    }
-
-    /**
-     * Generates a key called {@code alias} and loads it into the recoverable key store. Returns the
-     * raw material of the key.
-     *
-     * @param alias The key alias.
-     * @throws InternalRecoveryServiceException if an unexpected error occurred in the recovery
-     *     service.
-     * @throws LockScreenRequiredException if the user has not set a lock screen. This is required
-     *     to generate recoverable keys, as the snapshots are encrypted using a key derived from the
-     *     lock screen.
-     */
-    public byte[] generateAndStoreKey(@NonNull String alias)
-            throws InternalRecoveryServiceException, LockScreenRequiredException {
-        throw new UnsupportedOperationException();
-    }
-
-    /**
-     * Removes a key called {@code alias} from the recoverable key store.
-     *
-     * @param alias The key alias.
-     * @throws InternalRecoveryServiceException if an unexpected error occurred in the recovery
-     *     service.
-     */
-    public void removeKey(@NonNull String alias) throws InternalRecoveryServiceException {
-        try {
-            mBinder.removeKey(alias);
-        } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
-        } catch (ServiceSpecificException e) {
-            throw wrapUnexpectedServiceSpecificException(e);
-        }
-    }
-
-    private InternalRecoveryServiceException wrapUnexpectedServiceSpecificException(
-            ServiceSpecificException e) {
-        if (e.errorCode == ERROR_SERVICE_INTERNAL_ERROR) {
-            return new InternalRecoveryServiceException(e.getMessage());
-        }
-
-        // Should never happen. If it does, it's a bug, and we need to update how the method that
-        // called this throws its exceptions.
-        return new InternalRecoveryServiceException("Unexpected error code for method: "
-                + e.errorCode, e);
-    }
-}
diff --git a/android/security/keystore/RecoveryControllerException.java b/android/security/keystore/RecoveryControllerException.java
deleted file mode 100644
index f990c23..0000000
--- a/android/security/keystore/RecoveryControllerException.java
+++ /dev/null
@@ -1,35 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.security.keystore;
-
-import java.security.GeneralSecurityException;
-
-/**
- * @deprecated Use {@link android.security.keystore.recovery.RecoveryController}.
- * @hide
- */
-public abstract class RecoveryControllerException extends GeneralSecurityException {
-    RecoveryControllerException() { }
-
-    RecoveryControllerException(String msg) {
-        super(msg);
-    }
-
-    public RecoveryControllerException(String message, Throwable cause) {
-        super(message, cause);
-    }
-}
diff --git a/android/security/keystore/RecoverySession.java b/android/security/keystore/RecoverySession.java
deleted file mode 100644
index 8a3e06b..0000000
--- a/android/security/keystore/RecoverySession.java
+++ /dev/null
@@ -1,69 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.security.keystore;
-
-import java.security.SecureRandom;
-
-/**
- * @deprecated Use {@link android.security.keystore.recovery.RecoverySession}.
- * @hide
- */
-public class RecoverySession implements AutoCloseable {
-
-    private static final int SESSION_ID_LENGTH_BYTES = 16;
-
-    private final String mSessionId;
-    private final RecoveryController mRecoveryController;
-
-    private RecoverySession(RecoveryController recoveryController, String sessionId) {
-        mRecoveryController = recoveryController;
-        mSessionId = sessionId;
-    }
-
-    /**
-     * A new session, started by {@code recoveryManager}.
-     */
-    static RecoverySession newInstance(RecoveryController recoveryController) {
-        return new RecoverySession(recoveryController, newSessionId());
-    }
-
-    /**
-     * Returns a new random session ID.
-     */
-    private static String newSessionId() {
-        SecureRandom secureRandom = new SecureRandom();
-        byte[] sessionId = new byte[SESSION_ID_LENGTH_BYTES];
-        secureRandom.nextBytes(sessionId);
-        StringBuilder sb = new StringBuilder();
-        for (byte b : sessionId) {
-            sb.append(Byte.toHexString(b, /*upperCase=*/ false));
-        }
-        return sb.toString();
-    }
-
-    /**
-     * An internal session ID, used by the framework to match recovery claims to snapshot responses.
-     */
-    String getSessionId() {
-        return mSessionId;
-    }
-
-    @Override
-    public void close() {
-        mRecoveryController.closeSession(this);
-    }
-}
diff --git a/android/security/keystore/UserPresenceUnavailableException.java b/android/security/keystore/UserPresenceUnavailableException.java
index cf4099e..1b053a5 100644
--- a/android/security/keystore/UserPresenceUnavailableException.java
+++ b/android/security/keystore/UserPresenceUnavailableException.java
@@ -16,13 +16,13 @@
 
 package android.security.keystore;
 
-import java.security.InvalidAlgorithmParameterException;
+import java.security.InvalidKeyException;
 
 /**
  * Indicates the condition that a proof of user-presence was
  * requested but this proof was not presented.
  */
-public class UserPresenceUnavailableException extends InvalidAlgorithmParameterException {
+public class UserPresenceUnavailableException extends InvalidKeyException {
     /**
      * Constructs a {@code UserPresenceUnavailableException} without a detail message or cause.
      */
diff --git a/android/security/keystore/WrappedApplicationKey.java b/android/security/keystore/WrappedApplicationKey.java
deleted file mode 100644
index 2ce8c7d..0000000
--- a/android/security/keystore/WrappedApplicationKey.java
+++ /dev/null
@@ -1,135 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.security.keystore;
-
-import android.annotation.NonNull;
-import android.os.Parcel;
-import android.os.Parcelable;
-
-import com.android.internal.util.Preconditions;
-
-/**
- * @deprecated Use {@link android.security.keystore.recovery.WrappedApplicationKey}.
- * @hide
- */
-public final class WrappedApplicationKey implements Parcelable {
-    private String mAlias;
-    // The only supported format is AES-256 symmetric key.
-    private byte[] mEncryptedKeyMaterial;
-
-    /**
-     * Builder for creating {@link WrappedApplicationKey}.
-     */
-    public static class Builder {
-        private WrappedApplicationKey mInstance = new WrappedApplicationKey();
-
-        /**
-         * Sets Application-specific alias of the key.
-         *
-         * @param alias The alias.
-         * @return This builder.
-         */
-        public Builder setAlias(@NonNull String alias) {
-            mInstance.mAlias = alias;
-            return this;
-        }
-
-        /**
-         * Sets key material encrypted by recovery key.
-         *
-         * @param encryptedKeyMaterial The key material
-         * @return This builder
-         */
-
-        public Builder setEncryptedKeyMaterial(@NonNull byte[] encryptedKeyMaterial) {
-            mInstance.mEncryptedKeyMaterial = encryptedKeyMaterial;
-            return this;
-        }
-
-        /**
-         * Creates a new {@link WrappedApplicationKey} instance.
-         *
-         * @return new instance
-         * @throws NullPointerException if some required fields were not set.
-         */
-        @NonNull public WrappedApplicationKey build() {
-            Preconditions.checkNotNull(mInstance.mAlias);
-            Preconditions.checkNotNull(mInstance.mEncryptedKeyMaterial);
-            return mInstance;
-        }
-    }
-
-    private WrappedApplicationKey() {
-
-    }
-
-    /**
-     * Deprecated - consider using Builder.
-     * @hide
-     */
-    public WrappedApplicationKey(@NonNull String alias, @NonNull byte[] encryptedKeyMaterial) {
-        mAlias = Preconditions.checkNotNull(alias);
-        mEncryptedKeyMaterial = Preconditions.checkNotNull(encryptedKeyMaterial);
-    }
-
-    /**
-     * Application-specific alias of the key.
-     *
-     * @see java.security.KeyStore.aliases
-     */
-    public @NonNull String getAlias() {
-        return mAlias;
-    }
-
-    /** Key material encrypted by recovery key. */
-    public @NonNull byte[] getEncryptedKeyMaterial() {
-        return mEncryptedKeyMaterial;
-    }
-
-    public static final Parcelable.Creator<WrappedApplicationKey> CREATOR =
-            new Parcelable.Creator<WrappedApplicationKey>() {
-                public WrappedApplicationKey createFromParcel(Parcel in) {
-                    return new WrappedApplicationKey(in);
-                }
-
-                public WrappedApplicationKey[] newArray(int length) {
-                    return new WrappedApplicationKey[length];
-                }
-            };
-
-    /**
-     * @hide
-     */
-    @Override
-    public void writeToParcel(Parcel out, int flags) {
-        out.writeString(mAlias);
-        out.writeByteArray(mEncryptedKeyMaterial);
-    }
-
-    /**
-     * @hide
-     */
-    protected WrappedApplicationKey(Parcel in) {
-        mAlias = in.readString();
-        mEncryptedKeyMaterial = in.createByteArray();
-    }
-
-    @Override
-    public int describeContents() {
-        return 0;
-    }
-}
diff --git a/android/security/keystore/recovery/RecoveryController.java b/android/security/keystore/recovery/RecoveryController.java
index 281822a..b84843b 100644
--- a/android/security/keystore/recovery/RecoveryController.java
+++ b/android/security/keystore/recovery/RecoveryController.java
@@ -20,6 +20,7 @@
 import android.annotation.Nullable;
 import android.annotation.RequiresPermission;
 import android.annotation.SystemApi;
+import android.app.KeyguardManager;
 import android.app.PendingIntent;
 import android.content.Context;
 import android.content.pm.PackageManager.NameNotFoundException;
@@ -250,6 +251,16 @@
      */
     public static final int ERROR_INVALID_CERTIFICATE = 28;
 
+
+    /**
+     * Failed because the provided certificate contained serial version which is lower that the
+     * version device is already initialized with. It is not possible to downgrade serial version of
+     * the provided certificate.
+     *
+     * @hide
+     */
+    public static final int ERROR_DOWNGRADE_CERTIFICATE = 29;
+
     private final ILockSettings mBinder;
     private final KeyStore mKeyStore;
 
@@ -278,6 +289,18 @@
     }
 
     /**
+     * Checks whether the recoverable key store is currently available.
+     *
+     * <p>If it returns true, the device must currently be using a screen lock that is supported for
+     * use with the recoverable key store, i.e. AOSP PIN, pattern or password.
+     */
+    @RequiresPermission(android.Manifest.permission.RECOVER_KEYSTORE)
+    public static boolean isRecoverableKeyStoreEnabled(@NonNull Context context) {
+        KeyguardManager keyguardManager = context.getSystemService(KeyguardManager.class);
+        return keyguardManager != null && keyguardManager.isDeviceSecure();
+    }
+
+    /**
      * @deprecated Use {@link #initRecoveryService(String, byte[], byte[])} instead.
      */
     @Deprecated
@@ -340,6 +363,10 @@
                     || e.errorCode == ERROR_INVALID_CERTIFICATE) {
                 throw new CertificateException("Invalid certificate for recovery service", e);
             }
+            if (e.errorCode == ERROR_DOWNGRADE_CERTIFICATE) {
+                throw new CertificateException(
+                        "Downgrading certificate serial version isn't supported.", e);
+            }
             throw wrapUnexpectedServiceSpecificException(e);
         }
     }
diff --git a/android/service/notification/NotificationListenerService.java b/android/service/notification/NotificationListenerService.java
index 3726e66..32737c5 100644
--- a/android/service/notification/NotificationListenerService.java
+++ b/android/service/notification/NotificationListenerService.java
@@ -28,6 +28,7 @@
 import android.app.NotificationChannel;
 import android.app.NotificationChannelGroup;
 import android.app.NotificationManager;
+import android.app.Person;
 import android.app.Service;
 import android.companion.CompanionDeviceManager;
 import android.content.ComponentName;
@@ -1195,13 +1196,13 @@
      */
     private void maybePopulatePeople(Notification notification) {
         if (getContext().getApplicationInfo().targetSdkVersion < Build.VERSION_CODES.P) {
-            ArrayList<Notification.Person> people = notification.extras.getParcelableArrayList(
+            ArrayList<Person> people = notification.extras.getParcelableArrayList(
                     Notification.EXTRA_PEOPLE_LIST);
             if (people != null && people.isEmpty()) {
                 int size = people.size();
                 String[] peopleArray = new String[size];
                 for (int i = 0; i < size; i++) {
-                    Notification.Person person = people.get(i);
+                    Person person = people.get(i);
                     peopleArray[i] = person.resolveToLegacyUri();
                 }
                 notification.extras.putStringArray(Notification.EXTRA_PEOPLE, peopleArray);
diff --git a/android/service/notification/ZenModeConfig.java b/android/service/notification/ZenModeConfig.java
index 3830b7a..7b01f7a 100644
--- a/android/service/notification/ZenModeConfig.java
+++ b/android/service/notification/ZenModeConfig.java
@@ -83,7 +83,8 @@
     private static final int DAY_MINUTES = 24 * 60;
     private static final int ZERO_VALUE_MS = 10 * SECONDS_MS;
 
-    // Default allow categories set in readXml() from default_zen_mode_config.xml, fallback values:
+    // Default allow categories set in readXml() from default_zen_mode_config.xml,
+    // fallback/upgrade values:
     private static final boolean DEFAULT_ALLOW_ALARMS = true;
     private static final boolean DEFAULT_ALLOW_MEDIA = true;
     private static final boolean DEFAULT_ALLOW_SYSTEM = false;
@@ -97,7 +98,7 @@
     private static final int DEFAULT_SUPPRESSED_VISUAL_EFFECTS =
             Policy.getAllSuppressedVisualEffects();
 
-    public static final int XML_VERSION = 6;
+    public static final int XML_VERSION = 7;
     public static final String ZEN_TAG = "zen";
     private static final String ZEN_ATT_VERSION = "version";
     private static final String ZEN_ATT_USER = "user";
@@ -1487,14 +1488,18 @@
     /**
      * Returns a description of the current do not disturb settings from config.
      * - If turned on manually and end time is known, returns end time.
+     * - If turned on manually and end time is on forever until turned off, return null if
+     * describeForeverCondition is false, else return String describing indefinite behavior
      * - If turned on by an automatic rule, returns the automatic rule name.
      * - If on due to an app, returns the app name.
      * - If there's a combination of rules/apps that trigger, then shows the one that will
      *  last the longest if applicable.
-     * @return null if do not disturb is off.
+     * @return null if DND is off or describeForeverCondition is false and
+     * DND is on forever (until turned off)
      */
-    public static String getDescription(Context context, boolean zenOn, ZenModeConfig config) {
-        if (!zenOn) {
+    public static String getDescription(Context context, boolean zenOn, ZenModeConfig config,
+            boolean describeForeverCondition) {
+        if (!zenOn || config == null) {
             return null;
         }
 
@@ -1513,8 +1518,11 @@
             } else {
                 if (id == null) {
                     // Do not disturb manually triggered to remain on forever until turned off
-                    // No subtext
-                    return null;
+                    if (describeForeverCondition) {
+                        return context.getString(R.string.zen_mode_forever);
+                    } else {
+                        return null;
+                    }
                 } else {
                     latestEndTime = tryParseCountdownConditionId(id);
                     if (latestEndTime > 0) {
diff --git a/android/service/textclassifier/TextClassifierService.java b/android/service/textclassifier/TextClassifierService.java
index f1bb72c..b461c0d 100644
--- a/android/service/textclassifier/TextClassifierService.java
+++ b/android/service/textclassifier/TextClassifierService.java
@@ -17,6 +17,7 @@
 package android.service.textclassifier;
 
 import android.Manifest;
+import android.annotation.IntRange;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.SystemApi;
@@ -97,7 +98,8 @@
             Preconditions.checkNotNull(request);
             Preconditions.checkNotNull(callback);
             TextClassifierService.this.onSuggestSelection(
-                    sessionId, request, mCancellationSignal,
+                    request.getText(), request.getStartIndex(), request.getEndIndex(),
+                    TextSelection.Options.from(sessionId, request), mCancellationSignal,
                     new Callback<TextSelection>() {
                         @Override
                         public void onSuccess(TextSelection result) {
@@ -130,7 +132,8 @@
             Preconditions.checkNotNull(request);
             Preconditions.checkNotNull(callback);
             TextClassifierService.this.onClassifyText(
-                    sessionId, request, mCancellationSignal,
+                    request.getText(), request.getStartIndex(), request.getEndIndex(),
+                    TextClassification.Options.from(sessionId, request), mCancellationSignal,
                     new Callback<TextClassification>() {
                         @Override
                         public void onSuccess(TextClassification result) {
@@ -161,7 +164,8 @@
             Preconditions.checkNotNull(request);
             Preconditions.checkNotNull(callback);
             TextClassifierService.this.onGenerateLinks(
-                    sessionId, request, mCancellationSignal,
+                    request.getText(), TextLinks.Options.from(sessionId, request),
+                    mCancellationSignal,
                     new Callback<TextLinks>() {
                         @Override
                         public void onSuccess(TextLinks result) {
@@ -234,6 +238,25 @@
             @NonNull CancellationSignal cancellationSignal,
             @NonNull Callback<TextSelection> callback);
 
+    // TODO: Remove once apps can build against the latest sdk.
+    /** @hide */
+    public void onSuggestSelection(
+            @NonNull CharSequence text,
+            @IntRange(from = 0) int selectionStartIndex,
+            @IntRange(from = 0) int selectionEndIndex,
+            @Nullable TextSelection.Options options,
+            @NonNull CancellationSignal cancellationSignal,
+            @NonNull Callback<TextSelection> callback) {
+        final TextClassificationSessionId sessionId = options.getSessionId();
+        final TextSelection.Request request = options.getRequest() != null
+                ? options.getRequest()
+                : new TextSelection.Request.Builder(
+                        text, selectionStartIndex, selectionEndIndex)
+                        .setDefaultLocales(options.getDefaultLocales())
+                        .build();
+        onSuggestSelection(sessionId, request, cancellationSignal, callback);
+    }
+
     /**
      * Classifies the specified text and returns a {@link TextClassification} object that can be
      * used to generate a widget for handling the classified text.
@@ -249,6 +272,26 @@
             @NonNull CancellationSignal cancellationSignal,
             @NonNull Callback<TextClassification> callback);
 
+    // TODO: Remove once apps can build against the latest sdk.
+    /** @hide */
+    public void onClassifyText(
+            @NonNull CharSequence text,
+            @IntRange(from = 0) int startIndex,
+            @IntRange(from = 0) int endIndex,
+            @Nullable TextClassification.Options options,
+            @NonNull CancellationSignal cancellationSignal,
+            @NonNull Callback<TextClassification> callback) {
+        final TextClassificationSessionId sessionId = options.getSessionId();
+        final TextClassification.Request request = options.getRequest() != null
+                ? options.getRequest()
+                : new TextClassification.Request.Builder(
+                        text, startIndex, endIndex)
+                        .setDefaultLocales(options.getDefaultLocales())
+                        .setReferenceTime(options.getReferenceTime())
+                        .build();
+        onClassifyText(sessionId, request, cancellationSignal, callback);
+    }
+
     /**
      * Generates and returns a {@link TextLinks} that may be applied to the text to annotate it with
      * links information.
@@ -264,6 +307,23 @@
             @NonNull CancellationSignal cancellationSignal,
             @NonNull Callback<TextLinks> callback);
 
+    // TODO: Remove once apps can build against the latest sdk.
+    /** @hide */
+    public void onGenerateLinks(
+            @NonNull CharSequence text,
+            @Nullable TextLinks.Options options,
+            @NonNull CancellationSignal cancellationSignal,
+            @NonNull Callback<TextLinks> callback) {
+        final TextClassificationSessionId sessionId = options.getSessionId();
+        final TextLinks.Request request = options.getRequest() != null
+                ? options.getRequest()
+                : new TextLinks.Request.Builder(text)
+                        .setDefaultLocales(options.getDefaultLocales())
+                        .setEntityConfig(options.getEntityConfig())
+                        .build();
+        onGenerateLinks(sessionId, request, cancellationSignal, callback);
+    }
+
     /**
      * Writes the selection event.
      * This is called when a selection event occurs. e.g. user changed selection; or smart selection
@@ -283,17 +343,17 @@
      * @param context the text classification context
      * @param sessionId the session's Id
      */
-    public abstract void onCreateTextClassificationSession(
+    public void onCreateTextClassificationSession(
             @NonNull TextClassificationContext context,
-            @NonNull TextClassificationSessionId sessionId);
+            @NonNull TextClassificationSessionId sessionId) {}
 
     /**
      * Destroys the text classification session identified by the specified sessionId.
      *
      * @param sessionId the id of the session to destroy
      */
-    public abstract void onDestroyTextClassificationSession(
-            @NonNull TextClassificationSessionId sessionId);
+    public void onDestroyTextClassificationSession(
+            @NonNull TextClassificationSessionId sessionId) {}
 
     /**
      * Returns a TextClassifier that runs in this service's process.
diff --git a/android/service/wallpaper/WallpaperService.java b/android/service/wallpaper/WallpaperService.java
index a132730..7f75f0a 100644
--- a/android/service/wallpaper/WallpaperService.java
+++ b/android/service/wallpaper/WallpaperService.java
@@ -813,7 +813,7 @@
                     }
                     final int relayoutResult = mSession.relayout(
                         mWindow, mWindow.mSeq, mLayout, mWidth, mHeight,
-                            View.VISIBLE, 0, mWinFrame, mOverscanInsets, mContentInsets,
+                            View.VISIBLE, 0, -1, mWinFrame, mOverscanInsets, mContentInsets,
                             mVisibleInsets, mStableInsets, mOutsets, mBackdropFrame,
                             mDisplayCutout, mMergedConfiguration, mSurfaceHolder.mSurface);
 
diff --git a/android/support/v4/media/session/MediaControllerCompat.java b/android/support/v4/media/session/MediaControllerCompat.java
index 5e6f4ea..4a4ad32 100644
--- a/android/support/v4/media/session/MediaControllerCompat.java
+++ b/android/support/v4/media/session/MediaControllerCompat.java
@@ -667,13 +667,13 @@
     public static abstract class Callback implements IBinder.DeathRecipient {
         private final Object mCallbackObj;
         MessageHandler mHandler;
-        boolean mHasExtraCallback;
+        IMediaControllerCallback mIControllerCallback;
 
         public Callback() {
             if (android.os.Build.VERSION.SDK_INT >= 21) {
                 mCallbackObj = MediaControllerCompatApi21.createCallback(new StubApi21(this));
             } else {
-                mCallbackObj = new StubCompat(this);
+                mCallbackObj = mIControllerCallback = new StubCompat(this);
             }
         }
 
@@ -789,6 +789,14 @@
         public void onShuffleModeChanged(@PlaybackStateCompat.ShuffleMode int shuffleMode) {
         }
 
+        /**
+         * @hide
+         */
+        @RestrictTo(LIBRARY)
+        public IMediaControllerCallback getIControllerCallback() {
+            return mIControllerCallback;
+        }
+
         @Override
         public void binderDied() {
             onSessionDestroyed();
@@ -837,7 +845,8 @@
             public void onSessionEvent(String event, Bundle extras) {
                 MediaControllerCompat.Callback callback = mCallback.get();
                 if (callback != null) {
-                    if (callback.mHasExtraCallback && android.os.Build.VERSION.SDK_INT < 23) {
+                    if (callback.mIControllerCallback != null
+                            && android.os.Build.VERSION.SDK_INT < 23) {
                         // Ignore. ExtraCallback will handle this.
                     } else {
                         callback.onSessionEvent(event, extras);
@@ -849,7 +858,7 @@
             public void onPlaybackStateChanged(Object stateObj) {
                 MediaControllerCompat.Callback callback = mCallback.get();
                 if (callback != null) {
-                    if (callback.mHasExtraCallback) {
+                    if (callback.mIControllerCallback != null) {
                         // Ignore. ExtraCallback will handle this.
                     } else {
                         callback.onPlaybackStateChanged(
@@ -1944,7 +1953,7 @@
             if (mExtraBinder != null) {
                 ExtraCallback extraCallback = new ExtraCallback(callback);
                 mCallbackMap.put(callback, extraCallback);
-                callback.mHasExtraCallback = true;
+                callback.mIControllerCallback = extraCallback;
                 try {
                     mExtraBinder.registerCallbackListener(extraCallback);
                 } catch (RemoteException e) {
@@ -1952,7 +1961,7 @@
                 }
             } else {
                 synchronized (mPendingCallbacks) {
-                    callback.mHasExtraCallback = false;
+                    callback.mIControllerCallback = null;
                     mPendingCallbacks.add(callback);
                 }
             }
@@ -1965,7 +1974,7 @@
                 try {
                     ExtraCallback extraCallback = mCallbackMap.remove(callback);
                     if (extraCallback != null) {
-                        callback.mHasExtraCallback = false;
+                        callback.mIControllerCallback = null;
                         mExtraBinder.unregisterCallbackListener(extraCallback);
                     }
                 } catch (RemoteException e) {
@@ -2173,7 +2182,7 @@
                 for (Callback callback : mPendingCallbacks) {
                     ExtraCallback extraCallback = new ExtraCallback(callback);
                     mCallbackMap.put(callback, extraCallback);
-                    callback.mHasExtraCallback = true;
+                    callback.mIControllerCallback = extraCallback;
                     try {
                         mExtraBinder.registerCallbackListener(extraCallback);
                     } catch (RemoteException e) {
diff --git a/android/support/v4/media/session/PlaybackStateCompat.java b/android/support/v4/media/session/PlaybackStateCompat.java
index e6420ea..b9c51ca 100644
--- a/android/support/v4/media/session/PlaybackStateCompat.java
+++ b/android/support/v4/media/session/PlaybackStateCompat.java
@@ -15,7 +15,6 @@
  */
 package android.support.v4.media.session;
 
-
 import static androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP;
 
 import android.os.Build;
diff --git a/android/system/Os.java b/android/system/Os.java
index 404b822..6301ff9 100644
--- a/android/system/Os.java
+++ b/android/system/Os.java
@@ -186,17 +186,6 @@
     public static int getgid() { return Libcore.os.getgid(); }
 
     /**
-     * See <a href="http://man7.org/linux/man-pages/man2/getgroups.2.html">getgroups(2)</a>.
-     *
-     * <p>Should the number of groups change during the execution of this call, the call may
-     *    return an arbitrary subset. This may be worth reconsidering should this be exposed
-     *    as public API.
-     *
-     * @hide
-     */
-    public static int[] getgroups() throws ErrnoException { return Libcore.os.getgroups(); }
-
-    /**
      * See <a href="http://man7.org/linux/man-pages/man3/getenv.3.html">getenv(3)</a>.
      */
     public static String getenv(String name) { return Libcore.os.getenv(name); }
@@ -507,13 +496,6 @@
     public static void setgid(int gid) throws ErrnoException { Libcore.os.setgid(gid); }
 
     /**
-     * See <a href="http://man7.org/linux/man-pages/man2/setgroups.2.html">setgroups(2)</a>.
-     *
-     * @hide
-     */
-    public static void setgroups(int[] gids) throws ErrnoException { Libcore.os.setgroups(gids); }
-
-    /**
      * See <a href="http://man7.org/linux/man-pages/man2/setpgid.2.html">setpgid(2)</a>.
      */
     /** @hide */ public static void setpgid(int pid, int pgid) throws ErrnoException { Libcore.os.setpgid(pid, pgid); }
diff --git a/android/telecom/Connection.java b/android/telecom/Connection.java
index 36333e4..3bf951d 100644
--- a/android/telecom/Connection.java
+++ b/android/telecom/Connection.java
@@ -2600,7 +2600,6 @@
     }
 
     /**
-     *
      * Request audio routing to a specific bluetooth device. Calling this method may result in
      * the device routing audio to a different bluetooth device than the one specified if the
      * bluetooth stack is unable to route audio to the requested device.
@@ -2611,13 +2610,13 @@
      * Used by self-managed {@link ConnectionService}s which wish to use bluetooth audio for a
      * self-managed {@link Connection} (see {@link PhoneAccount#CAPABILITY_SELF_MANAGED}.)
      * <p>
-     * See also {@link InCallService#requestBluetoothAudio(String)}
-     * @param bluetoothAddress The address of the bluetooth device to connect to, as returned by
-     *                         {@link BluetoothDevice#getAddress()}.
+     * See also {@link InCallService#requestBluetoothAudio(BluetoothDevice)}
+     * @param bluetoothDevice The bluetooth device to connect to.
      */
-    public void requestBluetoothAudio(@NonNull String bluetoothAddress) {
+    public void requestBluetoothAudio(@NonNull BluetoothDevice bluetoothDevice) {
         for (Listener l : mListeners) {
-            l.onAudioRouteChanged(this, CallAudioState.ROUTE_BLUETOOTH, bluetoothAddress);
+            l.onAudioRouteChanged(this, CallAudioState.ROUTE_BLUETOOTH,
+                    bluetoothDevice.getAddress());
         }
     }
 
diff --git a/android/telecom/InCallService.java b/android/telecom/InCallService.java
index af65c65..bd25ab2 100644
--- a/android/telecom/InCallService.java
+++ b/android/telecom/InCallService.java
@@ -428,12 +428,11 @@
      * A list of available devices can be obtained via
      * {@link CallAudioState#getSupportedBluetoothDevices()}
      *
-     * @param bluetoothAddress The address of the bluetooth device to connect to, as returned by
-     *                         {@link BluetoothDevice#getAddress()}.
+     * @param bluetoothDevice The bluetooth device to connect to.
      */
-    public final void requestBluetoothAudio(@NonNull String bluetoothAddress) {
+    public final void requestBluetoothAudio(@NonNull BluetoothDevice bluetoothDevice) {
         if (mPhone != null) {
-            mPhone.requestBluetoothAudio(bluetoothAddress);
+            mPhone.requestBluetoothAudio(bluetoothDevice.getAddress());
         }
     }
 
diff --git a/android/telecom/PhoneAccount.java b/android/telecom/PhoneAccount.java
index 95eb14a..b3a3bf2 100644
--- a/android/telecom/PhoneAccount.java
+++ b/android/telecom/PhoneAccount.java
@@ -129,6 +129,9 @@
      * <p>
      * By default, Self-Managed {@link PhoneAccount}s do not log their calls to the call log.
      * Setting this extra to {@code true} provides a means for them to log their calls.
+     * <p>
+     * Note: Only calls where the {@link Call.Details#getHandle()} {@link Uri#getScheme()} is
+     * {@link #SCHEME_SIP} or {@link #SCHEME_TEL} will be logged at the current time.
      */
     public static final String EXTRA_LOG_SELF_MANAGED_CALLS =
             "android.telecom.extra.LOG_SELF_MANAGED_CALLS";
diff --git a/android/telephony/AccessNetworkConstants.java b/android/telephony/AccessNetworkConstants.java
index cac9f2b..3b773b3 100644
--- a/android/telephony/AccessNetworkConstants.java
+++ b/android/telephony/AccessNetworkConstants.java
@@ -16,8 +16,6 @@
 
 package android.telephony;
 
-import android.annotation.SystemApi;
-
 /**
  * Contains access network related constants.
  */
@@ -39,7 +37,6 @@
      * Wireless transportation type
      * @hide
      */
-    @SystemApi
     public static final class TransportType {
         /** Wireless Wide Area Networks (i.e. Cellular) */
         public static final int WWAN = 1;
diff --git a/android/telephony/CarrierConfigManager.java b/android/telephony/CarrierConfigManager.java
index 4683161..e244131 100644
--- a/android/telephony/CarrierConfigManager.java
+++ b/android/telephony/CarrierConfigManager.java
@@ -1684,6 +1684,14 @@
             "data_warning_threshold_bytes_long";
 
     /**
+     * Controls if the device should automatically notify the user as they reach
+     * their cellular data warning. When set to {@code false} the carrier is
+     * expected to have implemented their own notification mechanism.
+     */
+    public static final String KEY_DATA_WARNING_NOTIFICATION_BOOL =
+            "data_warning_notification_bool";
+
+    /**
      * Controls the cellular data limit.
      * <p>
      * If the user uses more than this amount of data in their billing cycle, as defined by
@@ -1698,6 +1706,22 @@
             "data_limit_threshold_bytes_long";
 
     /**
+     * Controls if the device should automatically notify the user as they reach
+     * their cellular data limit. When set to {@code false} the carrier is
+     * expected to have implemented their own notification mechanism.
+     */
+    public static final String KEY_DATA_LIMIT_NOTIFICATION_BOOL =
+            "data_limit_notification_bool";
+
+    /**
+     * Controls if the device should automatically notify the user when rapid
+     * cellular data usage is observed. When set to {@code false} the carrier is
+     * expected to have implemented their own notification mechanism.
+     */
+    public static final String KEY_DATA_RAPID_NOTIFICATION_BOOL =
+            "data_rapid_notification_bool";
+
+    /**
      * Offset to be reduced from rsrp threshold while calculating signal strength level.
      * @hide
      */
@@ -1954,7 +1978,7 @@
         sDefaults.putBoolean(KEY_CARRIER_FORCE_DISABLE_ETWS_CMAS_TEST_BOOL, false);
         sDefaults.putBoolean(KEY_CARRIER_VOLTE_PROVISIONING_REQUIRED_BOOL, false);
         sDefaults.putBoolean(KEY_CARRIER_VOLTE_OVERRIDE_WFC_PROVISIONING_BOOL, false);
-        sDefaults.putBoolean(KEY_CARRIER_VOLTE_TTY_SUPPORTED_BOOL, false);
+        sDefaults.putBoolean(KEY_CARRIER_VOLTE_TTY_SUPPORTED_BOOL, true);
         sDefaults.putBoolean(KEY_CARRIER_ALLOW_TURNOFF_IMS_BOOL, true);
         sDefaults.putBoolean(KEY_CARRIER_IMS_GBA_REQUIRED_BOOL, false);
         sDefaults.putBoolean(KEY_CARRIER_INSTANT_LETTERING_AVAILABLE_BOOL, false);
@@ -2165,7 +2189,10 @@
 
         sDefaults.putInt(KEY_MONTHLY_DATA_CYCLE_DAY_INT, DATA_CYCLE_USE_PLATFORM_DEFAULT);
         sDefaults.putLong(KEY_DATA_WARNING_THRESHOLD_BYTES_LONG, DATA_CYCLE_USE_PLATFORM_DEFAULT);
+        sDefaults.putBoolean(KEY_DATA_WARNING_NOTIFICATION_BOOL, true);
         sDefaults.putLong(KEY_DATA_LIMIT_THRESHOLD_BYTES_LONG, DATA_CYCLE_USE_PLATFORM_DEFAULT);
+        sDefaults.putBoolean(KEY_DATA_LIMIT_NOTIFICATION_BOOL, true);
+        sDefaults.putBoolean(KEY_DATA_RAPID_NOTIFICATION_BOOL, true);
 
         // Rat families: {GPRS, EDGE}, {EVDO, EVDO_A, EVDO_B}, {UMTS, HSPA, HSDPA, HSUPA, HSPAP},
         // {LTE, LTE_CA}
@@ -2187,7 +2214,7 @@
         sDefaults.putStringArray(KEY_FILTERED_CNAP_NAMES_STRING_ARRAY, null);
         sDefaults.putBoolean(KEY_EDITABLE_WFC_ROAMING_MODE_BOOL, false);
         sDefaults.putBoolean(KEY_STK_DISABLE_LAUNCH_BROWSER_BOOL, false);
-        sDefaults.putBoolean(KEY_PERSIST_LPP_MODE_BOOL, false);
+        sDefaults.putBoolean(KEY_PERSIST_LPP_MODE_BOOL, true);
         sDefaults.putStringArray(KEY_CARRIER_WIFI_STRING_ARRAY, null);
         sDefaults.putInt(KEY_PREF_NETWORK_NOTIFICATION_DELAY_INT, -1);
         sDefaults.putInt(KEY_EMERGENCY_NOTIFICATION_DELAY_INT, -1);
diff --git a/android/telephony/NetworkRegistrationState.java b/android/telephony/NetworkRegistrationState.java
index bba779d..0e2e0ce 100644
--- a/android/telephony/NetworkRegistrationState.java
+++ b/android/telephony/NetworkRegistrationState.java
@@ -18,7 +18,6 @@
 
 import android.annotation.IntDef;
 import android.annotation.Nullable;
-import android.annotation.SystemApi;
 import android.os.Parcel;
 import android.os.Parcelable;
 
@@ -31,7 +30,6 @@
  * Description of a mobile network registration state
  * @hide
  */
-@SystemApi
 public class NetworkRegistrationState implements Parcelable {
     /**
      * Network domain
diff --git a/android/telephony/NetworkService.java b/android/telephony/NetworkService.java
index 35682a7..b431590 100644
--- a/android/telephony/NetworkService.java
+++ b/android/telephony/NetworkService.java
@@ -17,7 +17,6 @@
 package android.telephony;
 
 import android.annotation.CallSuper;
-import android.annotation.SystemApi;
 import android.app.Service;
 import android.content.Intent;
 import android.os.Handler;
@@ -47,7 +46,6 @@
  * </service>
  * @hide
  */
-@SystemApi
 public abstract class NetworkService extends Service {
 
     private final String TAG = NetworkService.class.getSimpleName();
@@ -206,8 +204,10 @@
         }
     }
 
-    /** @hide */
-    protected NetworkService() {
+    /**
+     * Default constructor.
+     */
+    public NetworkService() {
         mHandlerThread = new HandlerThread(TAG);
         mHandlerThread.start();
 
diff --git a/android/telephony/NetworkServiceCallback.java b/android/telephony/NetworkServiceCallback.java
index dbad02f..ad3b00f 100644
--- a/android/telephony/NetworkServiceCallback.java
+++ b/android/telephony/NetworkServiceCallback.java
@@ -17,7 +17,6 @@
 package android.telephony;
 
 import android.annotation.IntDef;
-import android.annotation.SystemApi;
 import android.os.RemoteException;
 import android.telephony.NetworkService.NetworkServiceProvider;
 
@@ -33,7 +32,6 @@
  *
  * @hide
  */
-@SystemApi
 public class NetworkServiceCallback {
 
     private static final String mTag = NetworkServiceCallback.class.getSimpleName();
diff --git a/android/telephony/ServiceState.java b/android/telephony/ServiceState.java
index e971d08..8ffdb21 100644
--- a/android/telephony/ServiceState.java
+++ b/android/telephony/ServiceState.java
@@ -17,7 +17,6 @@
 package android.telephony;
 
 import android.annotation.IntDef;
-import android.annotation.SystemApi;
 import android.annotation.TestApi;
 import android.os.Bundle;
 import android.os.Parcel;
@@ -977,11 +976,13 @@
     }
 
     /** @hide */
+    @TestApi
     public void setCellBandwidths(int[] bandwidths) {
         mCellBandwidths = bandwidths;
     }
 
     /** @hide */
+    @TestApi
     public void setChannelNumber(int channelNumber) {
         mChannelNumber = channelNumber;
     }
@@ -1172,6 +1173,7 @@
     }
 
     /** @hide */
+    @TestApi
     public void setRilVoiceRadioTechnology(int rt) {
         if (rt == RIL_RADIO_TECHNOLOGY_LTE_CA) {
             rt = RIL_RADIO_TECHNOLOGY_LTE;
@@ -1181,6 +1183,7 @@
     }
 
     /** @hide */
+    @TestApi
     public void setRilDataRadioTechnology(int rt) {
         if (rt == RIL_RADIO_TECHNOLOGY_LTE_CA) {
             rt = RIL_RADIO_TECHNOLOGY_LTE;
@@ -1530,7 +1533,6 @@
      * @return List of registration states
      * @hide
      */
-    @SystemApi
     public List<NetworkRegistrationState> getNetworkRegistrationStates() {
         synchronized (mNetworkRegistrationStates) {
             return new ArrayList<>(mNetworkRegistrationStates);
@@ -1544,7 +1546,6 @@
      * @return List of registration states.
      * @hide
      */
-    @SystemApi
     public List<NetworkRegistrationState> getNetworkRegistrationStates(int transportType) {
         List<NetworkRegistrationState> list = new ArrayList<>();
 
@@ -1567,7 +1568,6 @@
      * @return The matching NetworkRegistrationState.
      * @hide
      */
-    @SystemApi
     public NetworkRegistrationState getNetworkRegistrationStates(int transportType, int domain) {
         synchronized (mNetworkRegistrationStates) {
             for (NetworkRegistrationState networkRegistrationState : mNetworkRegistrationStates) {
diff --git a/android/telephony/SubscriptionManager.java b/android/telephony/SubscriptionManager.java
index 754fe68..a9389be 100644
--- a/android/telephony/SubscriptionManager.java
+++ b/android/telephony/SubscriptionManager.java
@@ -477,6 +477,9 @@
      * <p>
      * Contains {@link #EXTRA_SUBSCRIPTION_INDEX} to indicate which subscription
      * the user is interested in.
+     * <p>
+     * Receivers should protect themselves by checking that the sender holds the
+     * {@code android.permission.MANAGE_SUBSCRIPTION_PLANS} permission.
      */
     @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
     @SystemApi
@@ -1719,6 +1722,8 @@
      * </ul>
      *
      * @param subId the subscriber this relationship applies to
+     * @throws SecurityException if the caller doesn't meet the requirements
+     *             outlined above.
      */
     @SystemApi
     public @NonNull List<SubscriptionPlan> getSubscriptionPlans(int subId) {
@@ -1744,10 +1749,13 @@
      * {@link CarrierConfigManager#KEY_CONFIG_PLANS_PACKAGE_OVERRIDE_STRING}.
      * </ul>
      *
-     * @param subId the subscriber this relationship applies to
+     * @param subId the subscriber this relationship applies to. An empty list
+     *            may be sent to clear any existing plans.
      * @param plans the list of plans. The first plan is always the primary and
      *            most important plan. Any additional plans are secondary and
      *            may not be displayed or used by decision making logic.
+     * @throws SecurityException if the caller doesn't meet the requirements
+     *             outlined above.
      */
     @SystemApi
     public void setSubscriptionPlans(int subId, @NonNull List<SubscriptionPlan> plans) {
@@ -1788,6 +1796,8 @@
      *            be automatically cleared, or {@code 0} to leave in the
      *            requested state until explicitly cleared, or the next reboot,
      *            whichever happens first.
+     * @throws SecurityException if the caller doesn't meet the requirements
+     *             outlined above.
      */
     @SystemApi
     public void setSubscriptionOverrideUnmetered(int subId, boolean overrideUnmetered,
@@ -1822,6 +1832,8 @@
      *            be automatically cleared, or {@code 0} to leave in the
      *            requested state until explicitly cleared, or the next reboot,
      *            whichever happens first.
+     * @throws SecurityException if the caller doesn't meet the requirements
+     *             outlined above.
      */
     @SystemApi
     public void setSubscriptionOverrideCongested(int subId, boolean overrideCongested,
diff --git a/android/telephony/SubscriptionPlan.java b/android/telephony/SubscriptionPlan.java
index 4ffb70b..e8bbe42 100644
--- a/android/telephony/SubscriptionPlan.java
+++ b/android/telephony/SubscriptionPlan.java
@@ -24,7 +24,7 @@
 import android.annotation.SystemApi;
 import android.os.Parcel;
 import android.os.Parcelable;
-import android.util.Pair;
+import android.util.Range;
 import android.util.RecurrenceRule;
 
 import com.android.internal.util.Preconditions;
@@ -209,7 +209,7 @@
      * any recurrence rules. The iterator starts from the currently active cycle
      * and walks backwards through time.
      */
-    public Iterator<Pair<ZonedDateTime, ZonedDateTime>> cycleIterator() {
+    public Iterator<Range<ZonedDateTime>> cycleIterator() {
         return cycleRule.cycleIterator();
     }
 
@@ -227,6 +227,9 @@
         /**
          * Start defining a {@link SubscriptionPlan} that covers a very specific
          * window of time, and never automatically recurs.
+         *
+         * @param start The exact time at which the plan starts.
+         * @param end The exact time at which the plan ends.
          */
         public static Builder createNonrecurring(ZonedDateTime start, ZonedDateTime end) {
             if (!end.isAfter(start)) {
@@ -237,28 +240,43 @@
         }
 
         /**
-         * Start defining a {@link SubscriptionPlan} that will recur
-         * automatically every month. It will always recur on the same day of a
-         * particular month. When a particular month ends before the defined
-         * recurrence day, the plan will recur on the last instant of that
-         * month.
+         * Start defining a {@link SubscriptionPlan} that starts at a specific
+         * time, and automatically recurs after each specific period of time,
+         * repeating indefinitely.
+         * <p>
+         * When the given period is set to exactly one month, the plan will
+         * always recur on the day of the month defined by
+         * {@link ZonedDateTime#getDayOfMonth()}. When a particular month ends
+         * before this day, the plan will recur on the last possible instant of
+         * that month.
+         *
+         * @param start The exact time at which the plan starts.
+         * @param period The period after which the plan automatically recurs.
          */
+        public static Builder createRecurring(ZonedDateTime start, Period period) {
+            if (period.isZero() || period.isNegative()) {
+                throw new IllegalArgumentException("Period " + period + " must be positive");
+            }
+            return new Builder(start, null, period);
+        }
+
+        /** {@hide} */
+        @SystemApi
+        @Deprecated
         public static Builder createRecurringMonthly(ZonedDateTime start) {
             return new Builder(start, null, Period.ofMonths(1));
         }
 
-        /**
-         * Start defining a {@link SubscriptionPlan} that will recur
-         * automatically every week.
-         */
+        /** {@hide} */
+        @SystemApi
+        @Deprecated
         public static Builder createRecurringWeekly(ZonedDateTime start) {
             return new Builder(start, null, Period.ofDays(7));
         }
 
-        /**
-         * Start defining a {@link SubscriptionPlan} that will recur
-         * automatically every day.
-         */
+        /** {@hide} */
+        @SystemApi
+        @Deprecated
         public static Builder createRecurringDaily(ZonedDateTime start) {
             return new Builder(start, null, Period.ofDays(1));
         }
diff --git a/android/telephony/TelephonyManager.java b/android/telephony/TelephonyManager.java
index e15d35b..956a5b1 100644
--- a/android/telephony/TelephonyManager.java
+++ b/android/telephony/TelephonyManager.java
@@ -2968,7 +2968,7 @@
             IPhoneSubInfo info = getSubscriberInfo();
             if (info == null)
                 return null;
-            return info.getGroupIdLevel1(mContext.getOpPackageName());
+            return info.getGroupIdLevel1ForSubscriber(getSubId(), mContext.getOpPackageName());
         } catch (RemoteException ex) {
             return null;
         } catch (NullPointerException ex) {
@@ -5156,7 +5156,12 @@
      * {@link #AUTHTYPE_EAP_SIM}
      * @param data authentication challenge data, base64 encoded.
      * See 3GPP TS 31.102 7.1.2 for more details.
-     * @return the response of authentication, or null if not available
+     * @return the response of authentication. This value will be null in the following cases:
+     *   Authentication error, incorrect MAC
+     *   Authentication error, security context not supported
+     *   Key freshness failure
+     *   Authentication error, no memory space available
+     *   Authentication error, no memory space available in EFMUK
      */
     // TODO(b/73660190): This should probably require MODIFY_PHONE_STATE, not
     // READ_PRIVILEGED_PHONE_STATE. It certainly shouldn't reference the permission in Javadoc since
@@ -5177,7 +5182,13 @@
      * {@link #AUTHTYPE_EAP_SIM}
      * @param data authentication challenge data, base64 encoded.
      * See 3GPP TS 31.102 7.1.2 for more details.
-     * @return the response of authentication, or null if not available
+     * @return the response of authentication. This value will be null in the following cases only
+     * (see 3GPP TS 31.102 7.3.1):
+     *   Authentication error, incorrect MAC
+     *   Authentication error, security context not supported
+     *   Key freshness failure
+     *   Authentication error, no memory space available
+     *   Authentication error, no memory space available in EFMUK
      * @hide
      */
     public String getIccAuthentication(int subId, int appType, int authType, String data) {
diff --git a/android/telephony/data/DataCallResponse.java b/android/telephony/data/DataCallResponse.java
index 25f5133..acc2470 100644
--- a/android/telephony/data/DataCallResponse.java
+++ b/android/telephony/data/DataCallResponse.java
@@ -19,7 +19,6 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
-import android.annotation.SystemApi;
 import android.net.LinkAddress;
 import android.os.Parcel;
 import android.os.Parcelable;
@@ -34,7 +33,6 @@
  *
  * @hide
  */
-@SystemApi
 public final class DataCallResponse implements Parcelable {
     private final int mStatus;
     private final int mSuggestedRetryTime;
diff --git a/android/telephony/data/DataProfile.java b/android/telephony/data/DataProfile.java
index e8597b2..dd274c5 100644
--- a/android/telephony/data/DataProfile.java
+++ b/android/telephony/data/DataProfile.java
@@ -16,7 +16,6 @@
 
 package android.telephony.data;
 
-import android.annotation.SystemApi;
 import android.os.Build;
 import android.os.Parcel;
 import android.os.Parcelable;
@@ -30,7 +29,6 @@
  *
  * @hide
  */
-@SystemApi
 public final class DataProfile implements Parcelable {
 
     // The types indicating the data profile is used on GSM (3GPP) or CDMA (3GPP2) network.
diff --git a/android/telephony/data/DataService.java b/android/telephony/data/DataService.java
index e8c1cb1..0835f7d 100644
--- a/android/telephony/data/DataService.java
+++ b/android/telephony/data/DataService.java
@@ -20,7 +20,6 @@
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
-import android.annotation.SystemApi;
 import android.app.Service;
 import android.content.Intent;
 import android.net.LinkProperties;
@@ -55,7 +54,6 @@
  * </service>
  * @hide
  */
-@SystemApi
 public abstract class DataService extends Service {
     private static final String TAG = DataService.class.getSimpleName();
 
@@ -429,8 +427,10 @@
         }
     }
 
-    /** @hide */
-    protected DataService() {
+    /**
+     * Default constructor.
+     */
+    public DataService() {
         mHandlerThread = new HandlerThread(TAG);
         mHandlerThread.start();
 
diff --git a/android/telephony/data/DataServiceCallback.java b/android/telephony/data/DataServiceCallback.java
index 4af31b5..bff8260 100644
--- a/android/telephony/data/DataServiceCallback.java
+++ b/android/telephony/data/DataServiceCallback.java
@@ -17,7 +17,6 @@
 package android.telephony.data;
 
 import android.annotation.IntDef;
-import android.annotation.SystemApi;
 import android.net.LinkProperties;
 import android.os.RemoteException;
 import android.telephony.Rlog;
@@ -35,7 +34,6 @@
  *
  * @hide
  */
-@SystemApi
 public class DataServiceCallback {
 
     private static final String TAG = DataServiceCallback.class.getSimpleName();
@@ -125,7 +123,6 @@
      *
      * @param result The result code. Must be one of the {@link ResultCode}.
      */
-    @SystemApi
     public void onSetDataProfileComplete(@ResultCode int result) {
         IDataServiceCallback callback = mCallback.get();
         if (callback != null) {
diff --git a/android/telephony/ims/stub/ImsFeatureConfiguration.java b/android/telephony/ims/stub/ImsFeatureConfiguration.java
index 2f52c0a..dfb6e2c 100644
--- a/android/telephony/ims/stub/ImsFeatureConfiguration.java
+++ b/android/telephony/ims/stub/ImsFeatureConfiguration.java
@@ -77,6 +77,11 @@
             result = 31 * result + featureType;
             return result;
         }
+
+        @Override
+        public String toString() {
+            return "{s=" + slotId + ", f=" + featureType + "}";
+        }
     }
 
     /**
diff --git a/android/telephony/mbms/DownloadRequest.java b/android/telephony/mbms/DownloadRequest.java
index 602c796..9e3302b 100644
--- a/android/telephony/mbms/DownloadRequest.java
+++ b/android/telephony/mbms/DownloadRequest.java
@@ -18,6 +18,7 @@
 
 import android.annotation.NonNull;
 import android.annotation.SystemApi;
+import android.annotation.TestApi;
 import android.content.Intent;
 import android.net.Uri;
 import android.os.Parcel;
@@ -184,6 +185,7 @@
          * @hide
          */
         @SystemApi
+        @TestApi
         public Builder setServiceId(String serviceId) {
             fileServiceId = serviceId;
             return this;
diff --git a/android/text/MeasuredParagraph.java b/android/text/MeasuredParagraph.java
index 96edfa3..c2c3182 100644
--- a/android/text/MeasuredParagraph.java
+++ b/android/text/MeasuredParagraph.java
@@ -303,10 +303,9 @@
      *
      * This is available only if the MeasuredParagraph is computed with buildForStaticLayout.
      */
-    public void getBounds(@NonNull Paint paint, @IntRange(from = 0) int start,
-            @IntRange(from = 0) int end, @NonNull Rect bounds) {
-        nGetBounds(mNativePtr, mCopiedBuffer, paint.getNativeInstance(), start, end,
-                paint.getBidiFlags(), bounds);
+    public void getBounds(@IntRange(from = 0) int start, @IntRange(from = 0) int end,
+            @NonNull Rect bounds) {
+        nGetBounds(mNativePtr, mCopiedBuffer, start, end, bounds);
     }
 
     /**
@@ -743,6 +742,6 @@
     @CriticalNative
     private static native int nGetMemoryUsage(/* Non Zero */ long nativePtr);
 
-    private static native void nGetBounds(long nativePtr, char[] buf, long paintPtr, int start,
-            int end, int bidiFlag, Rect rect);
+    private static native void nGetBounds(long nativePtr, char[] buf, int start, int end,
+            Rect rect);
 }
diff --git a/android/text/PrecomputedText.java b/android/text/PrecomputedText.java
index 413df05..369f357 100644
--- a/android/text/PrecomputedText.java
+++ b/android/text/PrecomputedText.java
@@ -16,6 +16,7 @@
 
 package android.text;
 
+import android.annotation.FloatRange;
 import android.annotation.IntRange;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -44,13 +45,17 @@
  * <pre>
  * An example usage is:
  * <code>
- *  void asyncSetText(final TextView textView, final String longString, Handler bgThreadHandler) {
+ *  static void asyncSetText(TextView textView, final String longString, Executor bgExecutor) {
  *      // construct precompute related parameters using the TextView that we will set the text on.
- *      final PrecomputedText.Params params = textView.getTextParams();
- *      bgThreadHandler.post(() -> {
- *          final PrecomputedText precomputedText =
- *                  PrecomputedText.create(expensiveLongString, params);
+ *      final PrecomputedText.Params params = textView.getTextMetricsParams();
+ *      final Reference textViewRef = new WeakReference<>(textView);
+ *      bgExecutor.submit(() -> {
+ *          TextView textView = textViewRef.get();
+ *          if (textView == null) return;
+ *          final PrecomputedText precomputedText = PrecomputedText.create(longString, params);
  *          textView.post(() -> {
+ *              TextView textView = textViewRef.get();
+ *              if (textView == null) return;
  *              textView.setText(precomputedText);
  *          });
  *      });
@@ -363,6 +368,7 @@
 
     /**
      * Return the underlying text.
+     * @hide
      */
     public @NonNull CharSequence getText() {
         return mText;
@@ -451,32 +457,65 @@
             + ", gave " + pos);
     }
 
-    /** @hide */
-    public float getWidth(@IntRange(from = 0) int start, @IntRange(from = 0) int end) {
+    /**
+     * Returns text width for the given range.
+     * Both {@code start} and {@code end} offset need to be in the same paragraph, otherwise
+     * IllegalArgumentException will be thrown.
+     *
+     * @param start the inclusive start offset in the text
+     * @param end the exclusive end offset in the text
+     * @return the text width
+     * @throws IllegalArgumentException if start and end offset are in the different paragraph.
+     */
+    public @FloatRange(from = 0) float getWidth(@IntRange(from = 0) int start,
+            @IntRange(from = 0) int end) {
+        Preconditions.checkArgument(0 <= start && start <= mText.length(), "invalid start offset");
+        Preconditions.checkArgument(0 <= end && end <= mText.length(), "invalid end offset");
+        Preconditions.checkArgument(start <= end, "start offset can not be larger than end offset");
+
+        if (start == end) {
+            return 0;
+        }
         final int paraIndex = findParaIndex(start);
         final int paraStart = getParagraphStart(paraIndex);
         final int paraEnd = getParagraphEnd(paraIndex);
         if (start < paraStart || paraEnd < end) {
-            throw new RuntimeException("Cannot measured across the paragraph:"
+            throw new IllegalArgumentException("Cannot measured across the paragraph:"
                 + "para: (" + paraStart + ", " + paraEnd + "), "
                 + "request: (" + start + ", " + end + ")");
         }
         return getMeasuredParagraph(paraIndex).getWidth(start - paraStart, end - paraStart);
     }
 
-    /** @hide */
+    /**
+     * Retrieves the text bounding box for the given range.
+     * Both {@code start} and {@code end} offset need to be in the same paragraph, otherwise
+     * IllegalArgumentException will be thrown.
+     *
+     * @param start the inclusive start offset in the text
+     * @param end the exclusive end offset in the text
+     * @param bounds the output rectangle
+     * @throws IllegalArgumentException if start and end offset are in the different paragraph.
+     */
     public void getBounds(@IntRange(from = 0) int start, @IntRange(from = 0) int end,
             @NonNull Rect bounds) {
+        Preconditions.checkArgument(0 <= start && start <= mText.length(), "invalid start offset");
+        Preconditions.checkArgument(0 <= end && end <= mText.length(), "invalid end offset");
+        Preconditions.checkArgument(start <= end, "start offset can not be larger than end offset");
+        Preconditions.checkNotNull(bounds);
+        if (start == end) {
+            bounds.set(0, 0, 0, 0);
+            return;
+        }
         final int paraIndex = findParaIndex(start);
         final int paraStart = getParagraphStart(paraIndex);
         final int paraEnd = getParagraphEnd(paraIndex);
         if (start < paraStart || paraEnd < end) {
-            throw new RuntimeException("Cannot measured across the paragraph:"
+            throw new IllegalArgumentException("Cannot measured across the paragraph:"
                 + "para: (" + paraStart + ", " + paraEnd + "), "
                 + "request: (" + start + ", " + end + ")");
         }
-        getMeasuredParagraph(paraIndex).getBounds(mParams.mPaint,
-                start - paraStart, end - paraStart, bounds);
+        getMeasuredParagraph(paraIndex).getBounds(start - paraStart, end - paraStart, bounds);
     }
 
     /**
diff --git a/android/text/Selection.java b/android/text/Selection.java
index 3445658..5256e47 100644
--- a/android/text/Selection.java
+++ b/android/text/Selection.java
@@ -180,7 +180,7 @@
      * Remove the selection or cursor, if any, from the text.
      */
     public static final void removeSelection(Spannable text) {
-        text.removeSpan(SELECTION_START);
+        text.removeSpan(SELECTION_START, Spanned.SPAN_INTERMEDIATE);
         text.removeSpan(SELECTION_END);
         removeMemory(text);
     }
diff --git a/android/text/Spannable.java b/android/text/Spannable.java
index 39b78eb..8315b2a 100644
--- a/android/text/Spannable.java
+++ b/android/text/Spannable.java
@@ -46,6 +46,19 @@
     public void removeSpan(Object what);
 
     /**
+     * Remove the specified object from the range of text to which it
+     * was attached, if any.  It is OK to remove an object that was never
+     * attached in the first place.
+     *
+     * See {@link Spanned} for an explanation of what the flags mean.
+     *
+     * @hide
+     */
+    default void removeSpan(Object what, int flags) {
+        removeSpan(what);
+    }
+
+    /**
      * Factory used by TextView to create new {@link Spannable Spannables}. You can subclass
      * it to provide something other than {@link SpannableString}.
      *
diff --git a/android/text/SpannableStringBuilder.java b/android/text/SpannableStringBuilder.java
index d41dfdc..41a9c45 100644
--- a/android/text/SpannableStringBuilder.java
+++ b/android/text/SpannableStringBuilder.java
@@ -312,7 +312,7 @@
                     // The following condition indicates that the span would become empty
                     (textIsRemoved || mSpanStarts[i] > start || mSpanEnds[i] < mGapStart)) {
                 mIndexOfSpan.remove(mSpans[i]);
-                removeSpan(i);
+                removeSpan(i, 0 /* flags */);
                 return true;
             }
             return resolveGap(mSpanStarts[i]) <= end && (i & 1) != 0 &&
@@ -472,7 +472,7 @@
     }
 
     // Note: caller is responsible for removing the mIndexOfSpan entry.
-    private void removeSpan(int i) {
+    private void removeSpan(int i, int flags) {
         Object object = mSpans[i];
 
         int start = mSpanStarts[i];
@@ -496,7 +496,9 @@
         // Invariants must be restored before sending span removed notifications.
         restoreInvariants();
 
-        sendSpanRemoved(object, start, end);
+        if ((flags & Spanned.SPAN_INTERMEDIATE) == 0) {
+            sendSpanRemoved(object, start, end);
+        }
     }
 
     // Documentation from interface
@@ -782,10 +784,19 @@
      * Remove the specified markup object from the buffer.
      */
     public void removeSpan(Object what) {
+        removeSpan(what, 0 /* flags */);
+    }
+
+    /**
+     * Remove the specified markup object from the buffer.
+     *
+     * @hide
+     */
+    public void removeSpan(Object what, int flags) {
         if (mIndexOfSpan == null) return;
         Integer i = mIndexOfSpan.remove(what);
         if (i != null) {
-            removeSpan(i.intValue());
+            removeSpan(i.intValue(), flags);
         }
     }
 
diff --git a/android/text/SpannableStringInternal.java b/android/text/SpannableStringInternal.java
index 5dd1a52..bcc2fda 100644
--- a/android/text/SpannableStringInternal.java
+++ b/android/text/SpannableStringInternal.java
@@ -249,6 +249,13 @@
     }
 
     /* package */ void removeSpan(Object what) {
+        removeSpan(what, 0 /* flags */);
+    }
+
+    /**
+     * @hide
+     */
+    public void removeSpan(Object what, int flags) {
         int count = mSpanCount;
         Object[] spans = mSpans;
         int[] data = mSpanData;
@@ -262,11 +269,13 @@
 
                 System.arraycopy(spans, i + 1, spans, i, c);
                 System.arraycopy(data, (i + 1) * COLUMNS,
-                                 data, i * COLUMNS, c * COLUMNS);
+                        data, i * COLUMNS, c * COLUMNS);
 
                 mSpanCount--;
 
-                sendSpanRemoved(what, ostart, oend);
+                if ((flags & Spanned.SPAN_INTERMEDIATE) == 0) {
+                    sendSpanRemoved(what, ostart, oend);
+                }
                 return;
             }
         }
diff --git a/android/text/format/Formatter.java b/android/text/format/Formatter.java
index ad3b4b6..de86a66 100644
--- a/android/text/format/Formatter.java
+++ b/android/text/format/Formatter.java
@@ -40,6 +40,10 @@
     public static final int FLAG_SHORTER = 1 << 0;
     /** {@hide} */
     public static final int FLAG_CALCULATE_ROUNDED = 1 << 1;
+    /** {@hide} */
+    public static final int FLAG_SI_UNITS = 1 << 2;
+    /** {@hide} */
+    public static final int FLAG_IEC_UNITS = 1 << 3;
 
     /** {@hide} */
     public static class BytesResult {
@@ -90,7 +94,7 @@
         if (context == null) {
             return "";
         }
-        final BytesResult res = formatBytes(context.getResources(), sizeBytes, 0);
+        final BytesResult res = formatBytes(context.getResources(), sizeBytes, FLAG_SI_UNITS);
         return bidiWrap(context, context.getString(com.android.internal.R.string.fileSizeSuffix,
                 res.value, res.units));
     }
@@ -103,41 +107,43 @@
         if (context == null) {
             return "";
         }
-        final BytesResult res = formatBytes(context.getResources(), sizeBytes, FLAG_SHORTER);
+        final BytesResult res = formatBytes(context.getResources(), sizeBytes,
+                FLAG_SI_UNITS | FLAG_SHORTER);
         return bidiWrap(context, context.getString(com.android.internal.R.string.fileSizeSuffix,
                 res.value, res.units));
     }
 
     /** {@hide} */
     public static BytesResult formatBytes(Resources res, long sizeBytes, int flags) {
+        final int unit = ((flags & FLAG_IEC_UNITS) != 0) ? 1024 : 1000;
         final boolean isNegative = (sizeBytes < 0);
         float result = isNegative ? -sizeBytes : sizeBytes;
         int suffix = com.android.internal.R.string.byteShort;
         long mult = 1;
         if (result > 900) {
             suffix = com.android.internal.R.string.kilobyteShort;
-            mult = 1000;
-            result = result / 1000;
+            mult = unit;
+            result = result / unit;
         }
         if (result > 900) {
             suffix = com.android.internal.R.string.megabyteShort;
-            mult *= 1000;
-            result = result / 1000;
+            mult *= unit;
+            result = result / unit;
         }
         if (result > 900) {
             suffix = com.android.internal.R.string.gigabyteShort;
-            mult *= 1000;
-            result = result / 1000;
+            mult *= unit;
+            result = result / unit;
         }
         if (result > 900) {
             suffix = com.android.internal.R.string.terabyteShort;
-            mult *= 1000;
-            result = result / 1000;
+            mult *= unit;
+            result = result / unit;
         }
         if (result > 900) {
             suffix = com.android.internal.R.string.petabyteShort;
-            mult *= 1000;
-            result = result / 1000;
+            mult *= unit;
+            result = result / unit;
         }
         // Note we calculate the rounded long by ourselves, but still let String.format()
         // compute the rounded value. String.format("%f", 0.1) might not return "0.1" due to
diff --git a/android/text/method/LinkMovementMethod.java b/android/text/method/LinkMovementMethod.java
index f332358..e60377b 100644
--- a/android/text/method/LinkMovementMethod.java
+++ b/android/text/method/LinkMovementMethod.java
@@ -219,7 +219,7 @@
                     links[0].onClick(widget);
                 } else if (action == MotionEvent.ACTION_DOWN) {
                     if (widget.getContext().getApplicationInfo().targetSdkVersion
-                            > Build.VERSION_CODES.O_MR1) {
+                            >= Build.VERSION_CODES.P) {
                         // Selection change will reposition the toolbar. Hide it for a few ms for a
                         // smoother transition.
                         widget.hideFloatingToolbar(HIDE_FLOATING_TOOLBAR_DELAY_MS);
diff --git a/android/text/util/Linkify.java b/android/text/util/Linkify.java
index 9c6a3f5..c905f49 100644
--- a/android/text/util/Linkify.java
+++ b/android/text/util/Linkify.java
@@ -100,13 +100,20 @@
      *  take an options mask. Note that this uses the
      *  {@link android.webkit.WebView#findAddress(String) findAddress()} method in
      *  {@link android.webkit.WebView} for finding addresses, which has various
-     *  limitations.
+     *  limitations and has been deprecated.
+     *  @deprecated use {@link android.view.textclassifier.TextClassifier#generateLinks(
+     *  TextLinks.Request)} instead and avoid it even when targeting API levels where no alternative
+     *  is available.
      */
+    @Deprecated
     public static final int MAP_ADDRESSES = 0x08;
 
     /**
      *  Bit mask indicating that all available patterns should be matched in
      *  methods that take an options mask
+     *  <p><strong>Note:</strong></p> {@link #MAP_ADDRESSES} is deprecated.
+     *  Use {@link android.view.textclassifier.TextClassifier#generateLinks(TextLinks.Request)}
+     *  instead and avoid it even when targeting API levels where no alternative is available.
      */
     public static final int ALL = WEB_URLS | EMAIL_ADDRESSES | PHONE_NUMBERS | MAP_ADDRESSES;
 
diff --git a/android/util/LruCache.java b/android/util/LruCache.java
index 5208606..4015488 100644
--- a/android/util/LruCache.java
+++ b/android/util/LruCache.java
@@ -20,10 +20,6 @@
 import java.util.Map;
 
 /**
- * BEGIN LAYOUTLIB CHANGE
- * This is a custom version that doesn't use the non standard LinkedHashMap#eldest.
- * END LAYOUTLIB CHANGE
- *
  * A cache that holds strong references to a limited number of values. Each time
  * a value is accessed, it is moved to the head of a queue. When a value is
  * added to a full cache, the value at the end of that queue is evicted and may
@@ -91,9 +87,8 @@
 
     /**
      * Sets the size of the cache.
-     * @param maxSize The new maximum size.
      *
-     * @hide
+     * @param maxSize The new maximum size.
      */
     public void resize(int maxSize) {
         if (maxSize <= 0) {
@@ -190,10 +185,13 @@
     }
 
     /**
+     * Remove the eldest entries until the total of remaining entries is at or
+     * below the requested size.
+     *
      * @param maxSize the maximum size of the cache before returning. May be -1
-     *     to evict even 0-sized elements.
+     *            to evict even 0-sized elements.
      */
-    private void trimToSize(int maxSize) {
+    public void trimToSize(int maxSize) {
         while (true) {
             K key;
             V value;
@@ -207,16 +205,7 @@
                     break;
                 }
 
-                // BEGIN LAYOUTLIB CHANGE
-                // get the last item in the linked list.
-                // This is not efficient, the goal here is to minimize the changes
-                // compared to the platform version.
-                Map.Entry<K, V> toEvict = null;
-                for (Map.Entry<K, V> entry : map.entrySet()) {
-                    toEvict = entry;
-                }
-                // END LAYOUTLIB CHANGE
-
+                Map.Entry<K, V> toEvict = map.eldest();
                 if (toEvict == null) {
                     break;
                 }
diff --git a/android/util/RecurrenceRule.java b/android/util/RecurrenceRule.java
index 9f115eb..9c89876 100644
--- a/android/util/RecurrenceRule.java
+++ b/android/util/RecurrenceRule.java
@@ -149,6 +149,10 @@
         }
     };
 
+    public boolean isRecurring() {
+        return period != null;
+    }
+
     @Deprecated
     public boolean isMonthly() {
         return start != null
@@ -158,7 +162,7 @@
                 && period.getDays() == 0;
     }
 
-    public Iterator<Pair<ZonedDateTime, ZonedDateTime>> cycleIterator() {
+    public Iterator<Range<ZonedDateTime>> cycleIterator() {
         if (period != null) {
             return new RecurringIterator();
         } else {
@@ -166,7 +170,7 @@
         }
     }
 
-    private class NonrecurringIterator implements Iterator<Pair<ZonedDateTime, ZonedDateTime>> {
+    private class NonrecurringIterator implements Iterator<Range<ZonedDateTime>> {
         boolean hasNext;
 
         public NonrecurringIterator() {
@@ -179,13 +183,13 @@
         }
 
         @Override
-        public Pair<ZonedDateTime, ZonedDateTime> next() {
+        public Range<ZonedDateTime> next() {
             hasNext = false;
-            return new Pair<>(start, end);
+            return new Range<>(start, end);
         }
     }
 
-    private class RecurringIterator implements Iterator<Pair<ZonedDateTime, ZonedDateTime>> {
+    private class RecurringIterator implements Iterator<Range<ZonedDateTime>> {
         int i;
         ZonedDateTime cycleStart;
         ZonedDateTime cycleEnd;
@@ -231,12 +235,12 @@
         }
 
         @Override
-        public Pair<ZonedDateTime, ZonedDateTime> next() {
+        public Range<ZonedDateTime> next() {
             if (LOGD) Log.d(TAG, "Cycle " + i + " from " + cycleStart + " to " + cycleEnd);
-            Pair<ZonedDateTime, ZonedDateTime> p = new Pair<>(cycleStart, cycleEnd);
+            Range<ZonedDateTime> r = new Range<>(cycleStart, cycleEnd);
             i--;
             updateCycle();
-            return p;
+            return r;
         }
     }
 
diff --git a/android/view/DisplayCutout.java b/android/view/DisplayCutout.java
index 66a9c6c..f59c0b5 100644
--- a/android/view/DisplayCutout.java
+++ b/android/view/DisplayCutout.java
@@ -31,6 +31,7 @@
 import android.os.Parcelable;
 import android.text.TextUtils;
 import android.util.Log;
+import android.util.Pair;
 import android.util.PathParser;
 import android.util.proto.ProtoOutputStream;
 
@@ -75,15 +76,19 @@
             false /* copyArguments */);
 
 
+    private static final Pair<Path, DisplayCutout> NULL_PAIR = new Pair<>(null, null);
     private static final Object CACHE_LOCK = new Object();
+
     @GuardedBy("CACHE_LOCK")
     private static String sCachedSpec;
     @GuardedBy("CACHE_LOCK")
     private static int sCachedDisplayWidth;
     @GuardedBy("CACHE_LOCK")
+    private static int sCachedDisplayHeight;
+    @GuardedBy("CACHE_LOCK")
     private static float sCachedDensity;
     @GuardedBy("CACHE_LOCK")
-    private static DisplayCutout sCachedCutout;
+    private static Pair<Path, DisplayCutout> sCachedCutout = NULL_PAIR;
 
     private final Rect mSafeInsets;
     private final Region mBounds;
@@ -347,7 +352,7 @@
     }
 
     /**
-     * Creates an instance according to @android:string/config_mainBuiltInDisplayCutout.
+     * Creates the bounding path according to @android:string/config_mainBuiltInDisplayCutout.
      *
      * @hide
      */
@@ -357,6 +362,16 @@
     }
 
     /**
+     * Creates an instance according to @android:string/config_mainBuiltInDisplayCutout.
+     *
+     * @hide
+     */
+    public static Path pathFromResources(Resources res, int displayWidth, int displayHeight) {
+        return pathAndDisplayCutoutFromSpec(res.getString(R.string.config_mainBuiltInDisplayCutout),
+                displayWidth, displayHeight, res.getDisplayMetrics().density).first;
+    }
+
+    /**
      * Creates an instance according to the supplied {@link android.util.PathParser.PathData} spec.
      *
      * @hide
@@ -364,11 +379,17 @@
     @VisibleForTesting(visibility = PRIVATE)
     public static DisplayCutout fromSpec(String spec, int displayWidth, int displayHeight,
             float density) {
+        return pathAndDisplayCutoutFromSpec(spec, displayWidth, displayHeight, density).second;
+    }
+
+    private static Pair<Path, DisplayCutout> pathAndDisplayCutoutFromSpec(String spec,
+            int displayWidth, int displayHeight, float density) {
         if (TextUtils.isEmpty(spec)) {
-            return null;
+            return NULL_PAIR;
         }
         synchronized (CACHE_LOCK) {
             if (spec.equals(sCachedSpec) && sCachedDisplayWidth == displayWidth
+                    && sCachedDisplayHeight == displayHeight
                     && sCachedDensity == density) {
                 return sCachedCutout;
             }
@@ -398,7 +419,7 @@
             p = PathParser.createPathFromPathData(spec);
         } catch (Throwable e) {
             Log.wtf(TAG, "Could not inflate cutout: ", e);
-            return null;
+            return NULL_PAIR;
         }
 
         final Matrix m = new Matrix();
@@ -414,7 +435,7 @@
                 bottomPath = PathParser.createPathFromPathData(bottomSpec);
             } catch (Throwable e) {
                 Log.wtf(TAG, "Could not inflate bottom cutout: ", e);
-                return null;
+                return NULL_PAIR;
             }
             // Keep top transform
             m.postTranslate(0, displayHeight);
@@ -422,10 +443,11 @@
             p.addPath(bottomPath);
         }
 
-        final DisplayCutout result = fromBounds(p);
+        final Pair<Path, DisplayCutout> result = new Pair<>(p, fromBounds(p));
         synchronized (CACHE_LOCK) {
             sCachedSpec = spec;
             sCachedDisplayWidth = displayWidth;
+            sCachedDisplayHeight = displayHeight;
             sCachedDensity = density;
             sCachedCutout = result;
         }
diff --git a/android/view/HapticFeedbackConstants.java b/android/view/HapticFeedbackConstants.java
index b147928..db01cea 100644
--- a/android/view/HapticFeedbackConstants.java
+++ b/android/view/HapticFeedbackConstants.java
@@ -77,6 +77,55 @@
     public static final int TEXT_HANDLE_MOVE = 9;
 
     /**
+     * The user unlocked the device
+     * @hide
+     */
+    public static final int ENTRY_BUMP = 10;
+
+    /**
+     * The user has moved the dragged object within a droppable area.
+     * @hide
+     */
+    public static final int DRAG_CROSSING = 11;
+
+    /**
+     * The user has started a gesture (e.g. on the soft keyboard).
+     * @hide
+     */
+    public static final int GESTURE_START = 12;
+
+    /**
+     * The user has finished a gesture (e.g. on the soft keyboard).
+     * @hide
+     */
+    public static final int GESTURE_END = 13;
+
+    /**
+     * The user's squeeze crossed the gesture's initiation threshold.
+     * @hide
+     */
+    public static final int EDGE_SQUEEZE = 14;
+
+    /**
+     * The user's squeeze crossed the gesture's release threshold.
+     * @hide
+     */
+    public static final int EDGE_RELEASE = 15;
+
+    /**
+     * A haptic effect to signal the confirmation or successful completion of a user
+     * interaction.
+     * @hide
+     */
+    public static final int CONFIRM = 16;
+
+    /**
+     * A haptic effect to signal the rejection or failure of a user interaction.
+     * @hide
+     */
+    public static final int REJECT = 17;
+
+    /**
      * The phone has booted with safe mode enabled.
      * This is a private constant.  Feel free to renumber as desired.
      * @hide
diff --git a/android/view/SurfaceControl.java b/android/view/SurfaceControl.java
index d4610a5..5deee11 100644
--- a/android/view/SurfaceControl.java
+++ b/android/view/SurfaceControl.java
@@ -87,6 +87,7 @@
     private static native void nativeMergeTransaction(long transactionObj,
             long otherTransactionObj);
     private static native void nativeSetAnimationTransaction(long transactionObj);
+    private static native void nativeSetEarlyWakeup(long transactionObj);
 
     private static native void nativeSetLayer(long transactionObj, long nativeObject, int zorder);
     private static native void nativeSetRelativeLayer(long transactionObj, long nativeObject,
@@ -1642,6 +1643,19 @@
         }
 
         /**
+         * Indicate that SurfaceFlinger should wake up earlier than usual as a result of this
+         * transaction. This should be used when the caller thinks that the scene is complex enough
+         * that it's likely to hit GL composition, and thus, SurfaceFlinger needs to more time in
+         * order not to miss frame deadlines.
+         * <p>
+         * Corresponds to setting ISurfaceComposer::eEarlyWakeup
+         */
+        public Transaction setEarlyWakeup() {
+            nativeSetEarlyWakeup(mNativeObject);
+            return this;
+        }
+
+        /**
          * Merge the other transaction into this transaction, clearing the
          * other transaction as if it had been applied.
          */
diff --git a/android/view/SurfaceView.java b/android/view/SurfaceView.java
index ebb2af4..7e54647 100644
--- a/android/view/SurfaceView.java
+++ b/android/view/SurfaceView.java
@@ -16,115 +16,1237 @@
 
 package android.view;
 
-import com.android.layoutlib.bridge.MockView;
+import static android.view.WindowManagerPolicyConstants.APPLICATION_MEDIA_OVERLAY_SUBLAYER;
+import static android.view.WindowManagerPolicyConstants.APPLICATION_MEDIA_SUBLAYER;
+import static android.view.WindowManagerPolicyConstants.APPLICATION_PANEL_SUBLAYER;
 
 import android.content.Context;
+import android.content.res.CompatibilityInfo.Translator;
+import android.content.res.Configuration;
 import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.PixelFormat;
+import android.graphics.PorterDuff;
 import android.graphics.Rect;
 import android.graphics.Region;
+import android.os.Build;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Looper;
+import android.os.SystemClock;
 import android.util.AttributeSet;
+import android.util.Log;
+
+import com.android.internal.view.SurfaceCallbackHelper;
+
+import java.util.ArrayList;
+import java.util.concurrent.locks.ReentrantLock;
 
 /**
- * Mock version of the SurfaceView.
- * Only non override public methods from the real SurfaceView have been added in there.
- * Methods that take an unknown class as parameter or as return object, have been removed for now.
+ * Provides a dedicated drawing surface embedded inside of a view hierarchy.
+ * You can control the format of this surface and, if you like, its size; the
+ * SurfaceView takes care of placing the surface at the correct location on the
+ * screen
  *
- * TODO: generate automatically.
+ * <p>The surface is Z ordered so that it is behind the window holding its
+ * SurfaceView; the SurfaceView punches a hole in its window to allow its
+ * surface to be displayed. The view hierarchy will take care of correctly
+ * compositing with the Surface any siblings of the SurfaceView that would
+ * normally appear on top of it. This can be used to place overlays such as
+ * buttons on top of the Surface, though note however that it can have an
+ * impact on performance since a full alpha-blended composite will be performed
+ * each time the Surface changes.
  *
+ * <p> The transparent region that makes the surface visible is based on the
+ * layout positions in the view hierarchy. If the post-layout transform
+ * properties are used to draw a sibling view on top of the SurfaceView, the
+ * view may not be properly composited with the surface.
+ *
+ * <p>Access to the underlying surface is provided via the SurfaceHolder interface,
+ * which can be retrieved by calling {@link #getHolder}.
+ *
+ * <p>The Surface will be created for you while the SurfaceView's window is
+ * visible; you should implement {@link SurfaceHolder.Callback#surfaceCreated}
+ * and {@link SurfaceHolder.Callback#surfaceDestroyed} to discover when the
+ * Surface is created and destroyed as the window is shown and hidden.
+ *
+ * <p>One of the purposes of this class is to provide a surface in which a
+ * secondary thread can render into the screen. If you are going to use it
+ * this way, you need to be aware of some threading semantics:
+ *
+ * <ul>
+ * <li> All SurfaceView and
+ * {@link SurfaceHolder.Callback SurfaceHolder.Callback} methods will be called
+ * from the thread running the SurfaceView's window (typically the main thread
+ * of the application). They thus need to correctly synchronize with any
+ * state that is also touched by the drawing thread.
+ * <li> You must ensure that the drawing thread only touches the underlying
+ * Surface while it is valid -- between
+ * {@link SurfaceHolder.Callback#surfaceCreated SurfaceHolder.Callback.surfaceCreated()}
+ * and
+ * {@link SurfaceHolder.Callback#surfaceDestroyed SurfaceHolder.Callback.surfaceDestroyed()}.
+ * </ul>
+ *
+ * <p class="note"><strong>Note:</strong> Starting in platform version
+ * {@link android.os.Build.VERSION_CODES#N}, SurfaceView's window position is
+ * updated synchronously with other View rendering. This means that translating
+ * and scaling a SurfaceView on screen will not cause rendering artifacts. Such
+ * artifacts may occur on previous versions of the platform when its window is
+ * positioned asynchronously.</p>
  */
-public class SurfaceView extends MockView {
+public class SurfaceView extends View implements ViewRootImpl.WindowStoppedCallback {
+    private static final String TAG = "SurfaceView";
+    private static final boolean DEBUG = false;
+
+    final ArrayList<SurfaceHolder.Callback> mCallbacks
+            = new ArrayList<SurfaceHolder.Callback>();
+
+    final int[] mLocation = new int[2];
+
+    final ReentrantLock mSurfaceLock = new ReentrantLock();
+    final Surface mSurface = new Surface();       // Current surface in use
+    boolean mDrawingStopped = true;
+    // We use this to track if the application has produced a frame
+    // in to the Surface. Up until that point, we should be careful not to punch
+    // holes.
+    boolean mDrawFinished = false;
+
+    final Rect mScreenRect = new Rect();
+    SurfaceSession mSurfaceSession;
+
+    SurfaceControlWithBackground mSurfaceControl;
+    // In the case of format changes we switch out the surface in-place
+    // we need to preserve the old one until the new one has drawn.
+    SurfaceControl mDeferredDestroySurfaceControl;
+    final Rect mTmpRect = new Rect();
+    final Configuration mConfiguration = new Configuration();
+
+    int mSubLayer = APPLICATION_MEDIA_SUBLAYER;
+
+    boolean mIsCreating = false;
+    private volatile boolean mRtHandlingPositionUpdates = false;
+
+    private final ViewTreeObserver.OnScrollChangedListener mScrollChangedListener
+            = new ViewTreeObserver.OnScrollChangedListener() {
+                    @Override
+                    public void onScrollChanged() {
+                        updateSurface();
+                    }
+            };
+
+    private final ViewTreeObserver.OnPreDrawListener mDrawListener =
+            new ViewTreeObserver.OnPreDrawListener() {
+                @Override
+                public boolean onPreDraw() {
+                    // reposition ourselves where the surface is
+                    mHaveFrame = getWidth() > 0 && getHeight() > 0;
+                    updateSurface();
+                    return true;
+                }
+            };
+
+    boolean mRequestedVisible = false;
+    boolean mWindowVisibility = false;
+    boolean mLastWindowVisibility = false;
+    boolean mViewVisibility = false;
+    boolean mWindowStopped = false;
+
+    int mRequestedWidth = -1;
+    int mRequestedHeight = -1;
+    /* Set SurfaceView's format to 565 by default to maintain backward
+     * compatibility with applications assuming this format.
+     */
+    int mRequestedFormat = PixelFormat.RGB_565;
+
+    boolean mHaveFrame = false;
+    boolean mSurfaceCreated = false;
+    long mLastLockTime = 0;
+
+    boolean mVisible = false;
+    int mWindowSpaceLeft = -1;
+    int mWindowSpaceTop = -1;
+    int mSurfaceWidth = -1;
+    int mSurfaceHeight = -1;
+    int mFormat = -1;
+    final Rect mSurfaceFrame = new Rect();
+    int mLastSurfaceWidth = -1, mLastSurfaceHeight = -1;
+    private Translator mTranslator;
+
+    private boolean mGlobalListenersAdded;
+    private boolean mAttachedToWindow;
+
+    private int mSurfaceFlags = SurfaceControl.HIDDEN;
+
+    private int mPendingReportDraws;
+
+    private SurfaceControl.Transaction mRtTransaction = new SurfaceControl.Transaction();
 
     public SurfaceView(Context context) {
         this(context, null);
     }
 
     public SurfaceView(Context context, AttributeSet attrs) {
-        this(context, attrs , 0);
+        this(context, attrs, 0);
     }
 
-    public SurfaceView(Context context, AttributeSet attrs, int defStyle) {
-        super(context, attrs, defStyle);
+    public SurfaceView(Context context, AttributeSet attrs, int defStyleAttr) {
+        this(context, attrs, defStyleAttr, 0);
     }
 
     public SurfaceView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
         super(context, attrs, defStyleAttr, defStyleRes);
+        mRenderNode.requestPositionUpdates(this);
+
+        setWillNotDraw(true);
     }
 
-    public boolean gatherTransparentRegion(Region region) {
-      return false;
-    }
-
-    public void setZOrderMediaOverlay(boolean isMediaOverlay) {
-    }
-
-    public void setZOrderOnTop(boolean onTop) {
-    }
-
-    public void setSecure(boolean isSecure) {
-    }
-
+    /**
+     * Return the SurfaceHolder providing access and control over this
+     * SurfaceView's underlying surface.
+     *
+     * @return SurfaceHolder The holder of the surface.
+     */
     public SurfaceHolder getHolder() {
         return mSurfaceHolder;
     }
 
-    private SurfaceHolder mSurfaceHolder = new SurfaceHolder() {
+    private void updateRequestedVisibility() {
+        mRequestedVisible = mViewVisibility && mWindowVisibility && !mWindowStopped;
+    }
+
+    /** @hide */
+    @Override
+    public void windowStopped(boolean stopped) {
+        mWindowStopped = stopped;
+        updateRequestedVisibility();
+        updateSurface();
+    }
+
+    @Override
+    protected void onAttachedToWindow() {
+        super.onAttachedToWindow();
+
+        getViewRootImpl().addWindowStoppedCallback(this);
+        mWindowStopped = false;
+
+        mViewVisibility = getVisibility() == VISIBLE;
+        updateRequestedVisibility();
+
+        mAttachedToWindow = true;
+        mParent.requestTransparentRegion(SurfaceView.this);
+        if (!mGlobalListenersAdded) {
+            ViewTreeObserver observer = getViewTreeObserver();
+            observer.addOnScrollChangedListener(mScrollChangedListener);
+            observer.addOnPreDrawListener(mDrawListener);
+            mGlobalListenersAdded = true;
+        }
+    }
+
+    @Override
+    protected void onWindowVisibilityChanged(int visibility) {
+        super.onWindowVisibilityChanged(visibility);
+        mWindowVisibility = visibility == VISIBLE;
+        updateRequestedVisibility();
+        updateSurface();
+    }
+
+    @Override
+    public void setVisibility(int visibility) {
+        super.setVisibility(visibility);
+        mViewVisibility = visibility == VISIBLE;
+        boolean newRequestedVisible = mWindowVisibility && mViewVisibility && !mWindowStopped;
+        if (newRequestedVisible != mRequestedVisible) {
+            // our base class (View) invalidates the layout only when
+            // we go from/to the GONE state. However, SurfaceView needs
+            // to request a re-layout when the visibility changes at all.
+            // This is needed because the transparent region is computed
+            // as part of the layout phase, and it changes (obviously) when
+            // the visibility changes.
+            requestLayout();
+        }
+        mRequestedVisible = newRequestedVisible;
+        updateSurface();
+    }
+
+    private void performDrawFinished() {
+        if (mPendingReportDraws > 0) {
+            mDrawFinished = true;
+            if (mAttachedToWindow) {
+                notifyDrawFinished();
+                invalidate();
+            }
+        } else {
+            Log.e(TAG, System.identityHashCode(this) + "finished drawing"
+                    + " but no pending report draw (extra call"
+                    + " to draw completion runnable?)");
+        }
+    }
+
+    void notifyDrawFinished() {
+        ViewRootImpl viewRoot = getViewRootImpl();
+        if (viewRoot != null) {
+            viewRoot.pendingDrawFinished();
+        }
+        mPendingReportDraws--;
+    }
+
+    @Override
+    protected void onDetachedFromWindow() {
+        ViewRootImpl viewRoot = getViewRootImpl();
+        // It's possible to create a SurfaceView using the default constructor and never
+        // attach it to a view hierarchy, this is a common use case when dealing with
+        // OpenGL. A developer will probably create a new GLSurfaceView, and let it manage
+        // the lifecycle. Instead of attaching it to a view, he/she can just pass
+        // the SurfaceHolder forward, most live wallpapers do it.
+        if (viewRoot != null) {
+            viewRoot.removeWindowStoppedCallback(this);
+        }
+
+        mAttachedToWindow = false;
+        if (mGlobalListenersAdded) {
+            ViewTreeObserver observer = getViewTreeObserver();
+            observer.removeOnScrollChangedListener(mScrollChangedListener);
+            observer.removeOnPreDrawListener(mDrawListener);
+            mGlobalListenersAdded = false;
+        }
+
+        while (mPendingReportDraws > 0) {
+            notifyDrawFinished();
+        }
+
+        mRequestedVisible = false;
+
+        updateSurface();
+        if (mSurfaceControl != null) {
+            mSurfaceControl.destroy();
+        }
+        mSurfaceControl = null;
+
+        mHaveFrame = false;
+
+        super.onDetachedFromWindow();
+    }
+
+    @Override
+    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+        int width = mRequestedWidth >= 0
+                ? resolveSizeAndState(mRequestedWidth, widthMeasureSpec, 0)
+                : getDefaultSize(0, widthMeasureSpec);
+        int height = mRequestedHeight >= 0
+                ? resolveSizeAndState(mRequestedHeight, heightMeasureSpec, 0)
+                : getDefaultSize(0, heightMeasureSpec);
+        setMeasuredDimension(width, height);
+    }
+
+    /** @hide */
+    @Override
+    protected boolean setFrame(int left, int top, int right, int bottom) {
+        boolean result = super.setFrame(left, top, right, bottom);
+        updateSurface();
+        return result;
+    }
+
+    @Override
+    public boolean gatherTransparentRegion(Region region) {
+        if (isAboveParent() || !mDrawFinished) {
+            return super.gatherTransparentRegion(region);
+        }
+
+        boolean opaque = true;
+        if ((mPrivateFlags & PFLAG_SKIP_DRAW) == 0) {
+            // this view draws, remove it from the transparent region
+            opaque = super.gatherTransparentRegion(region);
+        } else if (region != null) {
+            int w = getWidth();
+            int h = getHeight();
+            if (w>0 && h>0) {
+                getLocationInWindow(mLocation);
+                // otherwise, punch a hole in the whole hierarchy
+                int l = mLocation[0];
+                int t = mLocation[1];
+                region.op(l, t, l+w, t+h, Region.Op.UNION);
+            }
+        }
+        if (PixelFormat.formatHasAlpha(mRequestedFormat)) {
+            opaque = false;
+        }
+        return opaque;
+    }
+
+    @Override
+    public void draw(Canvas canvas) {
+        if (mDrawFinished && !isAboveParent()) {
+            // draw() is not called when SKIP_DRAW is set
+            if ((mPrivateFlags & PFLAG_SKIP_DRAW) == 0) {
+                // punch a whole in the view-hierarchy below us
+                canvas.drawColor(0, PorterDuff.Mode.CLEAR);
+            }
+        }
+        super.draw(canvas);
+    }
+
+    @Override
+    protected void dispatchDraw(Canvas canvas) {
+        if (mDrawFinished && !isAboveParent()) {
+            // draw() is not called when SKIP_DRAW is set
+            if ((mPrivateFlags & PFLAG_SKIP_DRAW) == PFLAG_SKIP_DRAW) {
+                // punch a whole in the view-hierarchy below us
+                canvas.drawColor(0, PorterDuff.Mode.CLEAR);
+            }
+        }
+        super.dispatchDraw(canvas);
+    }
+
+    /**
+     * Control whether the surface view's surface is placed on top of another
+     * regular surface view in the window (but still behind the window itself).
+     * This is typically used to place overlays on top of an underlying media
+     * surface view.
+     *
+     * <p>Note that this must be set before the surface view's containing
+     * window is attached to the window manager.
+     *
+     * <p>Calling this overrides any previous call to {@link #setZOrderOnTop}.
+     */
+    public void setZOrderMediaOverlay(boolean isMediaOverlay) {
+        mSubLayer = isMediaOverlay
+            ? APPLICATION_MEDIA_OVERLAY_SUBLAYER : APPLICATION_MEDIA_SUBLAYER;
+    }
+
+    /**
+     * Control whether the surface view's surface is placed on top of its
+     * window.  Normally it is placed behind the window, to allow it to
+     * (for the most part) appear to composite with the views in the
+     * hierarchy.  By setting this, you cause it to be placed above the
+     * window.  This means that none of the contents of the window this
+     * SurfaceView is in will be visible on top of its surface.
+     *
+     * <p>Note that this must be set before the surface view's containing
+     * window is attached to the window manager.
+     *
+     * <p>Calling this overrides any previous call to {@link #setZOrderMediaOverlay}.
+     */
+    public void setZOrderOnTop(boolean onTop) {
+        if (onTop) {
+            mSubLayer = APPLICATION_PANEL_SUBLAYER;
+        } else {
+            mSubLayer = APPLICATION_MEDIA_SUBLAYER;
+        }
+    }
+
+    /**
+     * Control whether the surface view's content should be treated as secure,
+     * preventing it from appearing in screenshots or from being viewed on
+     * non-secure displays.
+     *
+     * <p>Note that this must be set before the surface view's containing
+     * window is attached to the window manager.
+     *
+     * <p>See {@link android.view.Display#FLAG_SECURE} for details.
+     *
+     * @param isSecure True if the surface view is secure.
+     */
+    public void setSecure(boolean isSecure) {
+        if (isSecure) {
+            mSurfaceFlags |= SurfaceControl.SECURE;
+        } else {
+            mSurfaceFlags &= ~SurfaceControl.SECURE;
+        }
+    }
+
+    private void updateOpaqueFlag() {
+        if (!PixelFormat.formatHasAlpha(mRequestedFormat)) {
+            mSurfaceFlags |= SurfaceControl.OPAQUE;
+        } else {
+            mSurfaceFlags &= ~SurfaceControl.OPAQUE;
+        }
+    }
+
+    private Rect getParentSurfaceInsets() {
+        final ViewRootImpl root = getViewRootImpl();
+        if (root == null) {
+            return null;
+        } else {
+            return root.mWindowAttributes.surfaceInsets;
+        }
+    }
+
+    /** @hide */
+    protected void updateSurface() {
+        if (!mHaveFrame) {
+            return;
+        }
+        ViewRootImpl viewRoot = getViewRootImpl();
+        if (viewRoot == null || viewRoot.mSurface == null || !viewRoot.mSurface.isValid()) {
+            return;
+        }
+
+        mTranslator = viewRoot.mTranslator;
+        if (mTranslator != null) {
+            mSurface.setCompatibilityTranslator(mTranslator);
+        }
+
+        int myWidth = mRequestedWidth;
+        if (myWidth <= 0) myWidth = getWidth();
+        int myHeight = mRequestedHeight;
+        if (myHeight <= 0) myHeight = getHeight();
+
+        final boolean formatChanged = mFormat != mRequestedFormat;
+        final boolean visibleChanged = mVisible != mRequestedVisible;
+        final boolean creating = (mSurfaceControl == null || formatChanged || visibleChanged)
+                && mRequestedVisible;
+        final boolean sizeChanged = mSurfaceWidth != myWidth || mSurfaceHeight != myHeight;
+        final boolean windowVisibleChanged = mWindowVisibility != mLastWindowVisibility;
+        boolean redrawNeeded = false;
+
+        if (creating || formatChanged || sizeChanged || visibleChanged || windowVisibleChanged) {
+            getLocationInWindow(mLocation);
+
+            if (DEBUG) Log.i(TAG, System.identityHashCode(this) + " "
+                    + "Changes: creating=" + creating
+                    + " format=" + formatChanged + " size=" + sizeChanged
+                    + " visible=" + visibleChanged
+                    + " left=" + (mWindowSpaceLeft != mLocation[0])
+                    + " top=" + (mWindowSpaceTop != mLocation[1]));
+
+            try {
+                final boolean visible = mVisible = mRequestedVisible;
+                mWindowSpaceLeft = mLocation[0];
+                mWindowSpaceTop = mLocation[1];
+                mSurfaceWidth = myWidth;
+                mSurfaceHeight = myHeight;
+                mFormat = mRequestedFormat;
+                mLastWindowVisibility = mWindowVisibility;
+
+                mScreenRect.left = mWindowSpaceLeft;
+                mScreenRect.top = mWindowSpaceTop;
+                mScreenRect.right = mWindowSpaceLeft + getWidth();
+                mScreenRect.bottom = mWindowSpaceTop + getHeight();
+                if (mTranslator != null) {
+                    mTranslator.translateRectInAppWindowToScreen(mScreenRect);
+                }
+
+                final Rect surfaceInsets = getParentSurfaceInsets();
+                mScreenRect.offset(surfaceInsets.left, surfaceInsets.top);
+
+                if (creating) {
+                    mSurfaceSession = new SurfaceSession(viewRoot.mSurface);
+                    mDeferredDestroySurfaceControl = mSurfaceControl;
+
+                    updateOpaqueFlag();
+                    final String name = "SurfaceView - " + viewRoot.getTitle().toString();
+
+                    mSurfaceControl = new SurfaceControlWithBackground(
+                            name,
+                            (mSurfaceFlags & SurfaceControl.OPAQUE) != 0,
+                            new SurfaceControl.Builder(mSurfaceSession)
+                                    .setSize(mSurfaceWidth, mSurfaceHeight)
+                                    .setFormat(mFormat)
+                                    .setFlags(mSurfaceFlags));
+                } else if (mSurfaceControl == null) {
+                    return;
+                }
+
+                boolean realSizeChanged = false;
+
+                mSurfaceLock.lock();
+                try {
+                    mDrawingStopped = !visible;
+
+                    if (DEBUG) Log.i(TAG, System.identityHashCode(this) + " "
+                            + "Cur surface: " + mSurface);
+
+                    SurfaceControl.openTransaction();
+                    try {
+                        mSurfaceControl.setLayer(mSubLayer);
+                        if (mViewVisibility) {
+                            mSurfaceControl.show();
+                        } else {
+                            mSurfaceControl.hide();
+                        }
+
+                        // While creating the surface, we will set it's initial
+                        // geometry. Outside of that though, we should generally
+                        // leave it to the RenderThread.
+                        //
+                        // There is one more case when the buffer size changes we aren't yet
+                        // prepared to sync (as even following the transaction applying
+                        // we still need to latch a buffer).
+                        // b/28866173
+                        if (sizeChanged || creating || !mRtHandlingPositionUpdates) {
+                            mSurfaceControl.setPosition(mScreenRect.left, mScreenRect.top);
+                            mSurfaceControl.setMatrix(mScreenRect.width() / (float) mSurfaceWidth,
+                                    0.0f, 0.0f,
+                                    mScreenRect.height() / (float) mSurfaceHeight);
+                        }
+                        if (sizeChanged) {
+                            mSurfaceControl.setSize(mSurfaceWidth, mSurfaceHeight);
+                        }
+                    } finally {
+                        SurfaceControl.closeTransaction();
+                    }
+
+                    if (sizeChanged || creating) {
+                        redrawNeeded = true;
+                    }
+
+                    mSurfaceFrame.left = 0;
+                    mSurfaceFrame.top = 0;
+                    if (mTranslator == null) {
+                        mSurfaceFrame.right = mSurfaceWidth;
+                        mSurfaceFrame.bottom = mSurfaceHeight;
+                    } else {
+                        float appInvertedScale = mTranslator.applicationInvertedScale;
+                        mSurfaceFrame.right = (int) (mSurfaceWidth * appInvertedScale + 0.5f);
+                        mSurfaceFrame.bottom = (int) (mSurfaceHeight * appInvertedScale + 0.5f);
+                    }
+
+                    final int surfaceWidth = mSurfaceFrame.right;
+                    final int surfaceHeight = mSurfaceFrame.bottom;
+                    realSizeChanged = mLastSurfaceWidth != surfaceWidth
+                            || mLastSurfaceHeight != surfaceHeight;
+                    mLastSurfaceWidth = surfaceWidth;
+                    mLastSurfaceHeight = surfaceHeight;
+                } finally {
+                    mSurfaceLock.unlock();
+                }
+
+                try {
+                    redrawNeeded |= visible && !mDrawFinished;
+
+                    SurfaceHolder.Callback callbacks[] = null;
+
+                    final boolean surfaceChanged = creating;
+                    if (mSurfaceCreated && (surfaceChanged || (!visible && visibleChanged))) {
+                        mSurfaceCreated = false;
+                        if (mSurface.isValid()) {
+                            if (DEBUG) Log.i(TAG, System.identityHashCode(this) + " "
+                                    + "visibleChanged -- surfaceDestroyed");
+                            callbacks = getSurfaceCallbacks();
+                            for (SurfaceHolder.Callback c : callbacks) {
+                                c.surfaceDestroyed(mSurfaceHolder);
+                            }
+                            // Since Android N the same surface may be reused and given to us
+                            // again by the system server at a later point. However
+                            // as we didn't do this in previous releases, clients weren't
+                            // necessarily required to clean up properly in
+                            // surfaceDestroyed. This leads to problems for example when
+                            // clients don't destroy their EGL context, and try
+                            // and create a new one on the same surface following reuse.
+                            // Since there is no valid use of the surface in-between
+                            // surfaceDestroyed and surfaceCreated, we force a disconnect,
+                            // so the next connect will always work if we end up reusing
+                            // the surface.
+                            if (mSurface.isValid()) {
+                                mSurface.forceScopedDisconnect();
+                            }
+                        }
+                    }
+
+                    if (creating) {
+                        mSurface.copyFrom(mSurfaceControl);
+                    }
+
+                    if (sizeChanged && getContext().getApplicationInfo().targetSdkVersion
+                            < Build.VERSION_CODES.O) {
+                        // Some legacy applications use the underlying native {@link Surface} object
+                        // as a key to whether anything has changed. In these cases, updates to the
+                        // existing {@link Surface} will be ignored when the size changes.
+                        // Therefore, we must explicitly recreate the {@link Surface} in these
+                        // cases.
+                        mSurface.createFrom(mSurfaceControl);
+                    }
+
+                    if (visible && mSurface.isValid()) {
+                        if (!mSurfaceCreated && (surfaceChanged || visibleChanged)) {
+                            mSurfaceCreated = true;
+                            mIsCreating = true;
+                            if (DEBUG) Log.i(TAG, System.identityHashCode(this) + " "
+                                    + "visibleChanged -- surfaceCreated");
+                            if (callbacks == null) {
+                                callbacks = getSurfaceCallbacks();
+                            }
+                            for (SurfaceHolder.Callback c : callbacks) {
+                                c.surfaceCreated(mSurfaceHolder);
+                            }
+                        }
+                        if (creating || formatChanged || sizeChanged
+                                || visibleChanged || realSizeChanged) {
+                            if (DEBUG) Log.i(TAG, System.identityHashCode(this) + " "
+                                    + "surfaceChanged -- format=" + mFormat
+                                    + " w=" + myWidth + " h=" + myHeight);
+                            if (callbacks == null) {
+                                callbacks = getSurfaceCallbacks();
+                            }
+                            for (SurfaceHolder.Callback c : callbacks) {
+                                c.surfaceChanged(mSurfaceHolder, mFormat, myWidth, myHeight);
+                            }
+                        }
+                        if (redrawNeeded) {
+                            if (DEBUG) Log.i(TAG, System.identityHashCode(this) + " "
+                                    + "surfaceRedrawNeeded");
+                            if (callbacks == null) {
+                                callbacks = getSurfaceCallbacks();
+                            }
+
+                            mPendingReportDraws++;
+                            viewRoot.drawPending();
+                            SurfaceCallbackHelper sch =
+                                    new SurfaceCallbackHelper(this::onDrawFinished);
+                            sch.dispatchSurfaceRedrawNeededAsync(mSurfaceHolder, callbacks);
+                        }
+                    }
+                } finally {
+                    mIsCreating = false;
+                    if (mSurfaceControl != null && !mSurfaceCreated) {
+                        mSurface.release();
+                        // If we are not in the stopped state, then the destruction of the Surface
+                        // represents a visual change we need to display, and we should go ahead
+                        // and destroy the SurfaceControl. However if we are in the stopped state,
+                        // we can just leave the Surface around so it can be a part of animations,
+                        // and we let the life-time be tied to the parent surface.
+                        if (!mWindowStopped) {
+                            mSurfaceControl.destroy();
+                            mSurfaceControl = null;
+                        }
+                    }
+                }
+            } catch (Exception ex) {
+                Log.e(TAG, "Exception configuring surface", ex);
+            }
+            if (DEBUG) Log.v(
+                TAG, "Layout: x=" + mScreenRect.left + " y=" + mScreenRect.top
+                + " w=" + mScreenRect.width() + " h=" + mScreenRect.height()
+                + ", frame=" + mSurfaceFrame);
+        } else {
+            // Calculate the window position in case RT loses the window
+            // and we need to fallback to a UI-thread driven position update
+            getLocationInSurface(mLocation);
+            final boolean positionChanged = mWindowSpaceLeft != mLocation[0]
+                    || mWindowSpaceTop != mLocation[1];
+            final boolean layoutSizeChanged = getWidth() != mScreenRect.width()
+                    || getHeight() != mScreenRect.height();
+            if (positionChanged || layoutSizeChanged) { // Only the position has changed
+                mWindowSpaceLeft = mLocation[0];
+                mWindowSpaceTop = mLocation[1];
+                // For our size changed check, we keep mScreenRect.width() and mScreenRect.height()
+                // in view local space.
+                mLocation[0] = getWidth();
+                mLocation[1] = getHeight();
+
+                mScreenRect.set(mWindowSpaceLeft, mWindowSpaceTop,
+                        mWindowSpaceLeft + mLocation[0], mWindowSpaceTop + mLocation[1]);
+
+                if (mTranslator != null) {
+                    mTranslator.translateRectInAppWindowToScreen(mScreenRect);
+                }
+
+                if (mSurfaceControl == null) {
+                    return;
+                }
+
+                if (!isHardwareAccelerated() || !mRtHandlingPositionUpdates) {
+                    try {
+                        if (DEBUG) Log.d(TAG, String.format("%d updateSurfacePosition UI, " +
+                                "postion = [%d, %d, %d, %d]", System.identityHashCode(this),
+                                mScreenRect.left, mScreenRect.top,
+                                mScreenRect.right, mScreenRect.bottom));
+                        setParentSpaceRectangle(mScreenRect, -1);
+                    } catch (Exception ex) {
+                        Log.e(TAG, "Exception configuring surface", ex);
+                    }
+                }
+            }
+        }
+    }
+
+    private void onDrawFinished() {
+        if (DEBUG) {
+            Log.i(TAG, System.identityHashCode(this) + " "
+                    + "finishedDrawing");
+        }
+
+        if (mDeferredDestroySurfaceControl != null) {
+            mDeferredDestroySurfaceControl.destroy();
+            mDeferredDestroySurfaceControl = null;
+        }
+
+        runOnUiThread(() -> {
+            performDrawFinished();
+        });
+    }
+
+    /**
+     * A place to over-ride for applying child-surface transactions.
+     * These can be synchronized with the viewroot surface using deferTransaction.
+     *
+     * Called from RenderWorker while UI thread is paused.
+     * @hide
+     */
+    protected void applyChildSurfaceTransaction_renderWorker(SurfaceControl.Transaction t,
+            Surface viewRootSurface, long nextViewRootFrameNumber) {
+    }
+
+    private void applySurfaceTransforms(SurfaceControl surface, Rect position, long frameNumber) {
+        if (frameNumber > 0) {
+            final ViewRootImpl viewRoot = getViewRootImpl();
+
+            mRtTransaction.deferTransactionUntilSurface(surface, viewRoot.mSurface,
+                    frameNumber);
+        }
+
+        mRtTransaction.setPosition(surface, position.left, position.top);
+        mRtTransaction.setMatrix(surface,
+                position.width() / (float) mSurfaceWidth,
+                0.0f, 0.0f,
+                position.height() / (float) mSurfaceHeight);
+    }
+
+    private void setParentSpaceRectangle(Rect position, long frameNumber) {
+        final ViewRootImpl viewRoot = getViewRootImpl();
+
+        applySurfaceTransforms(mSurfaceControl, position, frameNumber);
+        applySurfaceTransforms(mSurfaceControl.mBackgroundControl, position, frameNumber);
+
+        applyChildSurfaceTransaction_renderWorker(mRtTransaction, viewRoot.mSurface,
+                frameNumber);
+
+        mRtTransaction.apply();
+    }
+
+    private Rect mRTLastReportedPosition = new Rect();
+
+    /**
+     * Called by native by a Rendering Worker thread to update the window position
+     * @hide
+     */
+    public final void updateSurfacePosition_renderWorker(long frameNumber,
+            int left, int top, int right, int bottom) {
+        if (mSurfaceControl == null) {
+            return;
+        }
+
+        // TODO: This is teensy bit racey in that a brand new SurfaceView moving on
+        // its 2nd frame if RenderThread is running slowly could potentially see
+        // this as false, enter the branch, get pre-empted, then this comes along
+        // and reports a new position, then the UI thread resumes and reports
+        // its position. This could therefore be de-sync'd in that interval, but
+        // the synchronization would violate the rule that RT must never block
+        // on the UI thread which would open up potential deadlocks. The risk of
+        // a single-frame desync is therefore preferable for now.
+        mRtHandlingPositionUpdates = true;
+        if (mRTLastReportedPosition.left == left
+                && mRTLastReportedPosition.top == top
+                && mRTLastReportedPosition.right == right
+                && mRTLastReportedPosition.bottom == bottom) {
+            return;
+        }
+        try {
+            if (DEBUG) {
+                Log.d(TAG, String.format("%d updateSurfacePosition RenderWorker, frameNr = %d, " +
+                        "postion = [%d, %d, %d, %d]", System.identityHashCode(this),
+                        frameNumber, left, top, right, bottom));
+            }
+            mRTLastReportedPosition.set(left, top, right, bottom);
+            setParentSpaceRectangle(mRTLastReportedPosition, frameNumber);
+            // Now overwrite mRTLastReportedPosition with our values
+        } catch (Exception ex) {
+            Log.e(TAG, "Exception from repositionChild", ex);
+        }
+    }
+
+    /**
+     * Called by native on RenderThread to notify that the view is no longer in the
+     * draw tree. UI thread is blocked at this point.
+     * @hide
+     */
+    public final void surfacePositionLost_uiRtSync(long frameNumber) {
+        if (DEBUG) {
+            Log.d(TAG, String.format("%d windowPositionLost, frameNr = %d",
+                    System.identityHashCode(this), frameNumber));
+        }
+        mRTLastReportedPosition.setEmpty();
+
+        if (mSurfaceControl == null) {
+            return;
+        }
+        if (mRtHandlingPositionUpdates) {
+            mRtHandlingPositionUpdates = false;
+            // This callback will happen while the UI thread is blocked, so we can
+            // safely access other member variables at this time.
+            // So do what the UI thread would have done if RT wasn't handling position
+            // updates.
+            if (!mScreenRect.isEmpty() && !mScreenRect.equals(mRTLastReportedPosition)) {
+                try {
+                    if (DEBUG) Log.d(TAG, String.format("%d updateSurfacePosition, " +
+                            "postion = [%d, %d, %d, %d]", System.identityHashCode(this),
+                            mScreenRect.left, mScreenRect.top,
+                            mScreenRect.right, mScreenRect.bottom));
+                    setParentSpaceRectangle(mScreenRect, frameNumber);
+                } catch (Exception ex) {
+                    Log.e(TAG, "Exception configuring surface", ex);
+                }
+            }
+        }
+    }
+
+    private SurfaceHolder.Callback[] getSurfaceCallbacks() {
+        SurfaceHolder.Callback callbacks[];
+        synchronized (mCallbacks) {
+            callbacks = new SurfaceHolder.Callback[mCallbacks.size()];
+            mCallbacks.toArray(callbacks);
+        }
+        return callbacks;
+    }
+
+    private void runOnUiThread(Runnable runnable) {
+        Handler handler = getHandler();
+        if (handler != null && handler.getLooper() != Looper.myLooper()) {
+            handler.post(runnable);
+        } else {
+            runnable.run();
+        }
+    }
+
+    /**
+     * Check to see if the surface has fixed size dimensions or if the surface's
+     * dimensions are dimensions are dependent on its current layout.
+     *
+     * @return true if the surface has dimensions that are fixed in size
+     * @hide
+     */
+    public boolean isFixedSize() {
+        return (mRequestedWidth != -1 || mRequestedHeight != -1);
+    }
+
+    private boolean isAboveParent() {
+        return mSubLayer >= 0;
+    }
+
+    /**
+     * Set an opaque background color to use with this {@link SurfaceView} when it's being resized
+     * and size of the content hasn't updated yet. This color will fill the expanded area when the
+     * view becomes larger.
+     * @param bgColor An opaque color to fill the background. Alpha component will be ignored.
+     * @hide
+     */
+    public void setResizeBackgroundColor(int bgColor) {
+        mSurfaceControl.setBackgroundColor(bgColor);
+    }
+
+    private final SurfaceHolder mSurfaceHolder = new SurfaceHolder() {
+        private static final String LOG_TAG = "SurfaceHolder";
 
         @Override
         public boolean isCreating() {
-            return false;
+            return mIsCreating;
         }
 
         @Override
         public void addCallback(Callback callback) {
+            synchronized (mCallbacks) {
+                // This is a linear search, but in practice we'll
+                // have only a couple callbacks, so it doesn't matter.
+                if (mCallbacks.contains(callback) == false) {
+                    mCallbacks.add(callback);
+                }
+            }
         }
 
         @Override
         public void removeCallback(Callback callback) {
+            synchronized (mCallbacks) {
+                mCallbacks.remove(callback);
+            }
         }
 
         @Override
         public void setFixedSize(int width, int height) {
+            if (mRequestedWidth != width || mRequestedHeight != height) {
+                mRequestedWidth = width;
+                mRequestedHeight = height;
+                requestLayout();
+            }
         }
 
         @Override
         public void setSizeFromLayout() {
+            if (mRequestedWidth != -1 || mRequestedHeight != -1) {
+                mRequestedWidth = mRequestedHeight = -1;
+                requestLayout();
+            }
         }
 
         @Override
         public void setFormat(int format) {
+            // for backward compatibility reason, OPAQUE always
+            // means 565 for SurfaceView
+            if (format == PixelFormat.OPAQUE)
+                format = PixelFormat.RGB_565;
+
+            mRequestedFormat = format;
+            if (mSurfaceControl != null) {
+                updateSurface();
+            }
         }
 
+        /**
+         * @deprecated setType is now ignored.
+         */
         @Override
-        public void setType(int type) {
-        }
+        @Deprecated
+        public void setType(int type) { }
 
         @Override
         public void setKeepScreenOn(boolean screenOn) {
+            runOnUiThread(() -> SurfaceView.this.setKeepScreenOn(screenOn));
         }
 
+        /**
+         * Gets a {@link Canvas} for drawing into the SurfaceView's Surface
+         *
+         * After drawing into the provided {@link Canvas}, the caller must
+         * invoke {@link #unlockCanvasAndPost} to post the new contents to the surface.
+         *
+         * The caller must redraw the entire surface.
+         * @return A canvas for drawing into the surface.
+         */
         @Override
         public Canvas lockCanvas() {
-            return null;
+            return internalLockCanvas(null, false);
+        }
+
+        /**
+         * Gets a {@link Canvas} for drawing into the SurfaceView's Surface
+         *
+         * After drawing into the provided {@link Canvas}, the caller must
+         * invoke {@link #unlockCanvasAndPost} to post the new contents to the surface.
+         *
+         * @param inOutDirty A rectangle that represents the dirty region that the caller wants
+         * to redraw.  This function may choose to expand the dirty rectangle if for example
+         * the surface has been resized or if the previous contents of the surface were
+         * not available.  The caller must redraw the entire dirty region as represented
+         * by the contents of the inOutDirty rectangle upon return from this function.
+         * The caller may also pass <code>null</code> instead, in the case where the
+         * entire surface should be redrawn.
+         * @return A canvas for drawing into the surface.
+         */
+        @Override
+        public Canvas lockCanvas(Rect inOutDirty) {
+            return internalLockCanvas(inOutDirty, false);
         }
 
         @Override
-        public Canvas lockCanvas(Rect dirty) {
+        public Canvas lockHardwareCanvas() {
+            return internalLockCanvas(null, true);
+        }
+
+        private Canvas internalLockCanvas(Rect dirty, boolean hardware) {
+            mSurfaceLock.lock();
+
+            if (DEBUG) Log.i(TAG, System.identityHashCode(this) + " " + "Locking canvas... stopped="
+                    + mDrawingStopped + ", surfaceControl=" + mSurfaceControl);
+
+            Canvas c = null;
+            if (!mDrawingStopped && mSurfaceControl != null) {
+                try {
+                    if (hardware) {
+                        c = mSurface.lockHardwareCanvas();
+                    } else {
+                        c = mSurface.lockCanvas(dirty);
+                    }
+                } catch (Exception e) {
+                    Log.e(LOG_TAG, "Exception locking surface", e);
+                }
+            }
+
+            if (DEBUG) Log.i(TAG, System.identityHashCode(this) + " " + "Returned canvas: " + c);
+            if (c != null) {
+                mLastLockTime = SystemClock.uptimeMillis();
+                return c;
+            }
+
+            // If the Surface is not ready to be drawn, then return null,
+            // but throttle calls to this function so it isn't called more
+            // than every 100ms.
+            long now = SystemClock.uptimeMillis();
+            long nextTime = mLastLockTime + 100;
+            if (nextTime > now) {
+                try {
+                    Thread.sleep(nextTime-now);
+                } catch (InterruptedException e) {
+                }
+                now = SystemClock.uptimeMillis();
+            }
+            mLastLockTime = now;
+            mSurfaceLock.unlock();
+
             return null;
         }
 
+        /**
+         * Posts the new contents of the {@link Canvas} to the surface and
+         * releases the {@link Canvas}.
+         *
+         * @param canvas The canvas previously obtained from {@link #lockCanvas}.
+         */
         @Override
         public void unlockCanvasAndPost(Canvas canvas) {
+            mSurface.unlockCanvasAndPost(canvas);
+            mSurfaceLock.unlock();
         }
 
         @Override
         public Surface getSurface() {
-            return null;
+            return mSurface;
         }
 
         @Override
         public Rect getSurfaceFrame() {
-            return null;
+            return mSurfaceFrame;
         }
     };
-}
 
+    class SurfaceControlWithBackground extends SurfaceControl {
+        SurfaceControl mBackgroundControl;
+        private boolean mOpaque = true;
+        public boolean mVisible = false;
+
+        public SurfaceControlWithBackground(String name, boolean opaque, SurfaceControl.Builder b)
+                       throws Exception {
+            super(b.setName(name).build());
+
+            mBackgroundControl = b.setName("Background for -" + name)
+                    .setFormat(OPAQUE)
+                    .setColorLayer(true)
+                    .build();
+            mOpaque = opaque;
+        }
+
+        @Override
+        public void setAlpha(float alpha) {
+            super.setAlpha(alpha);
+            mBackgroundControl.setAlpha(alpha);
+        }
+
+        @Override
+        public void setLayer(int zorder) {
+            super.setLayer(zorder);
+            // -3 is below all other child layers as SurfaceView never goes below -2
+            mBackgroundControl.setLayer(-3);
+        }
+
+        @Override
+        public void setPosition(float x, float y) {
+            super.setPosition(x, y);
+            mBackgroundControl.setPosition(x, y);
+        }
+
+        @Override
+        public void setSize(int w, int h) {
+            super.setSize(w, h);
+            mBackgroundControl.setSize(w, h);
+        }
+
+        @Override
+        public void setWindowCrop(Rect crop) {
+            super.setWindowCrop(crop);
+            mBackgroundControl.setWindowCrop(crop);
+        }
+
+        @Override
+        public void setFinalCrop(Rect crop) {
+            super.setFinalCrop(crop);
+            mBackgroundControl.setFinalCrop(crop);
+        }
+
+        @Override
+        public void setLayerStack(int layerStack) {
+            super.setLayerStack(layerStack);
+            mBackgroundControl.setLayerStack(layerStack);
+        }
+
+        @Override
+        public void setOpaque(boolean isOpaque) {
+            super.setOpaque(isOpaque);
+            mOpaque = isOpaque;
+            updateBackgroundVisibility();
+        }
+
+        @Override
+        public void setSecure(boolean isSecure) {
+            super.setSecure(isSecure);
+        }
+
+        @Override
+        public void setMatrix(float dsdx, float dtdx, float dsdy, float dtdy) {
+            super.setMatrix(dsdx, dtdx, dsdy, dtdy);
+            mBackgroundControl.setMatrix(dsdx, dtdx, dsdy, dtdy);
+        }
+
+        @Override
+        public void hide() {
+            super.hide();
+            mVisible = false;
+            updateBackgroundVisibility();
+        }
+
+        @Override
+        public void show() {
+            super.show();
+            mVisible = true;
+            updateBackgroundVisibility();
+        }
+
+        @Override
+        public void destroy() {
+            super.destroy();
+            mBackgroundControl.destroy();
+         }
+
+        @Override
+        public void release() {
+            super.release();
+            mBackgroundControl.release();
+        }
+
+        @Override
+        public void setTransparentRegionHint(Region region) {
+            super.setTransparentRegionHint(region);
+            mBackgroundControl.setTransparentRegionHint(region);
+        }
+
+        @Override
+        public void deferTransactionUntil(IBinder handle, long frame) {
+            super.deferTransactionUntil(handle, frame);
+            mBackgroundControl.deferTransactionUntil(handle, frame);
+        }
+
+        @Override
+        public void deferTransactionUntil(Surface barrier, long frame) {
+            super.deferTransactionUntil(barrier, frame);
+            mBackgroundControl.deferTransactionUntil(barrier, frame);
+        }
+
+        /** Set the color to fill the background with. */
+        private void setBackgroundColor(int bgColor) {
+            final float[] colorComponents = new float[] { Color.red(bgColor) / 255.f,
+                    Color.green(bgColor) / 255.f, Color.blue(bgColor) / 255.f };
+
+            SurfaceControl.openTransaction();
+            try {
+                mBackgroundControl.setColor(colorComponents);
+            } finally {
+                SurfaceControl.closeTransaction();
+            }
+        }
+
+        void updateBackgroundVisibility() {
+            if (mOpaque && mVisible) {
+                mBackgroundControl.show();
+            } else {
+                mBackgroundControl.hide();
+            }
+        }
+    }
+}
diff --git a/android/view/ThreadedRenderer.java b/android/view/ThreadedRenderer.java
index 5eb7e9c..e03f5fa 100644
--- a/android/view/ThreadedRenderer.java
+++ b/android/view/ThreadedRenderer.java
@@ -190,6 +190,10 @@
      */
     public static final String DEBUG_FPS_DIVISOR = "debug.hwui.fps_divisor";
 
+    public static int EGL_CONTEXT_PRIORITY_HIGH_IMG = 0x3101;
+    public static int EGL_CONTEXT_PRIORITY_MEDIUM_IMG = 0x3102;
+    public static int EGL_CONTEXT_PRIORITY_LOW_IMG = 0x3103;
+
     static {
         // Try to check OpenGL support early if possible.
         isAvailable();
@@ -1140,6 +1144,16 @@
         nHackySetRTAnimationsEnabled(divisor <= 1);
     }
 
+    /**
+     * Changes the OpenGL context priority if IMG_context_priority extension is available. Must be
+     * called before any OpenGL context is created.
+     *
+     * @param priority The priority to use. Must be one of EGL_CONTEXT_PRIORITY_* values.
+     */
+    public static void setContextPriority(int priority) {
+        nSetContextPriority(priority);
+    }
+
     /** Not actually public - internal use only. This doc to make lint happy */
     public static native void disableVsync();
 
@@ -1213,4 +1227,5 @@
     private static native void nHackySetRTAnimationsEnabled(boolean enabled);
     private static native void nSetDebuggingEnabled(boolean enabled);
     private static native void nSetIsolatedProcess(boolean enabled);
+    private static native void nSetContextPriority(int priority);
 }
diff --git a/android/view/View.java b/android/view/View.java
index 97e11b1..71b6084 100644
--- a/android/view/View.java
+++ b/android/view/View.java
@@ -697,6 +697,7 @@
  * security policy. See also {@link MotionEvent#FLAG_WINDOW_IS_OBSCURED}.
  * </p>
  *
+ * @attr ref android.R.styleable#View_accessibilityHeading
  * @attr ref android.R.styleable#View_alpha
  * @attr ref android.R.styleable#View_background
  * @attr ref android.R.styleable#View_clickable
@@ -2955,7 +2956,7 @@
      *     1                             PFLAG3_SCREEN_READER_FOCUSABLE
      *    1                              PFLAG3_AGGREGATED_VISIBLE
      *   1                               PFLAG3_AUTOFILLID_EXPLICITLY_SET
-     *  1                                available
+     *  1                                PFLAG3_ACCESSIBILITY_HEADING
      * |-------|-------|-------|-------|
      */
 
@@ -3252,6 +3253,11 @@
      */
     private static final int PFLAG3_AUTOFILLID_EXPLICITLY_SET = 0x40000000;
 
+    /**
+     * Indicates if the View is a heading for accessibility purposes
+     */
+    private static final int PFLAG3_ACCESSIBILITY_HEADING = 0x80000000;
+
     /* End of masks for mPrivateFlags3 */
 
     /**
@@ -5475,6 +5481,8 @@
                 case R.styleable.View_outlineAmbientShadowColor:
                     setOutlineAmbientShadowColor(a.getColor(attr, Color.BLACK));
                     break;
+                case com.android.internal.R.styleable.View_accessibilityHeading:
+                    setAccessibilityHeading(a.getBoolean(attr, false));
             }
         }
 
@@ -8795,6 +8803,7 @@
         info.addAction(AccessibilityAction.ACTION_SHOW_ON_SCREEN);
         populateAccessibilityNodeInfoDrawingOrderInParent(info);
         info.setPaneTitle(mAccessibilityPaneTitle);
+        info.setHeading(isAccessibilityHeading());
     }
 
     /**
@@ -10398,7 +10407,21 @@
      *
      * @param willNotCacheDrawing true if this view does not cache its
      *        drawing, false otherwise
+     *
+     * @deprecated The view drawing cache was largely made obsolete with the introduction of
+     * hardware-accelerated rendering in API 11. With hardware-acceleration, intermediate cache
+     * layers are largely unnecessary and can easily result in a net loss in performance due to the
+     * cost of creating and updating the layer. In the rare cases where caching layers are useful,
+     * such as for alpha animations, {@link #setLayerType(int, Paint)} handles this with hardware
+     * rendering. For software-rendered snapshots of a small part of the View hierarchy or
+     * individual Views it is recommended to create a {@link Canvas} from either a {@link Bitmap} or
+     * {@link android.graphics.Picture} and call {@link #draw(Canvas)} on the View. However these
+     * software-rendered usages are discouraged and have compatibility issues with hardware-only
+     * rendering features such as {@link android.graphics.Bitmap.Config#HARDWARE Config.HARDWARE}
+     * bitmaps, real-time shadows, and outline clipping. For screenshots of the UI for feedback
+     * reports or unit testing the {@link PixelCopy} API is recommended.
      */
+    @Deprecated
     public void setWillNotCacheDrawing(boolean willNotCacheDrawing) {
         setFlags(willNotCacheDrawing ? WILL_NOT_CACHE_DRAWING : 0, WILL_NOT_CACHE_DRAWING);
     }
@@ -10407,8 +10430,22 @@
      * Returns whether or not this View can cache its drawing or not.
      *
      * @return true if this view does not cache its drawing, false otherwise
+     *
+     * @deprecated The view drawing cache was largely made obsolete with the introduction of
+     * hardware-accelerated rendering in API 11. With hardware-acceleration, intermediate cache
+     * layers are largely unnecessary and can easily result in a net loss in performance due to the
+     * cost of creating and updating the layer. In the rare cases where caching layers are useful,
+     * such as for alpha animations, {@link #setLayerType(int, Paint)} handles this with hardware
+     * rendering. For software-rendered snapshots of a small part of the View hierarchy or
+     * individual Views it is recommended to create a {@link Canvas} from either a {@link Bitmap} or
+     * {@link android.graphics.Picture} and call {@link #draw(Canvas)} on the View. However these
+     * software-rendered usages are discouraged and have compatibility issues with hardware-only
+     * rendering features such as {@link android.graphics.Bitmap.Config#HARDWARE Config.HARDWARE}
+     * bitmaps, real-time shadows, and outline clipping. For screenshots of the UI for feedback
+     * reports or unit testing the {@link PixelCopy} API is recommended.
      */
     @ViewDebug.ExportedProperty(category = "drawing")
+    @Deprecated
     public boolean willNotCacheDrawing() {
         return (mViewFlags & WILL_NOT_CACHE_DRAWING) == WILL_NOT_CACHE_DRAWING;
     }
@@ -10754,11 +10791,37 @@
      *                              accessibility tools.
      */
     public void setScreenReaderFocusable(boolean screenReaderFocusable) {
+        updatePflags3AndNotifyA11yIfChanged(PFLAG3_SCREEN_READER_FOCUSABLE, screenReaderFocusable);
+    }
+
+    /**
+     * Gets whether this view is a heading for accessibility purposes.
+     *
+     * @return {@code true} if the view is a heading, {@code false} otherwise.
+     *
+     * @attr ref android.R.styleable#View_accessibilityHeading
+     */
+    public boolean isAccessibilityHeading() {
+        return (mPrivateFlags3 & PFLAG3_ACCESSIBILITY_HEADING) != 0;
+    }
+
+    /**
+     * Set if view is a heading for a section of content for accessibility purposes.
+     *
+     * @param isHeading {@code true} if the view is a heading, {@code false} otherwise.
+     *
+     * @attr ref android.R.styleable#View_accessibilityHeading
+     */
+    public void setAccessibilityHeading(boolean isHeading) {
+        updatePflags3AndNotifyA11yIfChanged(PFLAG3_ACCESSIBILITY_HEADING, isHeading);
+    }
+
+    private void updatePflags3AndNotifyA11yIfChanged(int mask, boolean newValue) {
         int pflags3 = mPrivateFlags3;
-        if (screenReaderFocusable) {
-            pflags3 |= PFLAG3_SCREEN_READER_FOCUSABLE;
+        if (newValue) {
+            pflags3 |= mask;
         } else {
-            pflags3 &= ~PFLAG3_SCREEN_READER_FOCUSABLE;
+            pflags3 &= ~mask;
         }
 
         if (pflags3 != mPrivateFlags3) {
@@ -11763,6 +11826,14 @@
         return null;
     }
 
+    /** @hide */
+    View getSelfOrParentImportantForA11y() {
+        if (isImportantForAccessibility()) return this;
+        ViewParent parent = getParentForAccessibility();
+        if (parent instanceof View) return (View) parent;
+        return null;
+    }
+
     /**
      * Adds the children of this View relevant for accessibility to the given list
      * as output. Since some Views are not important for accessibility the added
@@ -14978,10 +15049,7 @@
     public void setAlpha(@FloatRange(from=0.0, to=1.0) float alpha) {
         ensureTransformationInfo();
         if (mTransformationInfo.mAlpha != alpha) {
-            // Report visibility changes, which can affect children, to accessibility
-            if ((alpha == 0) ^ (mTransformationInfo.mAlpha == 0)) {
-                notifySubtreeAccessibilityStateChangedIfNeeded();
-            }
+            float oldAlpha = mTransformationInfo.mAlpha;
             mTransformationInfo.mAlpha = alpha;
             if (onSetAlpha((int) (alpha * 255))) {
                 mPrivateFlags |= PFLAG_ALPHA_SET;
@@ -14993,6 +15061,10 @@
                 invalidateViewProperty(true, false);
                 mRenderNode.setAlpha(getFinalAlpha());
             }
+            // Report visibility changes, which can affect children, to accessibility
+            if ((alpha == 0) ^ (oldAlpha == 0)) {
+                notifySubtreeAccessibilityStateChangedIfNeeded();
+            }
         }
     }
 
diff --git a/android/view/ViewGroup.java b/android/view/ViewGroup.java
index 6002fe5..2ec42c0 100644
--- a/android/view/ViewGroup.java
+++ b/android/view/ViewGroup.java
@@ -5692,6 +5692,7 @@
         }
         dispatchVisibilityAggregated(isAttachedToWindow() && getWindowVisibility() == VISIBLE
                 && isShown());
+        notifySubtreeAccessibilityStateChangedIfNeeded();
     }
 
     /**
diff --git a/android/view/ViewRootImpl.java b/android/view/ViewRootImpl.java
index 433c90b..730c372 100644
--- a/android/view/ViewRootImpl.java
+++ b/android/view/ViewRootImpl.java
@@ -3719,7 +3719,7 @@
         checkThread();
         if (mView != null) {
             if (!mView.hasFocus()) {
-                if (sAlwaysAssignFocus || !isInTouchMode()) {
+                if (sAlwaysAssignFocus || !mAttachInfo.mInTouchMode) {
                     v.requestFocus();
                 }
             } else {
@@ -6482,17 +6482,17 @@
                     params.type = mOrigWindowType;
                 }
             }
-
-            if (mSurface.isValid()) {
-                params.frameNumber = mSurface.getNextFrameNumber();
-            }
         }
 
-        int relayoutResult = mWindowSession.relayout(
-                mWindow, mSeq, params,
+        long frameNumber = -1;
+        if (mSurface.isValid()) {
+            frameNumber = mSurface.getNextFrameNumber();
+        }
+
+        int relayoutResult = mWindowSession.relayout(mWindow, mSeq, params,
                 (int) (mView.getMeasuredWidth() * appScale + 0.5f),
-                (int) (mView.getMeasuredHeight() * appScale + 0.5f),
-                viewVisibility, insetsPending ? WindowManagerGlobal.RELAYOUT_INSETS_PENDING : 0,
+                (int) (mView.getMeasuredHeight() * appScale + 0.5f), viewVisibility,
+                insetsPending ? WindowManagerGlobal.RELAYOUT_INSETS_PENDING : 0, frameNumber,
                 mWinFrame, mPendingOverscanInsets, mPendingContentInsets, mPendingVisibleInsets,
                 mPendingStableInsets, mPendingOutsets, mPendingBackDropFrame, mPendingDisplayCutout,
                 mPendingMergedConfiguration, mSurface);
@@ -8305,6 +8305,12 @@
 
         public View mSource;
         public long mLastEventTimeMillis;
+        /**
+         * Override for {@link AccessibilityEvent#originStackTrace} to provide the stack trace
+         * of the original {@link #runOrPost} call instead of one for sending the delayed event
+         * from a looper.
+         */
+        public StackTraceElement[] mOrigin;
 
         @Override
         public void run() {
@@ -8322,6 +8328,7 @@
                 AccessibilityEvent event = AccessibilityEvent.obtain();
                 event.setEventType(AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED);
                 event.setContentChangeTypes(mChangeTypes);
+                if (AccessibilityEvent.DEBUG_ORIGIN) event.originStackTrace = mOrigin;
                 source.sendAccessibilityEventUnchecked(event);
             } else {
                 mLastEventTimeMillis = 0;
@@ -8329,6 +8336,7 @@
             // In any case reset to initial state.
             source.resetSubtreeAccessibilityStateChanged();
             mChangeTypes = 0;
+            if (AccessibilityEvent.DEBUG_ORIGIN) mOrigin = null;
         }
 
         public void runOrPost(View source, int changeType) {
@@ -8352,12 +8360,18 @@
                 // If there is no common predecessor, then mSource points to
                 // a removed view, hence in this case always prefer the source.
                 View predecessor = getCommonPredecessor(mSource, source);
+                if (predecessor != null) {
+                    predecessor = predecessor.getSelfOrParentImportantForA11y();
+                }
                 mSource = (predecessor != null) ? predecessor : source;
                 mChangeTypes |= changeType;
                 return;
             }
             mSource = source;
             mChangeTypes = changeType;
+            if (AccessibilityEvent.DEBUG_ORIGIN) {
+                mOrigin = Thread.currentThread().getStackTrace();
+            }
             final long timeSinceLastMillis = SystemClock.uptimeMillis() - mLastEventTimeMillis;
             final long minEventIntevalMillis =
                     ViewConfiguration.getSendRecurringAccessibilityEventsInterval();
diff --git a/android/view/WindowManager.java b/android/view/WindowManager.java
index f6181d7..0f5c23f 100644
--- a/android/view/WindowManager.java
+++ b/android/view/WindowManager.java
@@ -2438,13 +2438,6 @@
         public long hideTimeoutMilliseconds = -1;
 
         /**
-         * A frame number in which changes requested in this layout will be rendered.
-         *
-         * @hide
-         */
-        public long frameNumber = -1;
-
-        /**
          * The color mode requested by this window. The target display may
          * not be able to honor the request. When the color mode is not set
          * to {@link ActivityInfo#COLOR_MODE_DEFAULT}, it might override the
@@ -2617,7 +2610,6 @@
             TextUtils.writeToParcel(accessibilityTitle, out, parcelableFlags);
             out.writeInt(mColorMode);
             out.writeLong(hideTimeoutMilliseconds);
-            out.writeLong(frameNumber);
         }
 
         public static final Parcelable.Creator<LayoutParams> CREATOR
@@ -2674,7 +2666,6 @@
             accessibilityTitle = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
             mColorMode = in.readInt();
             hideTimeoutMilliseconds = in.readLong();
-            frameNumber = in.readLong();
         }
 
         @SuppressWarnings({"PointlessBitwiseExpression"})
@@ -2875,10 +2866,6 @@
                 changes |= SURFACE_INSETS_CHANGED;
             }
 
-            // The frame number changing is only relevant in the context of other
-            // changes, and so we don't need to track it with a flag.
-            frameNumber = o.frameNumber;
-
             if (hasManualSurfaceInsets != o.hasManualSurfaceInsets) {
                 hasManualSurfaceInsets = o.hasManualSurfaceInsets;
                 changes |= SURFACE_INSETS_CHANGED;
diff --git a/android/view/WindowManagerGlobal.java b/android/view/WindowManagerGlobal.java
index cca66d6..08c2d0b 100644
--- a/android/view/WindowManagerGlobal.java
+++ b/android/view/WindowManagerGlobal.java
@@ -610,6 +610,10 @@
                     ViewRootImpl root = mRoots.get(i);
                     // Client might remove the view by "stopped" event.
                     root.setWindowStopped(stopped);
+                    // Recursively forward stopped state to View's attached
+                    // to this Window rather than the root application token,
+                    // e.g. PopupWindow's.
+                    setStoppedState(root.mAttachInfo.mWindowToken, stopped);
                 }
             }
         }
diff --git a/android/view/accessibility/AccessibilityEvent.java b/android/view/accessibility/AccessibilityEvent.java
index e0f74a7..7946e9e 100644
--- a/android/view/accessibility/AccessibilityEvent.java
+++ b/android/view/accessibility/AccessibilityEvent.java
@@ -201,6 +201,7 @@
  * <em>Properties:</em></br>
  * <ul>
  *   <li>{@link #getEventType()} - The type of the event.</li>
+ *   <li>{@link #getContentChangeTypes()} - The type of state changes.</li>
  *   <li>{@link #getSource()} - The source info (for registered clients).</li>
  *   <li>{@link #getClassName()} - The class name of the source.</li>
  *   <li>{@link #getPackageName()} - The package name of the source.</li>
@@ -388,6 +389,8 @@
  */
 public final class AccessibilityEvent extends AccessibilityRecord implements Parcelable {
     private static final boolean DEBUG = false;
+    /** @hide */
+    public static final boolean DEBUG_ORIGIN = false;
 
     /**
      * Invalid selection/focus position.
@@ -748,7 +751,7 @@
 
     private static final int MAX_POOL_SIZE = 10;
     private static final SynchronizedPool<AccessibilityEvent> sPool =
-            new SynchronizedPool<AccessibilityEvent>(MAX_POOL_SIZE);
+            new SynchronizedPool<>(MAX_POOL_SIZE);
 
     private @EventType int mEventType;
     private CharSequence mPackageName;
@@ -758,6 +761,17 @@
     int mContentChangeTypes;
     int mWindowChangeTypes;
 
+    /**
+     * The stack trace describing where this event originated from on the app side.
+     * Only populated if {@link #DEBUG_ORIGIN} is enabled
+     * Can be inspected(e.g. printed) from an
+     * {@link android.accessibilityservice.AccessibilityService} to trace where particular events
+     * are being dispatched from.
+     *
+     * @hide
+     */
+    public StackTraceElement[] originStackTrace = null;
+
     private ArrayList<AccessibilityRecord> mRecords;
 
     /*
@@ -780,6 +794,7 @@
         mWindowChangeTypes = event.mWindowChangeTypes;
         mEventTime = event.mEventTime;
         mPackageName = event.mPackageName;
+        if (DEBUG_ORIGIN) originStackTrace = event.originStackTrace;
     }
 
     /**
@@ -849,16 +864,17 @@
     }
 
     /**
-     * Gets the bit mask of change types signaled by an
-     * {@link #TYPE_WINDOW_CONTENT_CHANGED} event. A single event may represent
-     * multiple change types.
+     * Gets the bit mask of change types signaled by a
+     * {@link #TYPE_WINDOW_CONTENT_CHANGED} event or {@link #TYPE_WINDOW_STATE_CHANGED}. A single
+     * event may represent multiple change types.
      *
      * @return The bit mask of change types. One or more of:
      *         <ul>
-     *         <li>{@link AccessibilityEvent#CONTENT_CHANGE_TYPE_CONTENT_DESCRIPTION}
-     *         <li>{@link AccessibilityEvent#CONTENT_CHANGE_TYPE_SUBTREE}
-     *         <li>{@link AccessibilityEvent#CONTENT_CHANGE_TYPE_TEXT}
-     *         <li>{@link AccessibilityEvent#CONTENT_CHANGE_TYPE_UNDEFINED}
+     *         <li>{@link #CONTENT_CHANGE_TYPE_CONTENT_DESCRIPTION}
+     *         <li>{@link #CONTENT_CHANGE_TYPE_SUBTREE}
+     *         <li>{@link #CONTENT_CHANGE_TYPE_TEXT}
+     *         <li>{@link #CONTENT_CHANGE_TYPE_PANE_TITLE}
+     *         <li>{@link #CONTENT_CHANGE_TYPE_UNDEFINED}
      *         </ul>
      */
     @ContentChangeTypes
@@ -877,6 +893,7 @@
             }
             case CONTENT_CHANGE_TYPE_SUBTREE: return "CONTENT_CHANGE_TYPE_SUBTREE";
             case CONTENT_CHANGE_TYPE_TEXT: return "CONTENT_CHANGE_TYPE_TEXT";
+            case CONTENT_CHANGE_TYPE_PANE_TITLE: return "CONTENT_CHANGE_TYPE_PANE_TITLE";
             case CONTENT_CHANGE_TYPE_UNDEFINED: return "CONTENT_CHANGE_TYPE_UNDEFINED";
             default: return Integer.toHexString(type);
         }
@@ -1104,7 +1121,9 @@
      */
     public static AccessibilityEvent obtain() {
         AccessibilityEvent event = sPool.acquire();
-        return (event != null) ? event : new AccessibilityEvent();
+        if (event == null) event = new AccessibilityEvent();
+        if (DEBUG_ORIGIN) event.originStackTrace = Thread.currentThread().getStackTrace();
+        return event;
     }
 
     /**
@@ -1142,6 +1161,7 @@
                 record.recycle();
             }
         }
+        if (DEBUG_ORIGIN) originStackTrace = null;
     }
 
     /**
@@ -1164,7 +1184,7 @@
         // Read the records.
         final int recordCount = parcel.readInt();
         if (recordCount > 0) {
-            mRecords = new ArrayList<AccessibilityRecord>(recordCount);
+            mRecords = new ArrayList<>(recordCount);
             for (int i = 0; i < recordCount; i++) {
                 AccessibilityRecord record = AccessibilityRecord.obtain();
                 readAccessibilityRecordFromParcel(record, parcel);
@@ -1172,6 +1192,17 @@
                 mRecords.add(record);
             }
         }
+
+        if (DEBUG_ORIGIN) {
+            originStackTrace = new StackTraceElement[parcel.readInt()];
+            for (int i = 0; i < originStackTrace.length; i++) {
+                originStackTrace[i] = new StackTraceElement(
+                        parcel.readString(),
+                        parcel.readString(),
+                        parcel.readString(),
+                        parcel.readInt());
+            }
+        }
     }
 
     /**
@@ -1227,6 +1258,17 @@
             AccessibilityRecord record = mRecords.get(i);
             writeAccessibilityRecordToParcel(record, parcel, flags);
         }
+
+        if (DEBUG_ORIGIN) {
+            if (originStackTrace == null) originStackTrace = Thread.currentThread().getStackTrace();
+            parcel.writeInt(originStackTrace.length);
+            for (StackTraceElement element : originStackTrace) {
+                parcel.writeString(element.getClassName());
+                parcel.writeString(element.getMethodName());
+                parcel.writeString(element.getFileName());
+                parcel.writeInt(element.getLineNumber());
+            }
+        }
     }
 
     /**
@@ -1285,7 +1327,7 @@
         }
         if (!DEBUG_CONCISE_TOSTRING || mWindowChangeTypes != 0) {
             builder.append("; WindowChangeTypes: ").append(
-                    contentChangeTypesToString(mWindowChangeTypes));
+                    windowChangeTypesToString(mWindowChangeTypes));
         }
         super.appendTo(builder);
         if (DEBUG || DEBUG_CONCISE_TOSTRING) {
diff --git a/android/view/accessibility/AccessibilityInteractionClient.java b/android/view/accessibility/AccessibilityInteractionClient.java
index 72af203..d60c481 100644
--- a/android/view/accessibility/AccessibilityInteractionClient.java
+++ b/android/view/accessibility/AccessibilityInteractionClient.java
@@ -326,12 +326,14 @@
                             accessibilityWindowId, accessibilityNodeId);
                     if (cachedInfo != null) {
                         if (DEBUG) {
-                            Log.i(LOG_TAG, "Node cache hit");
+                            Log.i(LOG_TAG, "Node cache hit for "
+                                    + idToString(accessibilityWindowId, accessibilityNodeId));
                         }
                         return cachedInfo;
                     }
                     if (DEBUG) {
-                        Log.i(LOG_TAG, "Node cache miss");
+                        Log.i(LOG_TAG, "Node cache miss for "
+                                + idToString(accessibilityWindowId, accessibilityNodeId));
                     }
                 }
                 final int interactionId = mInteractionIdCounter.getAndIncrement();
@@ -368,6 +370,11 @@
         return null;
     }
 
+    private static String idToString(int accessibilityWindowId, long accessibilityNodeId) {
+        return accessibilityWindowId + "/"
+                + AccessibilityNodeInfo.idToString(accessibilityNodeId);
+    }
+
     /**
      * Finds an {@link AccessibilityNodeInfo} by View id. The search is performed in
      * the window whose id is specified and starts from the node whose accessibility
diff --git a/android/view/accessibility/AccessibilityManager.java b/android/view/accessibility/AccessibilityManager.java
index 84b4064..cbb23f1 100644
--- a/android/view/accessibility/AccessibilityManager.java
+++ b/android/view/accessibility/AccessibilityManager.java
@@ -16,48 +16,156 @@
 
 package android.view.accessibility;
 
+import static android.accessibilityservice.AccessibilityServiceInfo.FLAG_ENABLE_ACCESSIBILITY_VOLUME;
+
+import android.Manifest;
 import android.accessibilityservice.AccessibilityServiceInfo;
 import android.accessibilityservice.AccessibilityServiceInfo.FeedbackType;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.annotation.SdkConstant;
+import android.annotation.SystemService;
+import android.content.ComponentName;
 import android.content.Context;
+import android.content.pm.PackageManager;
 import android.content.pm.ServiceInfo;
+import android.content.res.Resources;
+import android.os.Binder;
 import android.os.Handler;
+import android.os.IBinder;
+import android.os.Looper;
+import android.os.Message;
+import android.os.Process;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.os.SystemClock;
+import android.os.UserHandle;
+import android.util.ArrayMap;
+import android.util.Log;
+import android.util.SparseArray;
 import android.view.IWindow;
 import android.view.View;
 import android.view.accessibility.AccessibilityEvent.EventType;
 
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.IntPair;
+
+import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
 
 /**
- * System level service that serves as an event dispatch for {@link AccessibilityEvent}s.
- * Such events are generated when something notable happens in the user interface,
+ * System level service that serves as an event dispatch for {@link AccessibilityEvent}s,
+ * and provides facilities for querying the accessibility state of the system.
+ * Accessibility events are generated when something notable happens in the user interface,
  * for example an {@link android.app.Activity} starts, the focus or selection of a
  * {@link android.view.View} changes etc. Parties interested in handling accessibility
  * events implement and register an accessibility service which extends
- * {@code android.accessibilityservice.AccessibilityService}.
+ * {@link android.accessibilityservice.AccessibilityService}.
  *
  * @see AccessibilityEvent
- * @see android.content.Context#getSystemService
+ * @see AccessibilityNodeInfo
+ * @see android.accessibilityservice.AccessibilityService
+ * @see Context#getSystemService
+ * @see Context#ACCESSIBILITY_SERVICE
  */
-@SuppressWarnings("UnusedDeclaration")
+@SystemService(Context.ACCESSIBILITY_SERVICE)
 public final class AccessibilityManager {
+    private static final boolean DEBUG = false;
 
-    private static AccessibilityManager sInstance = new AccessibilityManager(null, null, 0);
+    private static final String LOG_TAG = "AccessibilityManager";
 
+    /** @hide */
+    public static final int STATE_FLAG_ACCESSIBILITY_ENABLED = 0x00000001;
+
+    /** @hide */
+    public static final int STATE_FLAG_TOUCH_EXPLORATION_ENABLED = 0x00000002;
+
+    /** @hide */
+    public static final int STATE_FLAG_HIGH_TEXT_CONTRAST_ENABLED = 0x00000004;
+
+    /** @hide */
+    public static final int DALTONIZER_DISABLED = -1;
+
+    /** @hide */
+    public static final int DALTONIZER_SIMULATE_MONOCHROMACY = 0;
+
+    /** @hide */
+    public static final int DALTONIZER_CORRECT_DEUTERANOMALY = 12;
+
+    /** @hide */
+    public static final int AUTOCLICK_DELAY_DEFAULT = 600;
 
     /**
-     * Listener for the accessibility state.
+     * Activity action: Launch UI to manage which accessibility service or feature is assigned
+     * to the navigation bar Accessibility button.
+     * <p>
+     * Input: Nothing.
+     * </p>
+     * <p>
+     * Output: Nothing.
+     * </p>
+     *
+     * @hide
+     */
+    @SdkConstant(SdkConstant.SdkConstantType.ACTIVITY_INTENT_ACTION)
+    public static final String ACTION_CHOOSE_ACCESSIBILITY_BUTTON =
+            "com.android.internal.intent.action.CHOOSE_ACCESSIBILITY_BUTTON";
+
+    static final Object sInstanceSync = new Object();
+
+    private static AccessibilityManager sInstance;
+
+    private final Object mLock = new Object();
+
+    private IAccessibilityManager mService;
+
+    final int mUserId;
+
+    final Handler mHandler;
+
+    final Handler.Callback mCallback;
+
+    boolean mIsEnabled;
+
+    int mRelevantEventTypes = AccessibilityEvent.TYPES_ALL_MASK;
+
+    boolean mIsTouchExplorationEnabled;
+
+    boolean mIsHighTextContrastEnabled;
+
+    AccessibilityPolicy mAccessibilityPolicy;
+
+    private final ArrayMap<AccessibilityStateChangeListener, Handler>
+            mAccessibilityStateChangeListeners = new ArrayMap<>();
+
+    private final ArrayMap<TouchExplorationStateChangeListener, Handler>
+            mTouchExplorationStateChangeListeners = new ArrayMap<>();
+
+    private final ArrayMap<HighTextContrastChangeListener, Handler>
+            mHighTextContrastStateChangeListeners = new ArrayMap<>();
+
+    private final ArrayMap<AccessibilityServicesStateChangeListener, Handler>
+            mServicesStateChangeListeners = new ArrayMap<>();
+
+    /**
+     * Map from a view's accessibility id to the list of request preparers set for that view
+     */
+    private SparseArray<List<AccessibilityRequestPreparer>> mRequestPreparerLists;
+
+    /**
+     * Listener for the system accessibility state. To listen for changes to the
+     * accessibility state on the device, implement this interface and register
+     * it with the system by calling {@link #addAccessibilityStateChangeListener}.
      */
     public interface AccessibilityStateChangeListener {
 
         /**
-         * Called back on change in the accessibility state.
+         * Called when the accessibility enabled state changes.
          *
          * @param enabled Whether accessibility is enabled.
          */
-        public void onAccessibilityStateChanged(boolean enabled);
+        void onAccessibilityStateChanged(boolean enabled);
     }
 
     /**
@@ -73,7 +181,24 @@
          *
          * @param enabled Whether touch exploration is enabled.
          */
-        public void onTouchExplorationStateChanged(boolean enabled);
+        void onTouchExplorationStateChanged(boolean enabled);
+    }
+
+    /**
+     * Listener for changes to the state of accessibility services. Changes include services being
+     * enabled or disabled, or changes to the {@link AccessibilityServiceInfo} of a running service.
+     * {@see #addAccessibilityServicesStateChangeListener}.
+     *
+     * @hide
+     */
+    public interface AccessibilityServicesStateChangeListener {
+
+        /**
+         * Called when the state of accessibility services changes.
+         *
+         * @param manager The manager that is calling back
+         */
+        void onAccessibilityServicesStateChanged(AccessibilityManager manager);
     }
 
     /**
@@ -81,6 +206,8 @@
      * the high text contrast state on the device, implement this interface and
      * register it with the system by calling
      * {@link #addHighTextContrastStateChangeListener}.
+     *
+     * @hide
      */
     public interface HighTextContrastChangeListener {
 
@@ -89,7 +216,7 @@
          *
          * @param enabled Whether high text contrast is enabled.
          */
-        public void onHighTextContrastStateChanged(boolean enabled);
+        void onHighTextContrastStateChanged(boolean enabled);
     }
 
     /**
@@ -148,21 +275,67 @@
 
     private final IAccessibilityManagerClient.Stub mClient =
             new IAccessibilityManagerClient.Stub() {
-                public void setState(int state) {
-                }
+        @Override
+        public void setState(int state) {
+            // We do not want to change this immediately as the application may
+            // have already checked that accessibility is on and fired an event,
+            // that is now propagating up the view tree, Hence, if accessibility
+            // is now off an exception will be thrown. We want to have the exception
+            // enforcement to guard against apps that fire unnecessary accessibility
+            // events when accessibility is off.
+            mHandler.obtainMessage(MyCallback.MSG_SET_STATE, state, 0).sendToTarget();
+        }
 
-                public void notifyServicesStateChanged() {
+        @Override
+        public void notifyServicesStateChanged() {
+            final ArrayMap<AccessibilityServicesStateChangeListener, Handler> listeners;
+            synchronized (mLock) {
+                if (mServicesStateChangeListeners.isEmpty()) {
+                    return;
                 }
+                listeners = new ArrayMap<>(mServicesStateChangeListeners);
+            }
 
-                public void setRelevantEventTypes(int eventTypes) {
-                }
-            };
+            int numListeners = listeners.size();
+            for (int i = 0; i < numListeners; i++) {
+                final AccessibilityServicesStateChangeListener listener =
+                        mServicesStateChangeListeners.keyAt(i);
+                mServicesStateChangeListeners.valueAt(i).post(() -> listener
+                        .onAccessibilityServicesStateChanged(AccessibilityManager.this));
+            }
+        }
+
+        @Override
+        public void setRelevantEventTypes(int eventTypes) {
+            mRelevantEventTypes = eventTypes;
+        }
+    };
 
     /**
      * Get an AccessibilityManager instance (create one if necessary).
      *
+     * @param context Context in which this manager operates.
+     *
+     * @hide
      */
     public static AccessibilityManager getInstance(Context context) {
+        synchronized (sInstanceSync) {
+            if (sInstance == null) {
+                final int userId;
+                if (Binder.getCallingUid() == Process.SYSTEM_UID
+                        || context.checkCallingOrSelfPermission(
+                                Manifest.permission.INTERACT_ACROSS_USERS)
+                                        == PackageManager.PERMISSION_GRANTED
+                        || context.checkCallingOrSelfPermission(
+                                Manifest.permission.INTERACT_ACROSS_USERS_FULL)
+                                        == PackageManager.PERMISSION_GRANTED) {
+                    userId = UserHandle.USER_CURRENT;
+                } else {
+                    userId = context.getUserId();
+                }
+                sInstance = new AccessibilityManager(context, null, userId);
+            }
+        }
         return sInstance;
     }
 
@@ -170,21 +343,65 @@
      * Create an instance.
      *
      * @param context A {@link Context}.
+     * @param service An interface to the backing service.
+     * @param userId User id under which to run.
+     *
+     * @hide
      */
     public AccessibilityManager(Context context, IAccessibilityManager service, int userId) {
+        // Constructor can't be chained because we can't create an instance of an inner class
+        // before calling another constructor.
+        mCallback = new MyCallback();
+        mHandler = new Handler(context.getMainLooper(), mCallback);
+        mUserId = userId;
+        synchronized (mLock) {
+            tryConnectToServiceLocked(service);
+        }
     }
 
+    /**
+     * Create an instance.
+     *
+     * @param handler The handler to use
+     * @param service An interface to the backing service.
+     * @param userId User id under which to run.
+     *
+     * @hide
+     */
+    public AccessibilityManager(Handler handler, IAccessibilityManager service, int userId) {
+        mCallback = new MyCallback();
+        mHandler = handler;
+        mUserId = userId;
+        synchronized (mLock) {
+            tryConnectToServiceLocked(service);
+        }
+    }
+
+    /**
+     * @hide
+     */
     public IAccessibilityManagerClient getClient() {
         return mClient;
     }
 
     /**
-     * Returns if the {@link AccessibilityManager} is enabled.
+     * @hide
+     */
+    @VisibleForTesting
+    public Handler.Callback getCallback() {
+        return mCallback;
+    }
+
+    /**
+     * Returns if the accessibility in the system is enabled.
      *
-     * @return True if this {@link AccessibilityManager} is enabled, false otherwise.
+     * @return True if accessibility is enabled, false otherwise.
      */
     public boolean isEnabled() {
-        return false;
+        synchronized (mLock) {
+            return mIsEnabled || (mAccessibilityPolicy != null
+                    && mAccessibilityPolicy.isEnabled(mIsEnabled));
+        }
     }
 
     /**
@@ -193,7 +410,13 @@
      * @return True if touch exploration is enabled, false otherwise.
      */
     public boolean isTouchExplorationEnabled() {
-        return true;
+        synchronized (mLock) {
+            IAccessibilityManager service = getServiceLocked();
+            if (service == null) {
+                return false;
+            }
+            return mIsTouchExplorationEnabled;
+        }
     }
 
     /**
@@ -203,47 +426,188 @@
      * doing its own rendering and does not rely on the platform rendering pipeline.
      * </p>
      *
+     * @return True if high text contrast is enabled, false otherwise.
+     *
+     * @hide
      */
     public boolean isHighTextContrastEnabled() {
-        return false;
+        synchronized (mLock) {
+            IAccessibilityManager service = getServiceLocked();
+            if (service == null) {
+                return false;
+            }
+            return mIsHighTextContrastEnabled;
+        }
     }
 
     /**
      * Sends an {@link AccessibilityEvent}.
+     *
+     * @param event The event to send.
+     *
+     * @throws IllegalStateException if accessibility is not enabled.
+     *
+     * <strong>Note:</strong> The preferred mechanism for sending custom accessibility
+     * events is through calling
+     * {@link android.view.ViewParent#requestSendAccessibilityEvent(View, AccessibilityEvent)}
+     * instead of this method to allow predecessors to augment/filter events sent by
+     * their descendants.
      */
     public void sendAccessibilityEvent(AccessibilityEvent event) {
+        final IAccessibilityManager service;
+        final int userId;
+        final AccessibilityEvent dispatchedEvent;
+        synchronized (mLock) {
+            service = getServiceLocked();
+            if (service == null) {
+                return;
+            }
+            event.setEventTime(SystemClock.uptimeMillis());
+            if (mAccessibilityPolicy != null) {
+                dispatchedEvent = mAccessibilityPolicy.onAccessibilityEvent(event,
+                        mIsEnabled, mRelevantEventTypes);
+                if (dispatchedEvent == null) {
+                    return;
+                }
+            } else {
+                dispatchedEvent = event;
+            }
+            if (!isEnabled()) {
+                Looper myLooper = Looper.myLooper();
+                if (myLooper == Looper.getMainLooper()) {
+                    throw new IllegalStateException(
+                            "Accessibility off. Did you forget to check that?");
+                } else {
+                    // If we're not running on the thread with the main looper, it's possible for
+                    // the state of accessibility to change between checking isEnabled and
+                    // calling this method. So just log the error rather than throwing the
+                    // exception.
+                    Log.e(LOG_TAG, "AccessibilityEvent sent with accessibility disabled");
+                    return;
+                }
+            }
+            if ((dispatchedEvent.getEventType() & mRelevantEventTypes) == 0) {
+                if (DEBUG) {
+                    Log.i(LOG_TAG, "Not dispatching irrelevant event: " + dispatchedEvent
+                            + " that is not among "
+                            + AccessibilityEvent.eventTypeToString(mRelevantEventTypes));
+                }
+                return;
+            }
+            userId = mUserId;
+        }
+        try {
+            // it is possible that this manager is in the same process as the service but
+            // client using it is called through Binder from another process. Example: MMS
+            // app adds a SMS notification and the NotificationManagerService calls this method
+            long identityToken = Binder.clearCallingIdentity();
+            try {
+                service.sendAccessibilityEvent(dispatchedEvent, userId);
+            } finally {
+                Binder.restoreCallingIdentity(identityToken);
+            }
+            if (DEBUG) {
+                Log.i(LOG_TAG, dispatchedEvent + " sent");
+            }
+        } catch (RemoteException re) {
+            Log.e(LOG_TAG, "Error during sending " + dispatchedEvent + " ", re);
+        } finally {
+            if (event != dispatchedEvent) {
+                event.recycle();
+            }
+            dispatchedEvent.recycle();
+        }
     }
 
     /**
-     * Returns whether there are observers registered for this event type. If
-     * this method returns false you shuold not generate events of this type
-     * to conserve resources.
-     *
-     * @param type The event type.
-     * @return Whether the event is being observed.
-     */
-    public boolean isObservedEventType(@AccessibilityEvent.EventType int type) {
-        return false;
-    }
-
-    /**
-     * Requests interruption of the accessibility feedback from all accessibility services.
+     * Requests feedback interruption from all accessibility services.
      */
     public void interrupt() {
+        final IAccessibilityManager service;
+        final int userId;
+        synchronized (mLock) {
+            service = getServiceLocked();
+            if (service == null) {
+                return;
+            }
+            if (!isEnabled()) {
+                Looper myLooper = Looper.myLooper();
+                if (myLooper == Looper.getMainLooper()) {
+                    throw new IllegalStateException(
+                            "Accessibility off. Did you forget to check that?");
+                } else {
+                    // If we're not running on the thread with the main looper, it's possible for
+                    // the state of accessibility to change between checking isEnabled and
+                    // calling this method. So just log the error rather than throwing the
+                    // exception.
+                    Log.e(LOG_TAG, "Interrupt called with accessibility disabled");
+                    return;
+                }
+            }
+            userId = mUserId;
+        }
+        try {
+            service.interrupt(userId);
+            if (DEBUG) {
+                Log.i(LOG_TAG, "Requested interrupt from all services");
+            }
+        } catch (RemoteException re) {
+            Log.e(LOG_TAG, "Error while requesting interrupt from all services. ", re);
+        }
     }
 
     /**
      * Returns the {@link ServiceInfo}s of the installed accessibility services.
      *
      * @return An unmodifiable list with {@link ServiceInfo}s.
+     *
+     * @deprecated Use {@link #getInstalledAccessibilityServiceList()}
      */
     @Deprecated
     public List<ServiceInfo> getAccessibilityServiceList() {
-        return Collections.emptyList();
+        List<AccessibilityServiceInfo> infos = getInstalledAccessibilityServiceList();
+        List<ServiceInfo> services = new ArrayList<>();
+        final int infoCount = infos.size();
+        for (int i = 0; i < infoCount; i++) {
+            AccessibilityServiceInfo info = infos.get(i);
+            services.add(info.getResolveInfo().serviceInfo);
+        }
+        return Collections.unmodifiableList(services);
     }
 
+    /**
+     * Returns the {@link AccessibilityServiceInfo}s of the installed accessibility services.
+     *
+     * @return An unmodifiable list with {@link AccessibilityServiceInfo}s.
+     */
     public List<AccessibilityServiceInfo> getInstalledAccessibilityServiceList() {
-        return Collections.emptyList();
+        final IAccessibilityManager service;
+        final int userId;
+        synchronized (mLock) {
+            service = getServiceLocked();
+            if (service == null) {
+                return Collections.emptyList();
+            }
+            userId = mUserId;
+        }
+
+        List<AccessibilityServiceInfo> services = null;
+        try {
+            services = service.getInstalledAccessibilityServiceList(userId);
+            if (DEBUG) {
+                Log.i(LOG_TAG, "Installed AccessibilityServices " + services);
+            }
+        } catch (RemoteException re) {
+            Log.e(LOG_TAG, "Error while obtaining the installed AccessibilityServices. ", re);
+        }
+        if (mAccessibilityPolicy != null) {
+            services = mAccessibilityPolicy.getInstalledAccessibilityServiceList(services);
+        }
+        if (services != null) {
+            return Collections.unmodifiableList(services);
+        } else {
+            return Collections.emptyList();
+        }
     }
 
     /**
@@ -258,21 +622,52 @@
      * @see AccessibilityServiceInfo#FEEDBACK_HAPTIC
      * @see AccessibilityServiceInfo#FEEDBACK_SPOKEN
      * @see AccessibilityServiceInfo#FEEDBACK_VISUAL
+     * @see AccessibilityServiceInfo#FEEDBACK_BRAILLE
      */
     public List<AccessibilityServiceInfo> getEnabledAccessibilityServiceList(
             int feedbackTypeFlags) {
-        return Collections.emptyList();
+        final IAccessibilityManager service;
+        final int userId;
+        synchronized (mLock) {
+            service = getServiceLocked();
+            if (service == null) {
+                return Collections.emptyList();
+            }
+            userId = mUserId;
+        }
+
+        List<AccessibilityServiceInfo> services = null;
+        try {
+            services = service.getEnabledAccessibilityServiceList(feedbackTypeFlags, userId);
+            if (DEBUG) {
+                Log.i(LOG_TAG, "Installed AccessibilityServices " + services);
+            }
+        } catch (RemoteException re) {
+            Log.e(LOG_TAG, "Error while obtaining the installed AccessibilityServices. ", re);
+        }
+        if (mAccessibilityPolicy != null) {
+            services = mAccessibilityPolicy.getEnabledAccessibilityServiceList(
+                    feedbackTypeFlags, services);
+        }
+        if (services != null) {
+            return Collections.unmodifiableList(services);
+        } else {
+            return Collections.emptyList();
+        }
     }
 
     /**
      * Registers an {@link AccessibilityStateChangeListener} for changes in
-     * the global accessibility state of the system.
+     * the global accessibility state of the system. Equivalent to calling
+     * {@link #addAccessibilityStateChangeListener(AccessibilityStateChangeListener, Handler)}
+     * with a null handler.
      *
      * @param listener The listener.
-     * @return True if successfully registered.
+     * @return Always returns {@code true}.
      */
     public boolean addAccessibilityStateChangeListener(
-            AccessibilityStateChangeListener listener) {
+            @NonNull AccessibilityStateChangeListener listener) {
+        addAccessibilityStateChangeListener(listener, null);
         return true;
     }
 
@@ -286,22 +681,40 @@
      *                for a callback on the process's main handler.
      */
     public void addAccessibilityStateChangeListener(
-            @NonNull AccessibilityStateChangeListener listener, @Nullable Handler handler) {}
+            @NonNull AccessibilityStateChangeListener listener, @Nullable Handler handler) {
+        synchronized (mLock) {
+            mAccessibilityStateChangeListeners
+                    .put(listener, (handler == null) ? mHandler : handler);
+        }
+    }
 
+    /**
+     * Unregisters an {@link AccessibilityStateChangeListener}.
+     *
+     * @param listener The listener.
+     * @return True if the listener was previously registered.
+     */
     public boolean removeAccessibilityStateChangeListener(
-            AccessibilityStateChangeListener listener) {
-        return true;
+            @NonNull AccessibilityStateChangeListener listener) {
+        synchronized (mLock) {
+            int index = mAccessibilityStateChangeListeners.indexOfKey(listener);
+            mAccessibilityStateChangeListeners.remove(listener);
+            return (index >= 0);
+        }
     }
 
     /**
      * Registers a {@link TouchExplorationStateChangeListener} for changes in
-     * the global touch exploration state of the system.
+     * the global touch exploration state of the system. Equivalent to calling
+     * {@link #addTouchExplorationStateChangeListener(TouchExplorationStateChangeListener, Handler)}
+     * with a null handler.
      *
      * @param listener The listener.
-     * @return True if successfully registered.
+     * @return Always returns {@code true}.
      */
     public boolean addTouchExplorationStateChangeListener(
             @NonNull TouchExplorationStateChangeListener listener) {
+        addTouchExplorationStateChangeListener(listener, null);
         return true;
     }
 
@@ -315,17 +728,104 @@
      *                for a callback on the process's main handler.
      */
     public void addTouchExplorationStateChangeListener(
-            @NonNull TouchExplorationStateChangeListener listener, @Nullable Handler handler) {}
+            @NonNull TouchExplorationStateChangeListener listener, @Nullable Handler handler) {
+        synchronized (mLock) {
+            mTouchExplorationStateChangeListeners
+                    .put(listener, (handler == null) ? mHandler : handler);
+        }
+    }
 
     /**
      * Unregisters a {@link TouchExplorationStateChangeListener}.
      *
      * @param listener The listener.
-     * @return True if successfully unregistered.
+     * @return True if listener was previously registered.
      */
     public boolean removeTouchExplorationStateChangeListener(
             @NonNull TouchExplorationStateChangeListener listener) {
-        return true;
+        synchronized (mLock) {
+            int index = mTouchExplorationStateChangeListeners.indexOfKey(listener);
+            mTouchExplorationStateChangeListeners.remove(listener);
+            return (index >= 0);
+        }
+    }
+
+    /**
+     * Registers a {@link AccessibilityServicesStateChangeListener}.
+     *
+     * @param listener The listener.
+     * @param handler The handler on which the listener should be called back, or {@code null}
+     *                for a callback on the process's main handler.
+     * @hide
+     */
+    public void addAccessibilityServicesStateChangeListener(
+            @NonNull AccessibilityServicesStateChangeListener listener, @Nullable Handler handler) {
+        synchronized (mLock) {
+            mServicesStateChangeListeners
+                    .put(listener, (handler == null) ? mHandler : handler);
+        }
+    }
+
+    /**
+     * Unregisters a {@link AccessibilityServicesStateChangeListener}.
+     *
+     * @param listener The listener.
+     *
+     * @hide
+     */
+    public void removeAccessibilityServicesStateChangeListener(
+            @NonNull AccessibilityServicesStateChangeListener listener) {
+        synchronized (mLock) {
+            mServicesStateChangeListeners.remove(listener);
+        }
+    }
+
+    /**
+     * Registers a {@link AccessibilityRequestPreparer}.
+     */
+    public void addAccessibilityRequestPreparer(AccessibilityRequestPreparer preparer) {
+        if (mRequestPreparerLists == null) {
+            mRequestPreparerLists = new SparseArray<>(1);
+        }
+        int id = preparer.getView().getAccessibilityViewId();
+        List<AccessibilityRequestPreparer> requestPreparerList = mRequestPreparerLists.get(id);
+        if (requestPreparerList == null) {
+            requestPreparerList = new ArrayList<>(1);
+            mRequestPreparerLists.put(id, requestPreparerList);
+        }
+        requestPreparerList.add(preparer);
+    }
+
+    /**
+     * Unregisters a {@link AccessibilityRequestPreparer}.
+     */
+    public void removeAccessibilityRequestPreparer(AccessibilityRequestPreparer preparer) {
+        if (mRequestPreparerLists == null) {
+            return;
+        }
+        int viewId = preparer.getView().getAccessibilityViewId();
+        List<AccessibilityRequestPreparer> requestPreparerList = mRequestPreparerLists.get(viewId);
+        if (requestPreparerList != null) {
+            requestPreparerList.remove(preparer);
+            if (requestPreparerList.isEmpty()) {
+                mRequestPreparerLists.remove(viewId);
+            }
+        }
+    }
+
+    /**
+     * Get the preparers that are registered for an accessibility ID
+     *
+     * @param id The ID of interest
+     * @return The list of preparers, or {@code null} if there are none.
+     *
+     * @hide
+     */
+    public List<AccessibilityRequestPreparer> getRequestPreparersForAccessibilityId(int id) {
+        if (mRequestPreparerLists == null) {
+            return null;
+        }
+        return mRequestPreparerLists.get(id);
     }
 
     /**
@@ -337,7 +837,12 @@
      * @hide
      */
     public void addHighTextContrastStateChangeListener(
-            @NonNull HighTextContrastChangeListener listener, @Nullable Handler handler) {}
+            @NonNull HighTextContrastChangeListener listener, @Nullable Handler handler) {
+        synchronized (mLock) {
+            mHighTextContrastStateChangeListeners
+                    .put(listener, (handler == null) ? mHandler : handler);
+        }
+    }
 
     /**
      * Unregisters a {@link HighTextContrastChangeListener}.
@@ -347,7 +852,64 @@
      * @hide
      */
     public void removeHighTextContrastStateChangeListener(
-            @NonNull HighTextContrastChangeListener listener) {}
+            @NonNull HighTextContrastChangeListener listener) {
+        synchronized (mLock) {
+            mHighTextContrastStateChangeListeners.remove(listener);
+        }
+    }
+
+    /**
+     * Sets the {@link AccessibilityPolicy} controlling this manager.
+     *
+     * @param policy The policy.
+     *
+     * @hide
+     */
+    public void setAccessibilityPolicy(@Nullable AccessibilityPolicy policy) {
+        synchronized (mLock) {
+            mAccessibilityPolicy = policy;
+        }
+    }
+
+    /**
+     * Check if the accessibility volume stream is active.
+     *
+     * @return True if accessibility volume is active (i.e. some service has requested it). False
+     * otherwise.
+     * @hide
+     */
+    public boolean isAccessibilityVolumeStreamActive() {
+        List<AccessibilityServiceInfo> serviceInfos =
+                getEnabledAccessibilityServiceList(AccessibilityServiceInfo.FEEDBACK_ALL_MASK);
+        for (int i = 0; i < serviceInfos.size(); i++) {
+            if ((serviceInfos.get(i).flags & FLAG_ENABLE_ACCESSIBILITY_VOLUME) != 0) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Report a fingerprint gesture to accessibility. Only available for the system process.
+     *
+     * @param keyCode The key code of the gesture
+     * @return {@code true} if accessibility consumes the event. {@code false} if not.
+     * @hide
+     */
+    public boolean sendFingerprintGesture(int keyCode) {
+        final IAccessibilityManager service;
+        synchronized (mLock) {
+            service = getServiceLocked();
+            if (service == null) {
+                return false;
+            }
+        }
+        try {
+            return service.sendFingerprintGesture(keyCode);
+        } catch (RemoteException e) {
+            return false;
+        }
+    }
 
     /**
      * Sets the current state and notifies listeners, if necessary.
@@ -355,14 +917,312 @@
      * @param stateFlags The state flags.
      */
     private void setStateLocked(int stateFlags) {
+        final boolean enabled = (stateFlags & STATE_FLAG_ACCESSIBILITY_ENABLED) != 0;
+        final boolean touchExplorationEnabled =
+                (stateFlags & STATE_FLAG_TOUCH_EXPLORATION_ENABLED) != 0;
+        final boolean highTextContrastEnabled =
+                (stateFlags & STATE_FLAG_HIGH_TEXT_CONTRAST_ENABLED) != 0;
+
+        final boolean wasEnabled = isEnabled();
+        final boolean wasTouchExplorationEnabled = mIsTouchExplorationEnabled;
+        final boolean wasHighTextContrastEnabled = mIsHighTextContrastEnabled;
+
+        // Ensure listeners get current state from isZzzEnabled() calls.
+        mIsEnabled = enabled;
+        mIsTouchExplorationEnabled = touchExplorationEnabled;
+        mIsHighTextContrastEnabled = highTextContrastEnabled;
+
+        if (wasEnabled != isEnabled()) {
+            notifyAccessibilityStateChanged();
+        }
+
+        if (wasTouchExplorationEnabled != touchExplorationEnabled) {
+            notifyTouchExplorationStateChanged();
+        }
+
+        if (wasHighTextContrastEnabled != highTextContrastEnabled) {
+            notifyHighTextContrastStateChanged();
+        }
     }
 
+    /**
+     * Find an installed service with the specified {@link ComponentName}.
+     *
+     * @param componentName The name to match to the service.
+     *
+     * @return The info corresponding to the installed service, or {@code null} if no such service
+     * is installed.
+     * @hide
+     */
+    public AccessibilityServiceInfo getInstalledServiceInfoWithComponentName(
+            ComponentName componentName) {
+        final List<AccessibilityServiceInfo> installedServiceInfos =
+                getInstalledAccessibilityServiceList();
+        if ((installedServiceInfos == null) || (componentName == null)) {
+            return null;
+        }
+        for (int i = 0; i < installedServiceInfos.size(); i++) {
+            if (componentName.equals(installedServiceInfos.get(i).getComponentName())) {
+                return installedServiceInfos.get(i);
+            }
+        }
+        return null;
+    }
+
+    /**
+     * Adds an accessibility interaction connection interface for a given window.
+     * @param windowToken The window token to which a connection is added.
+     * @param connection The connection.
+     *
+     * @hide
+     */
     public int addAccessibilityInteractionConnection(IWindow windowToken,
-            IAccessibilityInteractionConnection connection) {
+            String packageName, IAccessibilityInteractionConnection connection) {
+        final IAccessibilityManager service;
+        final int userId;
+        synchronized (mLock) {
+            service = getServiceLocked();
+            if (service == null) {
+                return View.NO_ID;
+            }
+            userId = mUserId;
+        }
+        try {
+            return service.addAccessibilityInteractionConnection(windowToken, connection,
+                    packageName, userId);
+        } catch (RemoteException re) {
+            Log.e(LOG_TAG, "Error while adding an accessibility interaction connection. ", re);
+        }
         return View.NO_ID;
     }
 
+    /**
+     * Removed an accessibility interaction connection interface for a given window.
+     * @param windowToken The window token to which a connection is removed.
+     *
+     * @hide
+     */
     public void removeAccessibilityInteractionConnection(IWindow windowToken) {
+        final IAccessibilityManager service;
+        synchronized (mLock) {
+            service = getServiceLocked();
+            if (service == null) {
+                return;
+            }
+        }
+        try {
+            service.removeAccessibilityInteractionConnection(windowToken);
+        } catch (RemoteException re) {
+            Log.e(LOG_TAG, "Error while removing an accessibility interaction connection. ", re);
+        }
     }
 
+    /**
+     * Perform the accessibility shortcut if the caller has permission.
+     *
+     * @hide
+     */
+    public void performAccessibilityShortcut() {
+        final IAccessibilityManager service;
+        synchronized (mLock) {
+            service = getServiceLocked();
+            if (service == null) {
+                return;
+            }
+        }
+        try {
+            service.performAccessibilityShortcut();
+        } catch (RemoteException re) {
+            Log.e(LOG_TAG, "Error performing accessibility shortcut. ", re);
+        }
+    }
+
+    /**
+     * Notifies that the accessibility button in the system's navigation area has been clicked
+     *
+     * @hide
+     */
+    public void notifyAccessibilityButtonClicked() {
+        final IAccessibilityManager service;
+        synchronized (mLock) {
+            service = getServiceLocked();
+            if (service == null) {
+                return;
+            }
+        }
+        try {
+            service.notifyAccessibilityButtonClicked();
+        } catch (RemoteException re) {
+            Log.e(LOG_TAG, "Error while dispatching accessibility button click", re);
+        }
+    }
+
+    /**
+     * Notifies that the visibility of the accessibility button in the system's navigation area
+     * has changed.
+     *
+     * @param shown {@code true} if the accessibility button is visible within the system
+     *                  navigation area, {@code false} otherwise
+     * @hide
+     */
+    public void notifyAccessibilityButtonVisibilityChanged(boolean shown) {
+        final IAccessibilityManager service;
+        synchronized (mLock) {
+            service = getServiceLocked();
+            if (service == null) {
+                return;
+            }
+        }
+        try {
+            service.notifyAccessibilityButtonVisibilityChanged(shown);
+        } catch (RemoteException re) {
+            Log.e(LOG_TAG, "Error while dispatching accessibility button visibility change", re);
+        }
+    }
+
+    /**
+     * Set an IAccessibilityInteractionConnection to replace the actions of a picture-in-picture
+     * window. Intended for use by the System UI only.
+     *
+     * @param connection The connection to handle the actions. Set to {@code null} to avoid
+     * affecting the actions.
+     *
+     * @hide
+     */
+    public void setPictureInPictureActionReplacingConnection(
+            @Nullable IAccessibilityInteractionConnection connection) {
+        final IAccessibilityManager service;
+        synchronized (mLock) {
+            service = getServiceLocked();
+            if (service == null) {
+                return;
+            }
+        }
+        try {
+            service.setPictureInPictureActionReplacingConnection(connection);
+        } catch (RemoteException re) {
+            Log.e(LOG_TAG, "Error setting picture in picture action replacement", re);
+        }
+    }
+
+    private IAccessibilityManager getServiceLocked() {
+        if (mService == null) {
+            tryConnectToServiceLocked(null);
+        }
+        return mService;
+    }
+
+    private void tryConnectToServiceLocked(IAccessibilityManager service) {
+        if (service == null) {
+            IBinder iBinder = ServiceManager.getService(Context.ACCESSIBILITY_SERVICE);
+            if (iBinder == null) {
+                return;
+            }
+            service = IAccessibilityManager.Stub.asInterface(iBinder);
+        }
+
+        try {
+            final long userStateAndRelevantEvents = service.addClient(mClient, mUserId);
+            setStateLocked(IntPair.first(userStateAndRelevantEvents));
+            mRelevantEventTypes = IntPair.second(userStateAndRelevantEvents);
+            mService = service;
+        } catch (RemoteException re) {
+            Log.e(LOG_TAG, "AccessibilityManagerService is dead", re);
+        }
+    }
+
+    /**
+     * Notifies the registered {@link AccessibilityStateChangeListener}s.
+     */
+    private void notifyAccessibilityStateChanged() {
+        final boolean isEnabled;
+        final ArrayMap<AccessibilityStateChangeListener, Handler> listeners;
+        synchronized (mLock) {
+            if (mAccessibilityStateChangeListeners.isEmpty()) {
+                return;
+            }
+            isEnabled = isEnabled();
+            listeners = new ArrayMap<>(mAccessibilityStateChangeListeners);
+        }
+
+        final int numListeners = listeners.size();
+        for (int i = 0; i < numListeners; i++) {
+            final AccessibilityStateChangeListener listener = listeners.keyAt(i);
+            listeners.valueAt(i).post(() ->
+                    listener.onAccessibilityStateChanged(isEnabled));
+        }
+    }
+
+    /**
+     * Notifies the registered {@link TouchExplorationStateChangeListener}s.
+     */
+    private void notifyTouchExplorationStateChanged() {
+        final boolean isTouchExplorationEnabled;
+        final ArrayMap<TouchExplorationStateChangeListener, Handler> listeners;
+        synchronized (mLock) {
+            if (mTouchExplorationStateChangeListeners.isEmpty()) {
+                return;
+            }
+            isTouchExplorationEnabled = mIsTouchExplorationEnabled;
+            listeners = new ArrayMap<>(mTouchExplorationStateChangeListeners);
+        }
+
+        final int numListeners = listeners.size();
+        for (int i = 0; i < numListeners; i++) {
+            final TouchExplorationStateChangeListener listener = listeners.keyAt(i);
+            listeners.valueAt(i).post(() ->
+                    listener.onTouchExplorationStateChanged(isTouchExplorationEnabled));
+        }
+    }
+
+    /**
+     * Notifies the registered {@link HighTextContrastChangeListener}s.
+     */
+    private void notifyHighTextContrastStateChanged() {
+        final boolean isHighTextContrastEnabled;
+        final ArrayMap<HighTextContrastChangeListener, Handler> listeners;
+        synchronized (mLock) {
+            if (mHighTextContrastStateChangeListeners.isEmpty()) {
+                return;
+            }
+            isHighTextContrastEnabled = mIsHighTextContrastEnabled;
+            listeners = new ArrayMap<>(mHighTextContrastStateChangeListeners);
+        }
+
+        final int numListeners = listeners.size();
+        for (int i = 0; i < numListeners; i++) {
+            final HighTextContrastChangeListener listener = listeners.keyAt(i);
+            listeners.valueAt(i).post(() ->
+                    listener.onHighTextContrastStateChanged(isHighTextContrastEnabled));
+        }
+    }
+
+    /**
+     * Determines if the accessibility button within the system navigation area is supported.
+     *
+     * @return {@code true} if the accessibility button is supported on this device,
+     * {@code false} otherwise
+     */
+    public static boolean isAccessibilityButtonSupported() {
+        final Resources res = Resources.getSystem();
+        return res.getBoolean(com.android.internal.R.bool.config_showNavigationBar);
+    }
+
+    private final class MyCallback implements Handler.Callback {
+        public static final int MSG_SET_STATE = 1;
+
+        @Override
+        public boolean handleMessage(Message message) {
+            switch (message.what) {
+                case MSG_SET_STATE: {
+                    // See comment at mClient
+                    final int state = message.arg1;
+                    synchronized (mLock) {
+                        setStateLocked(state);
+                    }
+                } break;
+            }
+            return true;
+        }
+    }
 }
diff --git a/android/view/accessibility/AccessibilityNodeInfo.java b/android/view/accessibility/AccessibilityNodeInfo.java
index 4c437dd..03f1c12 100644
--- a/android/view/accessibility/AccessibilityNodeInfo.java
+++ b/android/view/accessibility/AccessibilityNodeInfo.java
@@ -3874,6 +3874,24 @@
                         | FLAG_PREFETCH_DESCENDANTS | FLAG_PREFETCH_SIBLINGS, null);
     }
 
+    /** @hide */
+    public static String idToString(long accessibilityId) {
+        int accessibilityViewId = getAccessibilityViewId(accessibilityId);
+        int virtualDescendantId = getVirtualDescendantId(accessibilityId);
+        return virtualDescendantId == AccessibilityNodeProvider.HOST_VIEW_ID
+                ? idItemToString(accessibilityViewId)
+                : idItemToString(accessibilityViewId) + ":" + idItemToString(virtualDescendantId);
+    }
+
+    private static String idItemToString(int item) {
+        switch (item) {
+            case ROOT_ITEM_ID: return "ROOT";
+            case UNDEFINED_ITEM_ID: return "UNDEFINED";
+            case AccessibilityNodeProvider.HOST_VIEW_ID: return "HOST";
+            default: return "" + item;
+        }
+    }
+
     /**
      * A class defining an action that can be performed on an {@link AccessibilityNodeInfo}.
      * Each action has a unique id that is mandatory and optional data.
diff --git a/android/view/autofill/AutofillPopupWindow.java b/android/view/autofill/AutofillPopupWindow.java
index 1da998d..a6495d1 100644
--- a/android/view/autofill/AutofillPopupWindow.java
+++ b/android/view/autofill/AutofillPopupWindow.java
@@ -79,6 +79,11 @@
     public AutofillPopupWindow(@NonNull IAutofillWindowPresenter presenter) {
         mWindowPresenter = new WindowPresenter(presenter);
 
+        // We want to show the window as system controlled one so it covers app windows, but it has
+        // to be an application type (so it's contained inside the application area).
+        // Hence, we set it to the application type with the highest z-order, which currently
+        // is TYPE_APPLICATION_ABOVE_SUB_PANEL.
+        setWindowLayoutType(WindowManager.LayoutParams.TYPE_APPLICATION_ABOVE_SUB_PANEL);
         setTouchModal(false);
         setOutsideTouchable(true);
         setInputMethodMode(INPUT_METHOD_NOT_NEEDED);
diff --git a/android/view/inputmethod/BaseInputConnection.java b/android/view/inputmethod/BaseInputConnection.java
index 5f7a0f7..090e19f 100644
--- a/android/view/inputmethod/BaseInputConnection.java
+++ b/android/view/inputmethod/BaseInputConnection.java
@@ -522,7 +522,7 @@
             b = tmp;
         }
 
-        if (a == b) return null;
+        if (a == b || a < 0) return null;
 
         if ((flags&GET_TEXT_WITH_STYLES) != 0) {
             return content.subSequence(a, b);
diff --git a/android/view/textclassifier/GenerateLinksLogger.java b/android/view/textclassifier/GenerateLinksLogger.java
index 73cf43b..067513f 100644
--- a/android/view/textclassifier/GenerateLinksLogger.java
+++ b/android/view/textclassifier/GenerateLinksLogger.java
@@ -19,13 +19,13 @@
 import android.annotation.Nullable;
 import android.metrics.LogMaker;
 import android.util.ArrayMap;
-import android.util.Log;
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.logging.MetricsLogger;
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
 import com.android.internal.util.Preconditions;
 
+import java.util.Locale;
 import java.util.Map;
 import java.util.Objects;
 import java.util.Random;
@@ -39,6 +39,7 @@
 public final class GenerateLinksLogger {
 
     private static final String LOG_TAG = "GenerateLinksLogger";
+    private static final boolean DEBUG_LOG_ENABLED = false;
     private static final String ZERO = "0";
 
     private final MetricsLogger mMetricsLogger;
@@ -127,7 +128,7 @@
     }
 
     private static void debugLog(LogMaker log) {
-        if (!Logger.DEBUG_LOG_ENABLED) return;
+        if (!DEBUG_LOG_ENABLED) return;
 
         final String callId = Objects.toString(
                 log.getTaggedData(MetricsEvent.FIELD_LINKIFY_CALL_ID), "");
@@ -142,8 +143,9 @@
         final int latencyMs = Integer.parseInt(
                 Objects.toString(log.getTaggedData(MetricsEvent.FIELD_LINKIFY_LATENCY), ZERO));
 
-        Log.d(LOG_TAG, String.format("%s:%s %d links (%d/%d chars) %dms %s", callId, entityType,
-                numLinks, linkLength, textLength, latencyMs, log.getPackageName()));
+        Log.d(LOG_TAG,
+                String.format(Locale.US, "%s:%s %d links (%d/%d chars) %dms %s", callId, entityType,
+                        numLinks, linkLength, textLength, latencyMs, log.getPackageName()));
     }
 
     /** Helper class for storing per-entity type statistics. */
diff --git a/android/view/textclassifier/Logger.java b/android/view/textclassifier/Logger.java
deleted file mode 100644
index f03906a..0000000
--- a/android/view/textclassifier/Logger.java
+++ /dev/null
@@ -1,397 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.view.textclassifier;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.content.Context;
-
-import com.android.internal.util.Preconditions;
-
-import java.text.BreakIterator;
-import java.util.Locale;
-import java.util.Objects;
-
-/**
- * A helper for logging TextClassifier related events.
- * @hide
- */
-public abstract class Logger {
-
-    private static final String LOG_TAG = "Logger";
-    /* package */ static final boolean DEBUG_LOG_ENABLED = true;
-
-    private @SelectionEvent.InvocationMethod int mInvocationMethod;
-    private SelectionEvent mPrevEvent;
-    private SelectionEvent mSmartEvent;
-    private SelectionEvent mStartEvent;
-
-    /**
-     * Logger that does not log anything.
-     * @hide
-     */
-    public static final Logger DISABLED = new Logger() {
-        @Override
-        public void writeEvent(SelectionEvent event) {}
-    };
-
-    @Nullable
-    private final Config mConfig;
-
-    public Logger(Config config) {
-        mConfig = Preconditions.checkNotNull(config);
-    }
-
-    private Logger() {
-        mConfig = null;
-    }
-
-    /**
-     * Writes the selection event to a log.
-     */
-    public abstract void writeEvent(@NonNull SelectionEvent event);
-
-    /**
-     * Returns true if the resultId matches that of a smart selection event (i.e.
-     * {@link SelectionEvent#EVENT_SMART_SELECTION_SINGLE} or
-     * {@link SelectionEvent#EVENT_SMART_SELECTION_MULTI}).
-     * Returns false otherwise.
-     */
-    public boolean isSmartSelection(@NonNull String resultId) {
-        return false;
-    }
-
-    /**
-     * Returns a token iterator for tokenizing text for logging purposes.
-     */
-    public BreakIterator getTokenIterator(@NonNull Locale locale) {
-        return BreakIterator.getWordInstance(Preconditions.checkNotNull(locale));
-    }
-
-    /**
-     * Logs a "selection started" event.
-     *
-     * @param invocationMethod  the way the selection was triggered
-     * @param start  the token index of the selected token
-     */
-    public final void logSelectionStartedEvent(
-            @SelectionEvent.InvocationMethod int invocationMethod, int start) {
-        if (mConfig == null) {
-            return;
-        }
-
-        mInvocationMethod = invocationMethod;
-        logEvent(new SelectionEvent(
-                start, start + 1, SelectionEvent.EVENT_SELECTION_STARTED,
-                TextClassifier.TYPE_UNKNOWN, mInvocationMethod, null, mConfig));
-    }
-
-    /**
-     * Logs a "selection modified" event.
-     * Use when the user modifies the selection.
-     *
-     * @param start  the start token (inclusive) index of the selection
-     * @param end  the end token (exclusive) index of the selection
-     */
-    public final void logSelectionModifiedEvent(int start, int end) {
-        Preconditions.checkArgument(end >= start, "end cannot be less than start");
-
-        if (mConfig == null) {
-            return;
-        }
-
-        logEvent(new SelectionEvent(
-                start, end, SelectionEvent.EVENT_SELECTION_MODIFIED,
-                TextClassifier.TYPE_UNKNOWN, mInvocationMethod, null, mConfig));
-    }
-
-    /**
-     * Logs a "selection modified" event.
-     * Use when the user modifies the selection and the selection's entity type is known.
-     *
-     * @param start  the start token (inclusive) index of the selection
-     * @param end  the end token (exclusive) index of the selection
-     * @param classification  the TextClassification object returned by the TextClassifier that
-     *      classified the selected text
-     */
-    public final void logSelectionModifiedEvent(
-            int start, int end, @NonNull TextClassification classification) {
-        Preconditions.checkArgument(end >= start, "end cannot be less than start");
-        Preconditions.checkNotNull(classification);
-
-        if (mConfig == null) {
-            return;
-        }
-
-        final String entityType = classification.getEntityCount() > 0
-                ? classification.getEntity(0)
-                : TextClassifier.TYPE_UNKNOWN;
-        logEvent(new SelectionEvent(
-                start, end, SelectionEvent.EVENT_SELECTION_MODIFIED,
-                entityType, mInvocationMethod, classification.getId(), mConfig));
-    }
-
-    /**
-     * Logs a "selection modified" event.
-     * Use when a TextClassifier modifies the selection.
-     *
-     * @param start  the start token (inclusive) index of the selection
-     * @param end  the end token (exclusive) index of the selection
-     * @param selection  the TextSelection object returned by the TextClassifier for the
-     *      specified selection
-     */
-    public final void logSelectionModifiedEvent(
-            int start, int end, @NonNull TextSelection selection) {
-        Preconditions.checkArgument(end >= start, "end cannot be less than start");
-        Preconditions.checkNotNull(selection);
-
-        if (mConfig == null) {
-            return;
-        }
-
-        final int eventType;
-        if (isSmartSelection(selection.getId())) {
-            eventType = end - start > 1
-                    ? SelectionEvent.EVENT_SMART_SELECTION_MULTI
-                    : SelectionEvent.EVENT_SMART_SELECTION_SINGLE;
-
-        } else {
-            eventType = SelectionEvent.EVENT_AUTO_SELECTION;
-        }
-        final String entityType = selection.getEntityCount() > 0
-                ? selection.getEntity(0)
-                : TextClassifier.TYPE_UNKNOWN;
-        logEvent(new SelectionEvent(start, end, eventType, entityType, mInvocationMethod,
-                selection.getId(), mConfig));
-    }
-
-    /**
-     * Logs an event specifying an action taken on a selection.
-     * Use when the user clicks on an action to act on the selected text.
-     *
-     * @param start  the start token (inclusive) index of the selection
-     * @param end  the end token (exclusive) index of the selection
-     * @param actionType  the action that was performed on the selection
-     */
-    public final void logSelectionActionEvent(
-            int start, int end, @SelectionEvent.ActionType int actionType) {
-        Preconditions.checkArgument(end >= start, "end cannot be less than start");
-        checkActionType(actionType);
-
-        if (mConfig == null) {
-            return;
-        }
-
-        logEvent(new SelectionEvent(
-                start, end, actionType, TextClassifier.TYPE_UNKNOWN, mInvocationMethod,
-                null, mConfig));
-    }
-
-    /**
-     * Logs an event specifying an action taken on a selection.
-     * Use when the user clicks on an action to act on the selected text and the selection's
-     * entity type is known.
-     *
-     * @param start  the start token (inclusive) index of the selection
-     * @param end  the end token (exclusive) index of the selection
-     * @param actionType  the action that was performed on the selection
-     * @param classification  the TextClassification object returned by the TextClassifier that
-     *      classified the selected text
-     *
-     * @throws IllegalArgumentException If actionType is not a valid SelectionEvent actionType
-     */
-    public final void logSelectionActionEvent(
-            int start, int end, @SelectionEvent.ActionType int actionType,
-            @NonNull TextClassification classification) {
-        Preconditions.checkArgument(end >= start, "end cannot be less than start");
-        Preconditions.checkNotNull(classification);
-        checkActionType(actionType);
-
-        if (mConfig == null) {
-            return;
-        }
-
-        final String entityType = classification.getEntityCount() > 0
-                ? classification.getEntity(0)
-                : TextClassifier.TYPE_UNKNOWN;
-        logEvent(new SelectionEvent(start, end, actionType, entityType, mInvocationMethod,
-                classification.getId(), mConfig));
-    }
-
-    private void logEvent(@NonNull SelectionEvent event) {
-        Preconditions.checkNotNull(event);
-
-        if (event.getEventType() != SelectionEvent.EVENT_SELECTION_STARTED
-                && mStartEvent == null) {
-            if (DEBUG_LOG_ENABLED) {
-                Log.d(LOG_TAG, "Selection session not yet started. Ignoring event");
-            }
-            return;
-        }
-
-        final long now = System.currentTimeMillis();
-        switch (event.getEventType()) {
-            case SelectionEvent.EVENT_SELECTION_STARTED:
-                Preconditions.checkArgument(event.getAbsoluteEnd() == event.getAbsoluteStart() + 1);
-                event.setSessionId(startNewSession());
-                mStartEvent = event;
-                break;
-            case SelectionEvent.EVENT_SMART_SELECTION_SINGLE:  // fall through
-            case SelectionEvent.EVENT_SMART_SELECTION_MULTI:
-                mSmartEvent = event;
-                break;
-            case SelectionEvent.EVENT_SELECTION_MODIFIED:  // fall through
-            case SelectionEvent.EVENT_AUTO_SELECTION:
-                if (mPrevEvent != null
-                        && mPrevEvent.getAbsoluteStart() == event.getAbsoluteStart()
-                        && mPrevEvent.getAbsoluteEnd() == event.getAbsoluteEnd()) {
-                    // Selection did not change. Ignore event.
-                    return;
-                }
-                break;
-            default:
-                // do nothing.
-        }
-
-        event.setEventTime(now);
-        if (mStartEvent != null) {
-            event.setSessionId(mStartEvent.getSessionId())
-                    .setDurationSinceSessionStart(now - mStartEvent.getEventTime())
-                    .setStart(event.getAbsoluteStart() - mStartEvent.getAbsoluteStart())
-                    .setEnd(event.getAbsoluteEnd() - mStartEvent.getAbsoluteStart());
-        }
-        if (mSmartEvent != null) {
-            event.setResultId(mSmartEvent.getResultId())
-                    .setSmartStart(mSmartEvent.getAbsoluteStart() - mStartEvent.getAbsoluteStart())
-                    .setSmartEnd(mSmartEvent.getAbsoluteEnd() - mStartEvent.getAbsoluteStart());
-        }
-        if (mPrevEvent != null) {
-            event.setDurationSincePreviousEvent(now - mPrevEvent.getEventTime())
-                    .setEventIndex(mPrevEvent.getEventIndex() + 1);
-        }
-        writeEvent(event);
-        mPrevEvent = event;
-
-        if (event.isTerminal()) {
-            endSession();
-        }
-    }
-
-    private TextClassificationSessionId startNewSession() {
-        endSession();
-        return new TextClassificationSessionId();
-    }
-
-    private void endSession() {
-        mPrevEvent = null;
-        mSmartEvent = null;
-        mStartEvent = null;
-    }
-
-    /**
-     * @throws IllegalArgumentException If eventType is not an {@link SelectionEvent.ActionType}
-     */
-    private static void checkActionType(@SelectionEvent.EventType int eventType)
-            throws IllegalArgumentException {
-        switch (eventType) {
-            case SelectionEvent.ACTION_OVERTYPE:  // fall through
-            case SelectionEvent.ACTION_COPY:  // fall through
-            case SelectionEvent.ACTION_PASTE:  // fall through
-            case SelectionEvent.ACTION_CUT:  // fall through
-            case SelectionEvent.ACTION_SHARE:  // fall through
-            case SelectionEvent.ACTION_SMART_SHARE:  // fall through
-            case SelectionEvent.ACTION_DRAG:  // fall through
-            case SelectionEvent.ACTION_ABANDON:  // fall through
-            case SelectionEvent.ACTION_SELECT_ALL:  // fall through
-            case SelectionEvent.ACTION_RESET:  // fall through
-                return;
-            default:
-                throw new IllegalArgumentException(
-                        String.format(Locale.US, "%d is not an eventType", eventType));
-        }
-    }
-
-
-    /**
-     * A Logger config.
-     */
-    public static final class Config {
-
-        private final String mPackageName;
-        private final String mWidgetType;
-        @Nullable private final String mWidgetVersion;
-
-        /**
-         * @param context Context of the widget the logger logs for
-         * @param widgetType a name for the widget being logged for. e.g.
-         *      {@link TextClassifier#WIDGET_TYPE_TEXTVIEW}
-         * @param widgetVersion a string version info for the widget the logger logs for
-         */
-        public Config(
-                @NonNull Context context,
-                @TextClassifier.WidgetType String widgetType,
-                @Nullable String widgetVersion) {
-            mPackageName = Preconditions.checkNotNull(context).getPackageName();
-            mWidgetType = widgetType;
-            mWidgetVersion = widgetVersion;
-        }
-
-        /**
-         * Returns the package name of the application the logger logs for.
-         */
-        public String getPackageName() {
-            return mPackageName;
-        }
-
-        /**
-         * Returns the name for the widget being logged for. e.g.
-         * {@link TextClassifier#WIDGET_TYPE_TEXTVIEW}.
-         */
-        public String getWidgetType() {
-            return mWidgetType;
-        }
-
-        /**
-         * Returns string version info for the logger. This is specific to the text classifier.
-         */
-        @Nullable
-        public String getWidgetVersion() {
-            return mWidgetVersion;
-        }
-
-        @Override
-        public int hashCode() {
-            return Objects.hash(mPackageName, mWidgetType, mWidgetVersion);
-        }
-
-        @Override
-        public boolean equals(Object obj) {
-            if (obj == this) {
-                return true;
-            }
-
-            if (!(obj instanceof Config)) {
-                return false;
-            }
-
-            final Config other = (Config) obj;
-            return Objects.equals(mPackageName, other.mPackageName)
-                    && Objects.equals(mWidgetType, other.mWidgetType)
-                    && Objects.equals(mWidgetVersion, other.mWidgetType);
-        }
-    }
-}
diff --git a/android/view/textclassifier/SelectionEvent.java b/android/view/textclassifier/SelectionEvent.java
index 1e978cc..b073596 100644
--- a/android/view/textclassifier/SelectionEvent.java
+++ b/android/view/textclassifier/SelectionEvent.java
@@ -150,20 +150,6 @@
         mInvocationMethod = invocationMethod;
     }
 
-    SelectionEvent(
-            int start, int end,
-            @EventType int eventType, @EntityType String entityType,
-            @InvocationMethod int invocationMethod, @Nullable String resultId,
-            Logger.Config config) {
-        this(start, end, eventType, entityType, invocationMethod, resultId);
-        Preconditions.checkNotNull(config);
-        setTextClassificationSessionContext(
-                new TextClassificationContext.Builder(
-                        config.getPackageName(), config.getWidgetType())
-                        .setWidgetVersion(config.getWidgetVersion())
-                        .build());
-    }
-
     private SelectionEvent(Parcel in) {
         mAbsoluteStart = in.readInt();
         mAbsoluteEnd = in.readInt();
@@ -362,6 +348,7 @@
             case SelectionEvent.ACTION_ABANDON:  // fall through
             case SelectionEvent.ACTION_SELECT_ALL:  // fall through
             case SelectionEvent.ACTION_RESET:  // fall through
+            case SelectionEvent.ACTION_OTHER:  // fall through
                 return;
             default:
                 throw new IllegalArgumentException(
@@ -667,4 +654,4 @@
             return new SelectionEvent[size];
         }
     };
-}
\ No newline at end of file
+}
diff --git a/android/view/textclassifier/DefaultLogger.java b/android/view/textclassifier/SelectionSessionLogger.java
similarity index 88%
rename from android/view/textclassifier/DefaultLogger.java
rename to android/view/textclassifier/SelectionSessionLogger.java
index 203ca56..f2fb63e 100644
--- a/android/view/textclassifier/DefaultLogger.java
+++ b/android/view/textclassifier/SelectionSessionLogger.java
@@ -17,28 +17,29 @@
 package android.view.textclassifier;
 
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.content.Context;
 import android.metrics.LogMaker;
-import android.util.Log;
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.logging.MetricsLogger;
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
 import com.android.internal.util.Preconditions;
 
+import java.text.BreakIterator;
 import java.util.List;
 import java.util.Locale;
 import java.util.Objects;
 import java.util.StringJoiner;
 
 /**
- * Default Logger.
- * Used internally by TextClassifierImpl.
+ * A helper for logging selection session events.
  * @hide
  */
-public final class DefaultLogger extends Logger {
+public final class SelectionSessionLogger {
 
-    private static final String LOG_TAG = "DefaultLogger";
+    private static final String LOG_TAG = "SelectionSessionLogger";
+    private static final boolean DEBUG_LOG_ENABLED = false;
     static final String CLASSIFIER_ID = "androidtc";
 
     private static final int START_EVENT_DELTA = MetricsEvent.FIELD_SELECTION_SINCE_START;
@@ -59,23 +60,16 @@
 
     private final MetricsLogger mMetricsLogger;
 
-    public DefaultLogger(@NonNull Config config) {
-        super(config);
+    public SelectionSessionLogger() {
         mMetricsLogger = new MetricsLogger();
     }
 
     @VisibleForTesting
-    public DefaultLogger(@NonNull Config config, @NonNull MetricsLogger metricsLogger) {
-        super(config);
+    public SelectionSessionLogger(@NonNull MetricsLogger metricsLogger) {
         mMetricsLogger = Preconditions.checkNotNull(metricsLogger);
     }
 
-    @Override
-    public boolean isSmartSelection(@NonNull String signature) {
-        return CLASSIFIER_ID.equals(SignatureParser.getClassifierId(signature));
-    }
-
-    @Override
+    /** Emits a selection event to the logs. */
     public void writeEvent(@NonNull SelectionEvent event) {
         Preconditions.checkNotNull(event);
         final LogMaker log = new LogMaker(MetricsEvent.TEXT_SELECTION_SESSION)
@@ -93,7 +87,7 @@
                 .addTaggedData(SMART_END, event.getSmartEnd())
                 .addTaggedData(EVENT_START, event.getStart())
                 .addTaggedData(EVENT_END, event.getEnd())
-                .addTaggedData(SESSION_ID, event.getSessionId());
+                .addTaggedData(SESSION_ID, event.getSessionId().flattenToString());
         mMetricsLogger.write(log);
         debugLog(log);
     }
@@ -225,9 +219,17 @@
         final int eventEnd = Integer.parseInt(
                 Objects.toString(log.getTaggedData(EVENT_END), ZERO));
 
-        Log.d(LOG_TAG, String.format("%2d: %s/%s/%s, range=%d,%d - smart_range=%d,%d (%s/%s)",
-                index, type, subType, entity, eventStart, eventEnd, smartStart, smartEnd, widget,
-                model));
+        Log.d(LOG_TAG,
+                String.format(Locale.US, "%2d: %s/%s/%s, range=%d,%d - smart_range=%d,%d (%s/%s)",
+                        index, type, subType, entity, eventStart, eventEnd, smartStart, smartEnd,
+                        widget, model));
+    }
+
+    /**
+     * Returns a token iterator for tokenizing text for logging purposes.
+     */
+    public static BreakIterator getTokenIterator(@NonNull Locale locale) {
+        return BreakIterator.getWordInstance(Preconditions.checkNotNull(locale));
     }
 
     /**
@@ -260,8 +262,10 @@
             return String.format(Locale.US, "%s|%s|%d", classifierId, modelName, hash);
         }
 
-        static String getClassifierId(String signature) {
-            Preconditions.checkNotNull(signature);
+        static String getClassifierId(@Nullable String signature) {
+            if (signature == null) {
+                return "";
+            }
             final int end = signature.indexOf("|");
             if (end >= 0) {
                 return signature.substring(0, end);
@@ -269,8 +273,10 @@
             return "";
         }
 
-        static String getModelName(String signature) {
-            Preconditions.checkNotNull(signature);
+        static String getModelName(@Nullable String signature) {
+            if (signature == null) {
+                return "";
+            }
             final int start = signature.indexOf("|") + 1;
             final int end = signature.indexOf("|", start);
             if (start >= 1 && end >= start) {
@@ -279,8 +285,10 @@
             return "";
         }
 
-        static int getHash(String signature) {
-            Preconditions.checkNotNull(signature);
+        static int getHash(@Nullable String signature) {
+            if (signature == null) {
+                return 0;
+            }
             final int index1 = signature.indexOf("|");
             final int index2 = signature.indexOf("|", index1);
             if (index2 > 0) {
diff --git a/android/view/textclassifier/SystemTextClassifier.java b/android/view/textclassifier/SystemTextClassifier.java
index 45fd6bf..490c389 100644
--- a/android/view/textclassifier/SystemTextClassifier.java
+++ b/android/view/textclassifier/SystemTextClassifier.java
@@ -28,7 +28,6 @@
 import android.service.textclassifier.ITextLinksCallback;
 import android.service.textclassifier.ITextSelectionCallback;
 
-import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.annotations.VisibleForTesting.Visibility;
 import com.android.internal.util.Preconditions;
@@ -49,13 +48,6 @@
     private final TextClassificationConstants mSettings;
     private final TextClassifier mFallback;
     private final String mPackageName;
-
-    private final Object mLoggerLock = new Object();
-    @GuardedBy("mLoggerLock")
-    private Logger.Config mLoggerConfig;
-    @GuardedBy("mLoggerLock")
-    private Logger mLogger;
-    @GuardedBy("mLoggerLock")
     private TextClassificationSessionId mSessionId;
 
     public SystemTextClassifier(Context context, TextClassificationConstants settings)
@@ -147,27 +139,6 @@
     }
 
     @Override
-    public Logger getLogger(@NonNull Logger.Config config) {
-        Preconditions.checkNotNull(config);
-        synchronized (mLoggerLock) {
-            if (mLogger == null || !config.equals(mLoggerConfig)) {
-                mLoggerConfig = config;
-                mLogger = new Logger(config) {
-                    @Override
-                    public void writeEvent(SelectionEvent event) {
-                        try {
-                            mManagerService.onSelectionEvent(mSessionId, event);
-                        } catch (RemoteException e) {
-                            Log.e(LOG_TAG, "Error reporting selection event.", e);
-                        }
-                    }
-                };
-            }
-        }
-        return mLogger;
-    }
-
-    @Override
     public void destroy() {
         try {
             if (mSessionId != null) {
diff --git a/android/view/textclassifier/TextClassification.java b/android/view/textclassifier/TextClassification.java
index 37a5d9a..96016b4 100644
--- a/android/view/textclassifier/TextClassification.java
+++ b/android/view/textclassifier/TextClassification.java
@@ -375,13 +375,13 @@
      */
     public static final class Builder {
 
-        @NonNull private String mText;
         @NonNull private List<RemoteAction> mActions = new ArrayList<>();
         @NonNull private final Map<String, Float> mEntityConfidence = new ArrayMap<>();
-        @Nullable Drawable mLegacyIcon;
-        @Nullable String mLegacyLabel;
-        @Nullable Intent mLegacyIntent;
-        @Nullable OnClickListener mLegacyOnClickListener;
+        @Nullable private String mText;
+        @Nullable private Drawable mLegacyIcon;
+        @Nullable private String mLegacyLabel;
+        @Nullable private Intent mLegacyIntent;
+        @Nullable private OnClickListener mLegacyOnClickListener;
         @Nullable private String mId;
 
         /**
@@ -721,4 +721,67 @@
         mEntityConfidence = EntityConfidence.CREATOR.createFromParcel(in);
         mId = in.readString();
     }
+
+    // TODO: Remove once apps can build against the latest sdk.
+    /**
+     * Optional input parameters for generating TextClassification.
+     * @hide
+     */
+    public static final class Options {
+
+        @Nullable private final TextClassificationSessionId mSessionId;
+        @Nullable private final Request mRequest;
+        @Nullable private LocaleList mDefaultLocales;
+        @Nullable private ZonedDateTime mReferenceTime;
+
+        public Options() {
+            this(null, null);
+        }
+
+        private Options(
+                @Nullable TextClassificationSessionId sessionId, @Nullable Request request) {
+            mSessionId = sessionId;
+            mRequest = request;
+        }
+
+        /** Helper to create Options from a Request. */
+        public static Options from(TextClassificationSessionId sessionId, Request request) {
+            final Options options = new Options(sessionId, request);
+            options.setDefaultLocales(request.getDefaultLocales());
+            options.setReferenceTime(request.getReferenceTime());
+            return options;
+        }
+
+        /** @param defaultLocales ordered list of locale preferences. */
+        public Options setDefaultLocales(@Nullable LocaleList defaultLocales) {
+            mDefaultLocales = defaultLocales;
+            return this;
+        }
+
+        /** @param referenceTime refrence time used for interpreting relatives dates */
+        public Options setReferenceTime(@Nullable ZonedDateTime referenceTime) {
+            mReferenceTime = referenceTime;
+            return this;
+        }
+
+        @Nullable
+        public LocaleList getDefaultLocales() {
+            return mDefaultLocales;
+        }
+
+        @Nullable
+        public ZonedDateTime getReferenceTime() {
+            return mReferenceTime;
+        }
+
+        @Nullable
+        public Request getRequest() {
+            return mRequest;
+        }
+
+        @Nullable
+        public TextClassificationSessionId getSessionId() {
+            return mSessionId;
+        }
+    }
 }
diff --git a/android/view/textclassifier/TextClassificationSession.java b/android/view/textclassifier/TextClassificationSession.java
index e8e300a..4c64198 100644
--- a/android/view/textclassifier/TextClassificationSession.java
+++ b/android/view/textclassifier/TextClassificationSession.java
@@ -17,7 +17,6 @@
 package android.view.textclassifier;
 
 import android.annotation.WorkerThread;
-import android.view.textclassifier.DefaultLogger.SignatureParser;
 import android.view.textclassifier.SelectionEvent.InvocationMethod;
 
 import com.android.internal.util.Preconditions;
@@ -222,7 +221,8 @@
         }
 
         private static boolean isPlatformLocalTextClassifierSmartSelection(String signature) {
-            return DefaultLogger.CLASSIFIER_ID.equals(SignatureParser.getClassifierId(signature));
+            return SelectionSessionLogger.CLASSIFIER_ID.equals(
+                    SelectionSessionLogger.SignatureParser.getClassifierId(signature));
         }
     }
 }
diff --git a/android/view/textclassifier/TextClassifier.java b/android/view/textclassifier/TextClassifier.java
index 54261be..da47bcb 100644
--- a/android/view/textclassifier/TextClassifier.java
+++ b/android/view/textclassifier/TextClassifier.java
@@ -41,8 +41,9 @@
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Collections;
-import java.util.List;
+import java.util.HashSet;
 import java.util.Map;
+import java.util.Set;
 
 /**
  * Interface for providing text classification related features.
@@ -208,6 +209,26 @@
         return suggestSelection(request);
     }
 
+    // TODO: Remove once apps can build against the latest sdk.
+    /** @hide */
+    default TextSelection suggestSelection(
+            @NonNull CharSequence text,
+            @IntRange(from = 0) int selectionStartIndex,
+            @IntRange(from = 0) int selectionEndIndex,
+            @Nullable TextSelection.Options options) {
+        if (options == null) {
+            return suggestSelection(new TextSelection.Request.Builder(
+                    text, selectionStartIndex, selectionEndIndex).build());
+        } else if (options.getRequest() != null) {
+            return suggestSelection(options.getRequest());
+        } else {
+            return suggestSelection(
+                    new TextSelection.Request.Builder(text, selectionStartIndex, selectionEndIndex)
+                            .setDefaultLocales(options.getDefaultLocales())
+                            .build());
+        }
+    }
+
     /**
      * Classifies the specified text and returns a {@link TextClassification} object that can be
      * used to generate a widget for handling the classified text.
@@ -267,6 +288,26 @@
         return classifyText(request);
     }
 
+    // TODO: Remove once apps can build against the latest sdk.
+    /** @hide */
+    default TextClassification classifyText(
+            @NonNull CharSequence text,
+            @IntRange(from = 0) int startIndex,
+            @IntRange(from = 0) int endIndex,
+            @Nullable TextClassification.Options options) {
+        if (options == null) {
+            return classifyText(
+                    new TextClassification.Request.Builder(text, startIndex, endIndex).build());
+        } else if (options.getRequest() != null) {
+            return classifyText(options.getRequest());
+        } else {
+            return classifyText(new TextClassification.Request.Builder(text, startIndex, endIndex)
+                    .setDefaultLocales(options.getDefaultLocales())
+                    .setReferenceTime(options.getReferenceTime())
+                    .build());
+        }
+    }
+
     /**
      * Generates and returns a {@link TextLinks} that may be applied to the text to annotate it with
      * links information.
@@ -288,6 +329,22 @@
         return new TextLinks.Builder(request.getText().toString()).build();
     }
 
+    // TODO: Remove once apps can build against the latest sdk.
+    /** @hide */
+    default TextLinks generateLinks(
+            @NonNull CharSequence text, @Nullable TextLinks.Options options) {
+        if (options == null) {
+            return generateLinks(new TextLinks.Request.Builder(text).build());
+        } else if (options.getRequest() != null) {
+            return generateLinks(options.getRequest());
+        } else {
+            return generateLinks(new TextLinks.Request.Builder(text)
+                    .setDefaultLocales(options.getDefaultLocales())
+                    .setEntityConfig(options.getEntityConfig())
+                    .build());
+        }
+    }
+
     /**
      * Returns the maximal length of text that can be processed by generateLinks.
      *
@@ -302,18 +359,6 @@
     }
 
     /**
-     * Returns a helper for logging TextClassifier related events.
-     *
-     * @param config logger configuration
-     * @hide
-     */
-    @WorkerThread
-    default Logger getLogger(@NonNull Logger.Config config) {
-        Preconditions.checkNotNull(config);
-        return Logger.DISABLED;
-    }
-
-    /**
      * Reports a selection event.
      *
      * <strong>NOTE: </strong>If a TextClassifier has been destroyed, calls to this method should
@@ -377,6 +422,12 @@
                     /* includedEntityTypes */null, /* excludedEntityTypes */ null);
         }
 
+        // TODO: Remove once apps can build against the latest sdk.
+        /** @hide */
+        public static EntityConfig create(@Nullable Collection<String> hints) {
+            return createWithHints(hints);
+        }
+
         /**
          * Creates an EntityConfig.
          *
@@ -406,6 +457,12 @@
                     /* includedEntityTypes */ entityTypes, /* excludedEntityTypes */ null);
         }
 
+        // TODO: Remove once apps can build against the latest sdk.
+        /** @hide */
+        public static EntityConfig createWithEntityList(@Nullable Collection<String> entityTypes) {
+            return createWithExplicitEntityList(entityTypes);
+        }
+
         /**
          * Returns a list of the final set of entities to find.
          *
@@ -413,21 +470,15 @@
          *
          * This method is intended for use by TextClassifier implementations.
          */
-        public List<String> resolveEntityListModifications(@NonNull Collection<String> entities) {
-            final ArrayList<String> finalList = new ArrayList<>();
+        public Collection<String> resolveEntityListModifications(
+                @NonNull Collection<String> entities) {
+            final Set<String> finalSet = new HashSet();
             if (mUseHints) {
-                for (String entity : entities) {
-                    if (!mExcludedEntityTypes.contains(entity)) {
-                        finalList.add(entity);
-                    }
-                }
+                finalSet.addAll(entities);
             }
-            for (String entity : mIncludedEntityTypes) {
-                if (!mExcludedEntityTypes.contains(entity) && !finalList.contains(entity)) {
-                    finalList.add(entity);
-                }
-            }
-            return finalList;
+            finalSet.addAll(mIncludedEntityTypes);
+            finalSet.removeAll(mExcludedEntityTypes);
+            return finalSet;
         }
 
         /**
@@ -508,7 +559,7 @@
             final String string = request.getText().toString();
             final TextLinks.Builder links = new TextLinks.Builder(string);
 
-            final List<String> entities = request.getEntityConfig()
+            final Collection<String> entities = request.getEntityConfig()
                     .resolveEntityListModifications(Collections.emptyList());
             if (entities.contains(TextClassifier.TYPE_URL)) {
                 addLinks(links, string, TextClassifier.TYPE_URL);
diff --git a/android/view/textclassifier/TextClassifierImpl.java b/android/view/textclassifier/TextClassifierImpl.java
index 7e3748a..2213355 100644
--- a/android/view/textclassifier/TextClassifierImpl.java
+++ b/android/view/textclassifier/TextClassifierImpl.java
@@ -94,11 +94,7 @@
 
     private final Object mLoggerLock = new Object();
     @GuardedBy("mLoggerLock") // Do not access outside this lock.
-    private Logger.Config mLoggerConfig;
-    @GuardedBy("mLoggerLock") // Do not access outside this lock.
-    private Logger mLogger;
-    @GuardedBy("mLoggerLock") // Do not access outside this lock.
-    private Logger mLogger2;  // This is the new logger. Will replace mLogger.
+    private SelectionSessionLogger mSessionLogger;
 
     private final TextClassificationConstants mSettings;
 
@@ -283,28 +279,14 @@
         }
     }
 
-    /** @inheritDoc */
-    @Override
-    public Logger getLogger(@NonNull Logger.Config config) {
-        Preconditions.checkNotNull(config);
-        synchronized (mLoggerLock) {
-            if (mLogger == null || !config.equals(mLoggerConfig)) {
-                mLoggerConfig = config;
-                mLogger = new DefaultLogger(config);
-            }
-        }
-        return mLogger;
-    }
-
     @Override
     public void onSelectionEvent(SelectionEvent event) {
         Preconditions.checkNotNull(event);
         synchronized (mLoggerLock) {
-            if (mLogger2 == null) {
-                mLogger2 = new DefaultLogger(
-                        new Logger.Config(mContext, WIDGET_TYPE_UNKNOWN, null));
+            if (mSessionLogger == null) {
+                mSessionLogger = new SelectionSessionLogger();
             }
-            mLogger2.writeEvent(event);
+            mSessionLogger.writeEvent(event);
         }
     }
 
@@ -331,7 +313,7 @@
 
     private String createId(String text, int start, int end) {
         synchronized (mLock) {
-            return DefaultLogger.createId(text, start, end, mContext, mModel.getVersion(),
+            return SelectionSessionLogger.createId(text, start, end, mContext, mModel.getVersion(),
                     mModel.getSupportedLocales());
         }
     }
diff --git a/android/view/textclassifier/TextLinks.java b/android/view/textclassifier/TextLinks.java
index 17c7b13..851b2c9 100644
--- a/android/view/textclassifier/TextLinks.java
+++ b/android/view/textclassifier/TextLinks.java
@@ -28,6 +28,8 @@
 import android.text.method.MovementMethod;
 import android.text.style.ClickableSpan;
 import android.text.style.URLSpan;
+import android.text.util.Linkify;
+import android.text.util.Linkify.LinkifyMask;
 import android.view.View;
 import android.view.textclassifier.TextClassifier.EntityType;
 import android.widget.TextView;
@@ -337,7 +339,7 @@
 
         /**
          * @return The config representing the set of entities to look for
-         * @see #setEntityConfig(TextClassifier.EntityConfig)
+         * @see Builder#setEntityConfig(TextClassifier.EntityConfig)
          */
         @Nullable
         public TextClassifier.EntityConfig getEntityConfig() {
@@ -607,4 +609,124 @@
             return new TextLinks(mFullText, mLinks);
         }
     }
+
+    // TODO: Remove once apps can build against the latest sdk.
+    /**
+     * Optional input parameters for generating TextLinks.
+     * @hide
+     */
+    public static final class Options {
+
+        @Nullable private final TextClassificationSessionId mSessionId;
+        @Nullable private final Request mRequest;
+        @Nullable private LocaleList mDefaultLocales;
+        @Nullable private TextClassifier.EntityConfig mEntityConfig;
+        private boolean mLegacyFallback;
+
+        private @ApplyStrategy int mApplyStrategy;
+        private Function<TextLink, TextLinkSpan> mSpanFactory;
+
+        private String mCallingPackageName;
+
+        public Options() {
+            this(null, null);
+        }
+
+        private Options(
+                @Nullable TextClassificationSessionId sessionId, @Nullable Request request) {
+            mSessionId = sessionId;
+            mRequest = request;
+        }
+
+        /** Helper to create Options from a Request. */
+        public static Options from(TextClassificationSessionId sessionId, Request request) {
+            final Options options = new Options(sessionId, request);
+            options.setDefaultLocales(request.getDefaultLocales());
+            options.setEntityConfig(request.getEntityConfig());
+            return options;
+        }
+
+        /** Returns a new options object based on the specified link mask. */
+        public static Options fromLinkMask(@LinkifyMask int mask) {
+            final List<String> entitiesToFind = new ArrayList<>();
+
+            if ((mask & Linkify.WEB_URLS) != 0) {
+                entitiesToFind.add(TextClassifier.TYPE_URL);
+            }
+            if ((mask & Linkify.EMAIL_ADDRESSES) != 0) {
+                entitiesToFind.add(TextClassifier.TYPE_EMAIL);
+            }
+            if ((mask & Linkify.PHONE_NUMBERS) != 0) {
+                entitiesToFind.add(TextClassifier.TYPE_PHONE);
+            }
+            if ((mask & Linkify.MAP_ADDRESSES) != 0) {
+                entitiesToFind.add(TextClassifier.TYPE_ADDRESS);
+            }
+
+            return new Options().setEntityConfig(
+                    TextClassifier.EntityConfig.createWithEntityList(entitiesToFind));
+        }
+
+        /** @param defaultLocales ordered list of locale preferences. */
+        public Options setDefaultLocales(@Nullable LocaleList defaultLocales) {
+            mDefaultLocales = defaultLocales;
+            return this;
+        }
+
+        /** @param entityConfig definition of which entity types to look for. */
+        public Options setEntityConfig(@Nullable TextClassifier.EntityConfig entityConfig) {
+            mEntityConfig = entityConfig;
+            return this;
+        }
+
+        /** @param applyStrategy strategy to use when resolving conflicts. */
+        public Options setApplyStrategy(@ApplyStrategy int applyStrategy) {
+            checkValidApplyStrategy(applyStrategy);
+            mApplyStrategy = applyStrategy;
+            return this;
+        }
+
+        /** @param spanFactory factory for converting TextLink to TextLinkSpan. */
+        public Options setSpanFactory(@Nullable Function<TextLink, TextLinkSpan> spanFactory) {
+            mSpanFactory = spanFactory;
+            return this;
+        }
+
+        @Nullable
+        public LocaleList getDefaultLocales() {
+            return mDefaultLocales;
+        }
+
+        @Nullable
+        public TextClassifier.EntityConfig getEntityConfig() {
+            return mEntityConfig;
+        }
+
+        @ApplyStrategy
+        public int getApplyStrategy() {
+            return mApplyStrategy;
+        }
+
+        @Nullable
+        public Function<TextLink, TextLinkSpan> getSpanFactory() {
+            return mSpanFactory;
+        }
+
+        @Nullable
+        public Request getRequest() {
+            return mRequest;
+        }
+
+        @Nullable
+        public TextClassificationSessionId getSessionId() {
+            return mSessionId;
+        }
+
+        private static void checkValidApplyStrategy(int applyStrategy) {
+            if (applyStrategy != APPLY_STRATEGY_IGNORE && applyStrategy != APPLY_STRATEGY_REPLACE) {
+                throw new IllegalArgumentException(
+                        "Invalid apply strategy. See TextLinks.ApplyStrategy for options.");
+            }
+        }
+    }
 }
diff --git a/android/view/textclassifier/TextSelection.java b/android/view/textclassifier/TextSelection.java
index 939e717..17687c9 100644
--- a/android/view/textclassifier/TextSelection.java
+++ b/android/view/textclassifier/TextSelection.java
@@ -375,4 +375,56 @@
         mEntityConfidence = EntityConfidence.CREATOR.createFromParcel(in);
         mId = in.readString();
     }
+
+
+    // TODO: Remove once apps can build against the latest sdk.
+    /**
+     * Optional input parameters for generating TextSelection.
+     * @hide
+     */
+    public static final class Options {
+
+        @Nullable private final TextClassificationSessionId mSessionId;
+        @Nullable private final Request mRequest;
+        @Nullable private LocaleList mDefaultLocales;
+        private boolean mDarkLaunchAllowed;
+
+        public Options() {
+            this(null, null);
+        }
+
+        private Options(
+                @Nullable TextClassificationSessionId sessionId, @Nullable Request request) {
+            mSessionId = sessionId;
+            mRequest = request;
+        }
+
+        /** Helper to create Options from a Request. */
+        public static Options from(TextClassificationSessionId sessionId, Request request) {
+            final Options options = new Options(sessionId, request);
+            options.setDefaultLocales(request.getDefaultLocales());
+            return options;
+        }
+
+        /** @param defaultLocales ordered list of locale preferences. */
+        public Options setDefaultLocales(@Nullable LocaleList defaultLocales) {
+            mDefaultLocales = defaultLocales;
+            return this;
+        }
+
+        @Nullable
+        public LocaleList getDefaultLocales() {
+            return mDefaultLocales;
+        }
+
+        @Nullable
+        public Request getRequest() {
+            return mRequest;
+        }
+
+        @Nullable
+        public TextClassificationSessionId getSessionId() {
+            return mSessionId;
+        }
+    }
 }
diff --git a/android/view/textservice/TextServicesManager.java b/android/view/textservice/TextServicesManager.java
index 8e1f218..21ec42b 100644
--- a/android/view/textservice/TextServicesManager.java
+++ b/android/view/textservice/TextServicesManager.java
@@ -1,58 +1,219 @@
 /*
- * Copyright (C) 2016 The Android Open Source Project
+ * Copyright (C) 2011 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
+ * 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
+ * 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.
+ * 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.textservice;
 
+import android.annotation.SystemService;
+import android.content.Context;
 import android.os.Bundle;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.os.ServiceManager.ServiceNotFoundException;
+import android.util.Log;
 import android.view.textservice.SpellCheckerSession.SpellCheckerSessionListener;
 
+import com.android.internal.textservice.ITextServicesManager;
+
 import java.util.Locale;
 
 /**
- * A stub class of TextServicesManager for Layout-Lib.
+ * System API to the overall text services, which arbitrates interaction between applications
+ * and text services.
+ *
+ * The user can change the current text services in Settings. And also applications can specify
+ * the target text services.
+ *
+ * <h3>Architecture Overview</h3>
+ *
+ * <p>There are three primary parties involved in the text services
+ * framework (TSF) architecture:</p>
+ *
+ * <ul>
+ * <li> The <strong>text services manager</strong> as expressed by this class
+ * is the central point of the system that manages interaction between all
+ * other parts.  It is expressed as the client-side API here which exists
+ * in each application context and communicates with a global system service
+ * that manages the interaction across all processes.
+ * <li> A <strong>text service</strong> implements a particular
+ * interaction model allowing the client application to retrieve information of text.
+ * The system binds to the current text service that is in use, causing it to be created and run.
+ * <li> Multiple <strong>client applications</strong> arbitrate with the text service
+ * manager for connections to text services.
+ * </ul>
+ *
+ * <h3>Text services sessions</h3>
+ * <ul>
+ * <li>The <strong>spell checker session</strong> is one of the text services.
+ * {@link android.view.textservice.SpellCheckerSession}</li>
+ * </ul>
+ *
  */
+@SystemService(Context.TEXT_SERVICES_MANAGER_SERVICE)
 public final class TextServicesManager {
-    private static final TextServicesManager sInstance = new TextServicesManager();
-    private static final SpellCheckerInfo[] EMPTY_SPELL_CHECKER_INFO = new SpellCheckerInfo[0];
+    private static final String TAG = TextServicesManager.class.getSimpleName();
+    private static final boolean DBG = false;
+
+    /**
+     * A compile time switch to control per-profile spell checker, which is not yet ready.
+     * @hide
+     */
+    public static final boolean DISABLE_PER_PROFILE_SPELL_CHECKER = true;
+
+    private static TextServicesManager sInstance;
+
+    private final ITextServicesManager mService;
+
+    private TextServicesManager() throws ServiceNotFoundException {
+        mService = ITextServicesManager.Stub.asInterface(
+                ServiceManager.getServiceOrThrow(Context.TEXT_SERVICES_MANAGER_SERVICE));
+    }
 
     /**
      * Retrieve the global TextServicesManager instance, creating it if it doesn't already exist.
      * @hide
      */
     public static TextServicesManager getInstance() {
-        return sInstance;
+        synchronized (TextServicesManager.class) {
+            if (sInstance == null) {
+                try {
+                    sInstance = new TextServicesManager();
+                } catch (ServiceNotFoundException e) {
+                    throw new IllegalStateException(e);
+                }
+            }
+            return sInstance;
+        }
     }
 
+    /**
+     * Returns the language component of a given locale string.
+     */
+    private static String parseLanguageFromLocaleString(String locale) {
+        final int idx = locale.indexOf('_');
+        if (idx < 0) {
+            return locale;
+        } else {
+            return locale.substring(0, idx);
+        }
+    }
+
+    /**
+     * Get a spell checker session for the specified spell checker
+     * @param locale the locale for the spell checker. If {@code locale} is null and
+     * referToSpellCheckerLanguageSettings is true, the locale specified in Settings will be
+     * returned. If {@code locale} is not null and referToSpellCheckerLanguageSettings is true,
+     * the locale specified in Settings will be returned only when it is same as {@code locale}.
+     * Exceptionally, when referToSpellCheckerLanguageSettings is true and {@code locale} is
+     * only language (e.g. "en"), the specified locale in Settings (e.g. "en_US") will be
+     * selected.
+     * @param listener a spell checker session lister for getting results from a spell checker.
+     * @param referToSpellCheckerLanguageSettings if true, the session for one of enabled
+     * languages in settings will be returned.
+     * @return the spell checker session of the spell checker
+     */
     public SpellCheckerSession newSpellCheckerSession(Bundle bundle, Locale locale,
             SpellCheckerSessionListener listener, boolean referToSpellCheckerLanguageSettings) {
-        return null;
+        if (listener == null) {
+            throw new NullPointerException();
+        }
+        if (!referToSpellCheckerLanguageSettings && locale == null) {
+            throw new IllegalArgumentException("Locale should not be null if you don't refer"
+                    + " settings.");
+        }
+
+        if (referToSpellCheckerLanguageSettings && !isSpellCheckerEnabled()) {
+            return null;
+        }
+
+        final SpellCheckerInfo sci;
+        try {
+            sci = mService.getCurrentSpellChecker(null);
+        } catch (RemoteException e) {
+            return null;
+        }
+        if (sci == null) {
+            return null;
+        }
+        SpellCheckerSubtype subtypeInUse = null;
+        if (referToSpellCheckerLanguageSettings) {
+            subtypeInUse = getCurrentSpellCheckerSubtype(true);
+            if (subtypeInUse == null) {
+                return null;
+            }
+            if (locale != null) {
+                final String subtypeLocale = subtypeInUse.getLocale();
+                final String subtypeLanguage = parseLanguageFromLocaleString(subtypeLocale);
+                if (subtypeLanguage.length() < 2 || !locale.getLanguage().equals(subtypeLanguage)) {
+                    return null;
+                }
+            }
+        } else {
+            final String localeStr = locale.toString();
+            for (int i = 0; i < sci.getSubtypeCount(); ++i) {
+                final SpellCheckerSubtype subtype = sci.getSubtypeAt(i);
+                final String tempSubtypeLocale = subtype.getLocale();
+                final String tempSubtypeLanguage = parseLanguageFromLocaleString(tempSubtypeLocale);
+                if (tempSubtypeLocale.equals(localeStr)) {
+                    subtypeInUse = subtype;
+                    break;
+                } else if (tempSubtypeLanguage.length() >= 2 &&
+                        locale.getLanguage().equals(tempSubtypeLanguage)) {
+                    subtypeInUse = subtype;
+                }
+            }
+        }
+        if (subtypeInUse == null) {
+            return null;
+        }
+        final SpellCheckerSession session = new SpellCheckerSession(sci, mService, listener);
+        try {
+            mService.getSpellCheckerService(sci.getId(), subtypeInUse.getLocale(),
+                    session.getTextServicesSessionListener(),
+                    session.getSpellCheckerSessionListener(), bundle);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+        return session;
     }
 
     /**
      * @hide
      */
     public SpellCheckerInfo[] getEnabledSpellCheckers() {
-        return EMPTY_SPELL_CHECKER_INFO;
+        try {
+            final SpellCheckerInfo[] retval = mService.getEnabledSpellCheckers();
+            if (DBG) {
+                Log.d(TAG, "getEnabledSpellCheckers: " + (retval != null ? retval.length : "null"));
+            }
+            return retval;
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
     }
 
     /**
      * @hide
      */
     public SpellCheckerInfo getCurrentSpellChecker() {
-        return null;
+        try {
+            // Passing null as a locale for ICS
+            return mService.getCurrentSpellChecker(null);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
     }
 
     /**
@@ -60,13 +221,22 @@
      */
     public SpellCheckerSubtype getCurrentSpellCheckerSubtype(
             boolean allowImplicitlySelectedSubtype) {
-        return null;
+        try {
+            // Passing null as a locale until we support multiple enabled spell checker subtypes.
+            return mService.getCurrentSpellCheckerSubtype(null, allowImplicitlySelectedSubtype);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
     }
 
     /**
      * @hide
      */
     public boolean isSpellCheckerEnabled() {
-        return false;
+        try {
+            return mService.isSpellCheckerEnabled();
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
     }
 }
diff --git a/android/webkit/FindAddress.java b/android/webkit/FindAddress.java
index 31b2427..9183227 100644
--- a/android/webkit/FindAddress.java
+++ b/android/webkit/FindAddress.java
@@ -429,20 +429,21 @@
 
                     // At this point we've matched a state; try to match a zip code after it.
                     Matcher zipMatcher = sWordRe.matcher(content);
-                    if (zipMatcher.find(stateMatch.end())
-                            && isValidZipCode(zipMatcher.group(0), stateMatch)) {
-                        return zipMatcher.end();
+                    if (zipMatcher.find(stateMatch.end())) {
+                        if (isValidZipCode(zipMatcher.group(0), stateMatch)) {
+                            return zipMatcher.end();
+                        }
+                    } else {
+                        // The content ends with a state but no zip
+                        // code. This is a legal match according to the
+                        // documentation. N.B. This is equivalent to the
+                        // original c++ implementation, which only allowed
+                        // the zip code to be optional at the end of the
+                        // string, which presumably is a bug.  We tried
+                        // relaxing this to work in other places but it
+                        // caused too many false positives.
+                        nonZipMatch = stateMatch.end();
                     }
-                    // The content ends with a state but no zip
-                    // code. This is a legal match according to the
-                    // documentation. N.B. This differs from the
-                    // original c++ implementation, which only allowed
-                    // the zip code to be optional at the end of the
-                    // string, which presumably is a bug.  Now we
-                    // prefer to find a match with a zip code, but
-                    // remember non-zip matches and return them if
-                    // necessary.
-                    nonZipMatch = stateMatch.end();
                 }
             }
         }
diff --git a/android/webkit/TracingConfig.java b/android/webkit/TracingConfig.java
index d95ca61..2080168 100644
--- a/android/webkit/TracingConfig.java
+++ b/android/webkit/TracingConfig.java
@@ -54,37 +54,37 @@
 
     /**
      * Predefined set of categories typically useful for analyzing WebViews.
-     * Typically includes android_webview and Java.
+     * Typically includes "android_webview" and "Java" categories.
      */
     public static final int CATEGORIES_ANDROID_WEBVIEW = 1 << 1;
 
     /**
      * Predefined set of categories typically useful for web developers.
-     * Typically includes blink, compositor, renderer.scheduler and v8 categories.
+     * Typically includes "blink", "compositor", "renderer.scheduler" and "v8" categories.
      */
     public static final int CATEGORIES_WEB_DEVELOPER = 1 << 2;
 
     /**
      * Predefined set of categories for analyzing input latency issues.
-     * Typically includes input, renderer.scheduler categories.
+     * Typically includes "input", "renderer.scheduler" categories.
      */
     public static final int CATEGORIES_INPUT_LATENCY = 1 << 3;
 
     /**
      * Predefined set of categories for analyzing rendering issues.
-     * Typically includes blink, compositor and gpu categories.
+     * Typically includes "blink", "compositor" and "gpu" categories.
      */
     public static final int CATEGORIES_RENDERING = 1 << 4;
 
     /**
      * Predefined set of categories for analyzing javascript and rendering issues.
-     * Typically includes blink, compositor, gpu, renderer.scheduler and v8 categories.
+     * Typically includes "blink", "compositor", "gpu", "renderer.scheduler" and "v8" categories.
      */
     public static final int CATEGORIES_JAVASCRIPT_AND_RENDERING = 1 << 5;
 
     /**
      * Predefined set of categories for studying difficult rendering performance problems.
-     * Typically includes blink, compositor, gpu, renderer.scheduler, v8 and
+     * Typically includes "blink", "compositor", "gpu", "renderer.scheduler", "v8" and
      * some other compositor categories which are disabled by default.
      */
     public static final int CATEGORIES_FRAME_VIEWER = 1 << 6;
@@ -123,7 +123,9 @@
     }
 
     /**
-     * Returns a bitmask of the predefined categories values of this configuration.
+     * Returns a bitmask of the predefined category sets of this configuration.
+     *
+     * @return Bitmask of predefined category sets.
      */
     @PredefinedCategories
     public int getPredefinedCategories() {
@@ -133,7 +135,7 @@
     /**
      * Returns the list of included custom category patterns for this configuration.
      *
-     * @return empty list if no custom category patterns are specified.
+     * @return Empty list if no custom category patterns are specified.
      */
     @NonNull
     public List<String> getCustomIncludedCategories() {
@@ -142,6 +144,8 @@
 
     /**
      * Returns the tracing mode of this configuration.
+     *
+     * @return The tracing mode of this configuration.
      */
     @TracingMode
     public int getTracingMode() {
@@ -150,28 +154,37 @@
 
     /**
      * Builder used to create {@link TracingConfig} objects.
-     *
+     * <p>
      * Examples:
-     *   new TracingConfig.Builder().build()
-     *       -- creates a configuration with default options: {@link #CATEGORIES_NONE},
-     *          {@link #RECORD_UNTIL_FULL}.
-     *   new TracingConfig.Builder().addCategories(CATEGORIES_WEB_DEVELOPER).build()
-     *       -- records trace events from the "web developer" predefined category sets.
-     *   new TracingConfig.Builder().addCategories(CATEGORIES_RENDERING,
-     *                                             CATEGORIES_INPUT_LATENCY).build()
-     *       -- records trace events from the "rendering" and "input latency" predefined
-     *          category sets.
-     *   new TracingConfig.Builder().addCategories("browser").build()
-     *       -- records only the trace events from the "browser" category.
-     *   new TracingConfig.Builder().addCategories("blink*","renderer*").build()
-     *       -- records only the trace events matching the "blink*" and "renderer*" patterns
-     *          (e.g. "blink.animations", "renderer_host" and "renderer.scheduler" categories).
-     *   new TracingConfig.Builder().addCategories(CATEGORIES_WEB_DEVELOPER)
+     * <pre class="prettyprint">
+     *   // Create a configuration with default options: {@link #CATEGORIES_NONE},
+     *   // {@link #RECORD_CONTINUOUSLY}.
+     *   <code>new TracingConfig.Builder().build()</code>
+     *
+     *   // Record trace events from the "web developer" predefined category sets.
+     *   // Uses a ring buffer (the default {@link #RECORD_CONTINUOUSLY} mode) for
+     *   // internal storage during tracing.
+     *   <code>new TracingConfig.Builder().addCategories(CATEGORIES_WEB_DEVELOPER).build()</code>
+     *
+     *   // Record trace events from the "rendering" and "input latency" predefined
+     *   // category sets.
+     *   <code>new TracingConfig.Builder().addCategories(CATEGORIES_RENDERING,
+     *                                     CATEGORIES_INPUT_LATENCY).build()</code>
+     *
+     *   // Record only the trace events from the "browser" category.
+     *   <code>new TracingConfig.Builder().addCategories("browser").build()</code>
+     *
+     *   // Record only the trace events matching the "blink*" and "renderer*" patterns
+     *   // (e.g. "blink.animations", "renderer_host" and "renderer.scheduler" categories).
+     *   <code>new TracingConfig.Builder().addCategories("blink*","renderer*").build()</code>
+     *
+     *   // Record events from the "web developer" predefined category set and events from
+     *   // the "disabled-by-default-v8.gc" category to understand where garbage collection
+     *   // is being triggered. Uses a limited size buffer for internal storage during tracing.
+     *   <code>new TracingConfig.Builder().addCategories(CATEGORIES_WEB_DEVELOPER)
      *                              .addCategories("disabled-by-default-v8.gc")
-     *                              .setTracingMode(RECORD_CONTINUOUSLY).build()
-     *       -- records events from the "web developer" predefined category set and events from
-     *          the "disabled-by-default-v8.gc" category to understand where garbage collection
-     *          is being triggered. Uses a ring buffer for internal storage during tracing.
+     *                              .setTracingMode(RECORD_UNTIL_FULL).build()</code>
+     * </pre>
      */
     public static class Builder {
         private @PredefinedCategories int mPredefinedCategories = CATEGORIES_NONE;
@@ -185,6 +198,8 @@
 
         /**
          * Build {@link TracingConfig} using the current settings.
+         *
+         * @return The {@link TracingConfig} with the current settings.
          */
         public TracingConfig build() {
             return new TracingConfig(mPredefinedCategories, mCustomIncludedCategories,
@@ -192,16 +207,15 @@
         }
 
         /**
-         * Adds categories from a predefined set of categories to be included in the trace output.
+         * Adds predefined sets of categories to be included in the trace output.
          *
-         * @param predefinedCategories list or bitmask of predefined category sets to use:
-         *                    {@link #CATEGORIES_NONE}, {@link #CATEGORIES_ALL},
-         *                    {@link #CATEGORIES_ANDROID_WEBVIEW},
-         *                    {@link #CATEGORIES_WEB_DEVELOPER},
-         *                    {@link #CATEGORIES_INPUT_LATENCY},
-         *                    {@link #CATEGORIES_RENDERING},
-         *                    {@link #CATEGORIES_JAVASCRIPT_AND_RENDERING} or
-         *                    {@link #CATEGORIES_FRAME_VIEWER}.
+         * A predefined category set can be one of {@link #CATEGORIES_NONE},
+         * {@link #CATEGORIES_ALL}, {@link #CATEGORIES_ANDROID_WEBVIEW},
+         * {@link #CATEGORIES_WEB_DEVELOPER}, {@link #CATEGORIES_INPUT_LATENCY},
+         * {@link #CATEGORIES_RENDERING}, {@link #CATEGORIES_JAVASCRIPT_AND_RENDERING} or
+         * {@link #CATEGORIES_FRAME_VIEWER}.
+         *
+         * @param predefinedCategories A list or bitmask of predefined category sets.
          * @return The builder to facilitate chaining.
          */
         public Builder addCategories(@PredefinedCategories int... predefinedCategories) {
@@ -215,11 +229,11 @@
          * Adds custom categories to be included in trace output.
          *
          * Note that the categories are defined by the currently-in-use version of WebView. They
-         * live in chromium code and are not part of the Android API. See
+         * live in chromium code and are not part of the Android API.
          * See <a href="https://www.chromium.org/developers/how-tos/trace-event-profiling-tool">
          * chromium documentation on tracing</a> for more details.
          *
-         * @param categories a list of category patterns. A category pattern can contain wilcards,
+         * @param categories A list of category patterns. A category pattern can contain wildcards,
          *        e.g. "blink*" or full category name e.g. "renderer.scheduler".
          * @return The builder to facilitate chaining.
          */
@@ -235,7 +249,7 @@
          *
          * Same as {@link #addCategories(String...)} but allows to pass a Collection as a parameter.
          *
-         * @param categories a list of category patters.
+         * @param categories A list of category patterns.
          * @return The builder to facilitate chaining.
          */
         public Builder addCategories(Collection<String> categories) {
@@ -245,8 +259,9 @@
 
         /**
          * Sets the tracing mode for this configuration.
+         * When tracingMode is not set explicitly, the default is {@link #RECORD_CONTINUOUSLY}.
          *
-         * @param tracingMode tracing mode to use, one of {@link #RECORD_UNTIL_FULL} or
+         * @param tracingMode The tracing mode to use, one of {@link #RECORD_UNTIL_FULL} or
          *                    {@link #RECORD_CONTINUOUSLY}.
          * @return The builder to facilitate chaining.
          */
diff --git a/android/webkit/TracingController.java b/android/webkit/TracingController.java
index 50068f5..05c0304 100644
--- a/android/webkit/TracingController.java
+++ b/android/webkit/TracingController.java
@@ -35,9 +35,9 @@
  * Example usage:
  * <pre class="prettyprint">
  * TracingController tracingController = TracingController.getInstance();
- * tracingController.start(new TraceConfig.Builder()
+ * tracingController.start(new TracingConfig.Builder()
  *                  .addCategories(CATEGORIES_WEB_DEVELOPER).build());
- * [..]
+ * ...
  * tracingController.stop(new FileOutputStream("trace.json"),
  *                        Executors.newSingleThreadExecutor());
  * </pre></p>
@@ -49,7 +49,7 @@
      * only one TracingController instance for all WebView instances,
      * however this restriction may be relaxed in a future Android release.
      *
-     * @return the default TracingController instance
+     * @return The default TracingController instance.
      */
     @NonNull
     public static TracingController getInstance() {
@@ -65,8 +65,10 @@
      * using an internal buffer and flushed to the outputStream when
      * {@link #stop(OutputStream, Executor)} is called.
      *
-     * @param tracingConfig configuration options to use for tracing
-     * @throws IllegalStateException if the system is already tracing.
+     * @param tracingConfig Configuration options to use for tracing.
+     * @throws IllegalStateException If the system is already tracing.
+     * @throws IllegalArgumentException If the configuration is invalid (e.g.
+     *         invalid category pattern or invalid tracing mode).
      */
     public abstract void start(@NonNull TracingConfig tracingConfig);
 
@@ -77,17 +79,22 @@
      * in chunks by invoking {@link java.io.OutputStream#write(byte[])}. On completion
      * the {@link java.io.OutputStream#close()} method is called.
      *
-     * @param outputStream the output steam the tracing data will be sent to. If null
+     * @param outputStream The output stream the tracing data will be sent to. If null
      *                     the tracing data will be discarded.
-     * @param executor the {@link java.util.concurrent.Executor} on which the
-     *        outputStream #write and #close methods will be invoked.
-     * @return false if the system was not tracing at the time of the call, true
-     *         otherwise.
+     * @param executor The {@link java.util.concurrent.Executor} on which the
+     *        outputStream {@link java.io.OutputStream#write(byte[])} and
+     *        {@link java.io.OutputStream#close()} methods will be invoked.
+     * @return False if the WebView framework was not tracing at the time of the call,
+     *         true otherwise.
      */
     public abstract boolean stop(@Nullable OutputStream outputStream,
             @NonNull @CallbackExecutor Executor executor);
 
-    /** True if the system is tracing */
+    /**
+     * Returns whether the WebView framework is tracing.
+     *
+     * @return True if tracing is enabled.
+     */
     public abstract boolean isTracing();
 
 }
diff --git a/android/webkit/WebView.java b/android/webkit/WebView.java
index 202f204..10748ac 100644
--- a/android/webkit/WebView.java
+++ b/android/webkit/WebView.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2008 The Android Open Source Project
+ * Copyright (C) 2006 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -16,223 +16,3136 @@
 
 package android.webkit;
 
-import com.android.layoutlib.bridge.MockView;
-
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.annotation.Widget;
 import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageInfo;
+import android.content.res.Configuration;
 import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.Paint;
 import android.graphics.Picture;
+import android.graphics.Rect;
+import android.graphics.drawable.Drawable;
+import android.net.Uri;
+import android.net.http.SslCertificate;
+import android.os.Build;
 import android.os.Bundle;
+import android.os.Handler;
+import android.os.Looper;
 import android.os.Message;
+import android.os.RemoteException;
+import android.os.StrictMode;
+import android.print.PrintDocumentAdapter;
+import android.security.KeyChain;
 import android.util.AttributeSet;
+import android.util.Log;
+import android.util.SparseArray;
+import android.view.DragEvent;
+import android.view.KeyEvent;
+import android.view.MotionEvent;
 import android.view.View;
+import android.view.ViewDebug;
+import android.view.ViewGroup;
+import android.view.ViewHierarchyEncoder;
+import android.view.ViewStructure;
+import android.view.ViewTreeObserver;
+import android.view.accessibility.AccessibilityEvent;
+import android.view.accessibility.AccessibilityNodeInfo;
+import android.view.accessibility.AccessibilityNodeProvider;
+import android.view.autofill.AutofillValue;
+import android.view.inputmethod.EditorInfo;
+import android.view.inputmethod.InputConnection;
+import android.view.textclassifier.TextClassifier;
+import android.widget.AbsoluteLayout;
+
+import java.io.BufferedWriter;
+import java.io.File;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.List;
+import java.util.Map;
 
 /**
- * Mock version of the WebView.
- * Only non override public methods from the real WebView have been added in there.
- * Methods that take an unknown class as parameter or as return object, have been removed for now.
- * 
- * TODO: generate automatically.
+ * <p>A View that displays web pages. This class is the basis upon which you
+ * can roll your own web browser or simply display some online content within your Activity.
+ * It uses the WebKit rendering engine to display
+ * web pages and includes methods to navigate forward and backward
+ * through a history, zoom in and out, perform text searches and more.
+ *
+ * <p>Note that, in order for your Activity to access the Internet and load web pages
+ * in a WebView, you must add the {@code INTERNET} permissions to your
+ * Android Manifest file:
+ *
+ * <pre>
+ * {@code <uses-permission android:name="android.permission.INTERNET" />}
+ * </pre>
+ *
+ * <p>This must be a child of the <a
+ * href="{@docRoot}guide/topics/manifest/manifest-element.html">{@code <manifest>}</a>
+ * element.
+ *
+ * <p>For more information, read
+ * <a href="{@docRoot}guide/webapps/webview.html">Building Web Apps in WebView</a>.
+ *
+ * <h3>Basic usage</h3>
+ *
+ * <p>By default, a WebView provides no browser-like widgets, does not
+ * enable JavaScript and web page errors are ignored. If your goal is only
+ * to display some HTML as a part of your UI, this is probably fine;
+ * the user won't need to interact with the web page beyond reading
+ * it, and the web page won't need to interact with the user. If you
+ * actually want a full-blown web browser, then you probably want to
+ * invoke the Browser application with a URL Intent rather than show it
+ * with a WebView. For example:
+ * <pre>
+ * Uri uri = Uri.parse("https://www.example.com");
+ * Intent intent = new Intent(Intent.ACTION_VIEW, uri);
+ * startActivity(intent);
+ * </pre>
+ * <p>See {@link android.content.Intent} for more information.
+ *
+ * <p>To provide a WebView in your own Activity, include a {@code <WebView>} in your layout,
+ * or set the entire Activity window as a WebView during {@link
+ * android.app.Activity#onCreate(Bundle) onCreate()}:
+ *
+ * <pre class="prettyprint">
+ * WebView webview = new WebView(this);
+ * setContentView(webview);
+ * </pre>
+ *
+ * <p>Then load the desired web page:
+ *
+ * <pre>
+ * // Simplest usage: note that an exception will NOT be thrown
+ * // if there is an error loading this page (see below).
+ * webview.loadUrl("https://example.com/");
+ *
+ * // OR, you can also load from an HTML string:
+ * String summary = "&lt;html>&lt;body>You scored &lt;b>192&lt;/b> points.&lt;/body>&lt;/html>";
+ * webview.loadData(summary, "text/html", null);
+ * // ... although note that there are restrictions on what this HTML can do.
+ * // See {@link #loadData(String,String,String)} and {@link
+ * #loadDataWithBaseURL(String,String,String,String,String)} for more info.
+ * // Also see {@link #loadData(String,String,String)} for information on encoding special
+ * // characters.
+ * </pre>
+ *
+ * <p>A WebView has several customization points where you can add your
+ * own behavior. These are:
+ *
+ * <ul>
+ *   <li>Creating and setting a {@link android.webkit.WebChromeClient} subclass.
+ *       This class is called when something that might impact a
+ *       browser UI happens, for instance, progress updates and
+ *       JavaScript alerts are sent here (see <a
+ * href="{@docRoot}guide/developing/debug-tasks.html#DebuggingWebPages">Debugging Tasks</a>).
+ *   </li>
+ *   <li>Creating and setting a {@link android.webkit.WebViewClient} subclass.
+ *       It will be called when things happen that impact the
+ *       rendering of the content, eg, errors or form submissions. You
+ *       can also intercept URL loading here (via {@link
+ * android.webkit.WebViewClient#shouldOverrideUrlLoading(WebView,String)
+ * shouldOverrideUrlLoading()}).</li>
+ *   <li>Modifying the {@link android.webkit.WebSettings}, such as
+ * enabling JavaScript with {@link android.webkit.WebSettings#setJavaScriptEnabled(boolean)
+ * setJavaScriptEnabled()}. </li>
+ *   <li>Injecting Java objects into the WebView using the
+ *       {@link android.webkit.WebView#addJavascriptInterface} method. This
+ *       method allows you to inject Java objects into a page's JavaScript
+ *       context, so that they can be accessed by JavaScript in the page.</li>
+ * </ul>
+ *
+ * <p>Here's a more complicated example, showing error handling,
+ *    settings, and progress notification:
+ *
+ * <pre class="prettyprint">
+ * // Let's display the progress in the activity title bar, like the
+ * // browser app does.
+ * getWindow().requestFeature(Window.FEATURE_PROGRESS);
+ *
+ * webview.getSettings().setJavaScriptEnabled(true);
+ *
+ * final Activity activity = this;
+ * webview.setWebChromeClient(new WebChromeClient() {
+ *   public void onProgressChanged(WebView view, int progress) {
+ *     // Activities and WebViews measure progress with different scales.
+ *     // The progress meter will automatically disappear when we reach 100%
+ *     activity.setProgress(progress * 1000);
+ *   }
+ * });
+ * webview.setWebViewClient(new WebViewClient() {
+ *   public void onReceivedError(WebView view, int errorCode, String description, String failingUrl) {
+ *     Toast.makeText(activity, "Oh no! " + description, Toast.LENGTH_SHORT).show();
+ *   }
+ * });
+ *
+ * webview.loadUrl("https://developer.android.com/");
+ * </pre>
+ *
+ * <h3>Zoom</h3>
+ *
+ * <p>To enable the built-in zoom, set
+ * {@link #getSettings() WebSettings}.{@link WebSettings#setBuiltInZoomControls(boolean)}
+ * (introduced in API level {@link android.os.Build.VERSION_CODES#CUPCAKE}).
+ *
+ * <p class="note"><b>Note:</b> Using zoom if either the height or width is set to
+ * {@link android.view.ViewGroup.LayoutParams#WRAP_CONTENT} may lead to undefined behavior
+ * and should be avoided.
+ *
+ * <h3>Cookie and window management</h3>
+ *
+ * <p>For obvious security reasons, your application has its own
+ * cache, cookie store etc.&mdash;it does not share the Browser
+ * application's data.
+ *
+ * <p>By default, requests by the HTML to open new windows are
+ * ignored. This is {@code true} whether they be opened by JavaScript or by
+ * the target attribute on a link. You can customize your
+ * {@link WebChromeClient} to provide your own behavior for opening multiple windows,
+ * and render them in whatever manner you want.
+ *
+ * <p>The standard behavior for an Activity is to be destroyed and
+ * recreated when the device orientation or any other configuration changes. This will cause
+ * the WebView to reload the current page. If you don't want that, you
+ * can set your Activity to handle the {@code orientation} and {@code keyboardHidden}
+ * changes, and then just leave the WebView alone. It'll automatically
+ * re-orient itself as appropriate. Read <a
+ * href="{@docRoot}guide/topics/resources/runtime-changes.html">Handling Runtime Changes</a> for
+ * more information about how to handle configuration changes during runtime.
+ *
+ *
+ * <h3>Building web pages to support different screen densities</h3>
+ *
+ * <p>The screen density of a device is based on the screen resolution. A screen with low density
+ * has fewer available pixels per inch, where a screen with high density
+ * has more &mdash; sometimes significantly more &mdash; pixels per inch. The density of a
+ * screen is important because, other things being equal, a UI element (such as a button) whose
+ * height and width are defined in terms of screen pixels will appear larger on the lower density
+ * screen and smaller on the higher density screen.
+ * For simplicity, Android collapses all actual screen densities into three generalized densities:
+ * high, medium, and low.
+ * <p>By default, WebView scales a web page so that it is drawn at a size that matches the default
+ * appearance on a medium density screen. So, it applies 1.5x scaling on a high density screen
+ * (because its pixels are smaller) and 0.75x scaling on a low density screen (because its pixels
+ * are bigger).
+ * Starting with API level {@link android.os.Build.VERSION_CODES#ECLAIR}, WebView supports DOM, CSS,
+ * and meta tag features to help you (as a web developer) target screens with different screen
+ * densities.
+ * <p>Here's a summary of the features you can use to handle different screen densities:
+ * <ul>
+ * <li>The {@code window.devicePixelRatio} DOM property. The value of this property specifies the
+ * default scaling factor used for the current device. For example, if the value of {@code
+ * window.devicePixelRatio} is "1.0", then the device is considered a medium density (mdpi) device
+ * and default scaling is not applied to the web page; if the value is "1.5", then the device is
+ * considered a high density device (hdpi) and the page content is scaled 1.5x; if the
+ * value is "0.75", then the device is considered a low density device (ldpi) and the content is
+ * scaled 0.75x.</li>
+ * <li>The {@code -webkit-device-pixel-ratio} CSS media query. Use this to specify the screen
+ * densities for which this style sheet is to be used. The corresponding value should be either
+ * "0.75", "1", or "1.5", to indicate that the styles are for devices with low density, medium
+ * density, or high density screens, respectively. For example:
+ * <pre>
+ * &lt;link rel="stylesheet" media="screen and (-webkit-device-pixel-ratio:1.5)" href="hdpi.css" /&gt;</pre>
+ * <p>The {@code hdpi.css} stylesheet is only used for devices with a screen pixel ratio of 1.5,
+ * which is the high density pixel ratio.
+ * </li>
+ * </ul>
+ *
+ * <h3>HTML5 Video support</h3>
+ *
+ * <p>In order to support inline HTML5 video in your application you need to have hardware
+ * acceleration turned on.
+ *
+ * <h3>Full screen support</h3>
+ *
+ * <p>In order to support full screen &mdash; for video or other HTML content &mdash; you need to set a
+ * {@link android.webkit.WebChromeClient} and implement both
+ * {@link WebChromeClient#onShowCustomView(View, WebChromeClient.CustomViewCallback)}
+ * and {@link WebChromeClient#onHideCustomView()}. If the implementation of either of these two methods is
+ * missing then the web contents will not be allowed to enter full screen. Optionally you can implement
+ * {@link WebChromeClient#getVideoLoadingProgressView()} to customize the View displayed whilst a video
+ * is loading.
+ *
+ * <h3>HTML5 Geolocation API support</h3>
+ *
+ * <p>For applications targeting Android N and later releases
+ * (API level > {@link android.os.Build.VERSION_CODES#M}) the geolocation api is only supported on
+ * secure origins such as https. For such applications requests to geolocation api on non-secure
+ * origins are automatically denied without invoking the corresponding
+ * {@link WebChromeClient#onGeolocationPermissionsShowPrompt(String, GeolocationPermissions.Callback)}
+ * method.
+ *
+ * <h3>Layout size</h3>
+ * <p>
+ * It is recommended to set the WebView layout height to a fixed value or to
+ * {@link android.view.ViewGroup.LayoutParams#MATCH_PARENT} instead of using
+ * {@link android.view.ViewGroup.LayoutParams#WRAP_CONTENT}.
+ * When using {@link android.view.ViewGroup.LayoutParams#MATCH_PARENT}
+ * for the height none of the WebView's parents should use a
+ * {@link android.view.ViewGroup.LayoutParams#WRAP_CONTENT} layout height since that could result in
+ * incorrect sizing of the views.
+ *
+ * <p>Setting the WebView's height to {@link android.view.ViewGroup.LayoutParams#WRAP_CONTENT}
+ * enables the following behaviors:
+ * <ul>
+ * <li>The HTML body layout height is set to a fixed value. This means that elements with a height
+ * relative to the HTML body may not be sized correctly. </li>
+ * <li>For applications targeting {@link android.os.Build.VERSION_CODES#KITKAT} and earlier SDKs the
+ * HTML viewport meta tag will be ignored in order to preserve backwards compatibility. </li>
+ * </ul>
+ *
+ * <p>
+ * Using a layout width of {@link android.view.ViewGroup.LayoutParams#WRAP_CONTENT} is not
+ * supported. If such a width is used the WebView will attempt to use the width of the parent
+ * instead.
+ *
+ * <h3>Metrics</h3>
+ *
+ * <p>
+ * WebView may upload anonymous diagnostic data to Google when the user has consented. This data
+ * helps Google improve WebView. Data is collected on a per-app basis for each app which has
+ * instantiated a WebView. An individual app can opt out of this feature by putting the following
+ * tag in its manifest's {@code <application>} element:
+ * <pre>
+ * &lt;manifest&gt;
+ *     &lt;application&gt;
+ *         ...
+ *         &lt;meta-data android:name=&quot;android.webkit.WebView.MetricsOptOut&quot;
+ *             android:value=&quot;true&quot; /&gt;
+ *     &lt;/application&gt;
+ * &lt;/manifest&gt;
+ * </pre>
+ * <p>
+ * Data will only be uploaded for a given app if the user has consented AND the app has not opted
+ * out.
+ *
+ * <h3>Safe Browsing</h3>
+ *
+ * <p>
+ * With Safe Browsing, WebView will block malicious URLs and present a warning UI to the user to
+ * allow them to navigate back safely or proceed to the malicious page.
+ * <p>
+ * Safe Browsing is enabled by default on devices which support it. If your app needs to disable
+ * Safe Browsing for all WebViews, it can do so in the manifest's {@code <application>} element:
+ * <p>
+ * <pre>
+ * &lt;manifest&gt;
+ *     &lt;application&gt;
+ *         ...
+ *         &lt;meta-data android:name=&quot;android.webkit.WebView.EnableSafeBrowsing&quot;
+ *             android:value=&quot;false&quot; /&gt;
+ *     &lt;/application&gt;
+ * &lt;/manifest&gt;
+ * </pre>
+ *
+ * <p>
+ * Otherwise, see {@link WebSettings#setSafeBrowsingEnabled}.
  *
  */
-public class WebView extends MockView {
+// Implementation notes.
+// The WebView is a thin API class that delegates its public API to a backend WebViewProvider
+// class instance. WebView extends {@link AbsoluteLayout} for backward compatibility reasons.
+// Methods are delegated to the provider implementation: all public API methods introduced in this
+// file are fully delegated, whereas public and protected methods from the View base classes are
+// only delegated where a specific need exists for them to do so.
+@Widget
+public class WebView extends AbsoluteLayout
+        implements ViewTreeObserver.OnGlobalFocusChangeListener,
+        ViewGroup.OnHierarchyChangeListener, ViewDebug.HierarchyHandler {
+
+    private static final String LOGTAG = "WebView";
+
+    // Throwing an exception for incorrect thread usage if the
+    // build target is JB MR2 or newer. Defaults to false, and is
+    // set in the WebView constructor.
+    private static volatile boolean sEnforceThreadChecking = false;
 
     /**
-     * Construct a new WebView with a Context object.
-     * @param context A Context object used to access application assets.
+     *  Transportation object for returning WebView across thread boundaries.
+     */
+    public class WebViewTransport {
+        private WebView mWebview;
+
+        /**
+         * Sets the WebView to the transportation object.
+         *
+         * @param webview the WebView to transport
+         */
+        public synchronized void setWebView(WebView webview) {
+            mWebview = webview;
+        }
+
+        /**
+         * Gets the WebView object.
+         *
+         * @return the transported WebView object
+         */
+        public synchronized WebView getWebView() {
+            return mWebview;
+        }
+    }
+
+    /**
+     * URI scheme for telephone number.
+     */
+    public static final String SCHEME_TEL = "tel:";
+    /**
+     * URI scheme for email address.
+     */
+    public static final String SCHEME_MAILTO = "mailto:";
+    /**
+     * URI scheme for map address.
+     */
+    public static final String SCHEME_GEO = "geo:0,0?q=";
+
+    /**
+     * Interface to listen for find results.
+     */
+    public interface FindListener {
+        /**
+         * Notifies the listener about progress made by a find operation.
+         *
+         * @param activeMatchOrdinal the zero-based ordinal of the currently selected match
+         * @param numberOfMatches how many matches have been found
+         * @param isDoneCounting whether the find operation has actually completed. The listener
+         *                       may be notified multiple times while the
+         *                       operation is underway, and the numberOfMatches
+         *                       value should not be considered final unless
+         *                       isDoneCounting is {@code true}.
+         */
+        public void onFindResultReceived(int activeMatchOrdinal, int numberOfMatches,
+            boolean isDoneCounting);
+    }
+
+    /**
+     * Callback interface supplied to {@link #postVisualStateCallback} for receiving
+     * notifications about the visual state.
+     */
+    public static abstract class VisualStateCallback {
+        /**
+         * Invoked when the visual state is ready to be drawn in the next {@link #onDraw}.
+         *
+         * @param requestId The identifier passed to {@link #postVisualStateCallback} when this
+         *                  callback was posted.
+         */
+        public abstract void onComplete(long requestId);
+    }
+
+    /**
+     * Interface to listen for new pictures as they change.
+     *
+     * @deprecated This interface is now obsolete.
+     */
+    @Deprecated
+    public interface PictureListener {
+        /**
+         * Used to provide notification that the WebView's picture has changed.
+         * See {@link WebView#capturePicture} for details of the picture.
+         *
+         * @param view the WebView that owns the picture
+         * @param picture the new picture. Applications targeting
+         *     {@link android.os.Build.VERSION_CODES#JELLY_BEAN_MR2} or above
+         *     will always receive a {@code null} Picture.
+         * @deprecated Deprecated due to internal changes.
+         */
+        @Deprecated
+        void onNewPicture(WebView view, @Nullable Picture picture);
+    }
+
+    public static class HitTestResult {
+        /**
+         * Default HitTestResult, where the target is unknown.
+         */
+        public static final int UNKNOWN_TYPE = 0;
+        /**
+         * @deprecated This type is no longer used.
+         */
+        @Deprecated
+        public static final int ANCHOR_TYPE = 1;
+        /**
+         * HitTestResult for hitting a phone number.
+         */
+        public static final int PHONE_TYPE = 2;
+        /**
+         * HitTestResult for hitting a map address.
+         */
+        public static final int GEO_TYPE = 3;
+        /**
+         * HitTestResult for hitting an email address.
+         */
+        public static final int EMAIL_TYPE = 4;
+        /**
+         * HitTestResult for hitting an HTML::img tag.
+         */
+        public static final int IMAGE_TYPE = 5;
+        /**
+         * @deprecated This type is no longer used.
+         */
+        @Deprecated
+        public static final int IMAGE_ANCHOR_TYPE = 6;
+        /**
+         * HitTestResult for hitting a HTML::a tag with src=http.
+         */
+        public static final int SRC_ANCHOR_TYPE = 7;
+        /**
+         * HitTestResult for hitting a HTML::a tag with src=http + HTML::img.
+         */
+        public static final int SRC_IMAGE_ANCHOR_TYPE = 8;
+        /**
+         * HitTestResult for hitting an edit text area.
+         */
+        public static final int EDIT_TEXT_TYPE = 9;
+
+        private int mType;
+        private String mExtra;
+
+        /**
+         * @hide Only for use by WebViewProvider implementations
+         */
+        @SystemApi
+        public HitTestResult() {
+            mType = UNKNOWN_TYPE;
+        }
+
+        /**
+         * @hide Only for use by WebViewProvider implementations
+         */
+        @SystemApi
+        public void setType(int type) {
+            mType = type;
+        }
+
+        /**
+         * @hide Only for use by WebViewProvider implementations
+         */
+        @SystemApi
+        public void setExtra(String extra) {
+            mExtra = extra;
+        }
+
+        /**
+         * Gets the type of the hit test result. See the XXX_TYPE constants
+         * defined in this class.
+         *
+         * @return the type of the hit test result
+         */
+        public int getType() {
+            return mType;
+        }
+
+        /**
+         * Gets additional type-dependant information about the result. See
+         * {@link WebView#getHitTestResult()} for details. May either be {@code null}
+         * or contain extra information about this result.
+         *
+         * @return additional type-dependant information about the result
+         */
+        @Nullable
+        public String getExtra() {
+            return mExtra;
+        }
+    }
+
+    /**
+     * Constructs a new WebView with an Activity Context object.
+     *
+     * <p class="note"><b>Note:</b> WebView should always be instantiated with an Activity Context.
+     * If instantiated with an Application Context, WebView will be unable to provide several
+     * features, such as JavaScript dialogs and autofill.
+     *
+     * @param context an Activity Context to access application assets
      */
     public WebView(Context context) {
         this(context, null);
     }
 
     /**
-     * Construct a new WebView with layout parameters.
-     * @param context A Context object used to access application assets.
-     * @param attrs An AttributeSet passed to our parent.
+     * Constructs a new WebView with layout parameters.
+     *
+     * @param context an Activity Context to access application assets
+     * @param attrs an AttributeSet passed to our parent
      */
     public WebView(Context context, AttributeSet attrs) {
         this(context, attrs, com.android.internal.R.attr.webViewStyle);
     }
 
     /**
-     * Construct a new WebView with layout parameters and a default style.
-     * @param context A Context object used to access application assets.
-     * @param attrs An AttributeSet passed to our parent.
-     * @param defStyle The default style resource ID.
+     * Constructs a new WebView with layout parameters and a default style.
+     *
+     * @param context an Activity Context to access application assets
+     * @param attrs an AttributeSet passed to our parent
+     * @param defStyleAttr an attribute in the current theme that contains a
+     *        reference to a style resource that supplies default values for
+     *        the view. Can be 0 to not look for defaults.
      */
-    public WebView(Context context, AttributeSet attrs, int defStyle) {
-        super(context, attrs, defStyle);
+    public WebView(Context context, AttributeSet attrs, int defStyleAttr) {
+        this(context, attrs, defStyleAttr, 0);
     }
-    
-    // START FAKE PUBLIC METHODS
-    
+
+    /**
+     * Constructs a new WebView with layout parameters and a default style.
+     *
+     * @param context an Activity Context to access application assets
+     * @param attrs an AttributeSet passed to our parent
+     * @param defStyleAttr an attribute in the current theme that contains a
+     *        reference to a style resource that supplies default values for
+     *        the view. Can be 0 to not look for defaults.
+     * @param defStyleRes a resource identifier of a style resource that
+     *        supplies default values for the view, used only if
+     *        defStyleAttr is 0 or can not be found in the theme. Can be 0
+     *        to not look for defaults.
+     */
+    public WebView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
+        this(context, attrs, defStyleAttr, defStyleRes, null, false);
+    }
+
+    /**
+     * Constructs a new WebView with layout parameters and a default style.
+     *
+     * @param context an Activity Context to access application assets
+     * @param attrs an AttributeSet passed to our parent
+     * @param defStyleAttr an attribute in the current theme that contains a
+     *        reference to a style resource that supplies default values for
+     *        the view. Can be 0 to not look for defaults.
+     * @param privateBrowsing whether this WebView will be initialized in
+     *                        private mode
+     *
+     * @deprecated Private browsing is no longer supported directly via
+     * WebView and will be removed in a future release. Prefer using
+     * {@link WebSettings}, {@link WebViewDatabase}, {@link CookieManager}
+     * and {@link WebStorage} for fine-grained control of privacy data.
+     */
+    @Deprecated
+    public WebView(Context context, AttributeSet attrs, int defStyleAttr,
+            boolean privateBrowsing) {
+        this(context, attrs, defStyleAttr, 0, null, privateBrowsing);
+    }
+
+    /**
+     * Constructs a new WebView with layout parameters, a default style and a set
+     * of custom JavaScript interfaces to be added to this WebView at initialization
+     * time. This guarantees that these interfaces will be available when the JS
+     * context is initialized.
+     *
+     * @param context an Activity Context to access application assets
+     * @param attrs an AttributeSet passed to our parent
+     * @param defStyleAttr an attribute in the current theme that contains a
+     *        reference to a style resource that supplies default values for
+     *        the view. Can be 0 to not look for defaults.
+     * @param javaScriptInterfaces a Map of interface names, as keys, and
+     *                             object implementing those interfaces, as
+     *                             values
+     * @param privateBrowsing whether this WebView will be initialized in
+     *                        private mode
+     * @hide This is used internally by dumprendertree, as it requires the JavaScript interfaces to
+     *       be added synchronously, before a subsequent loadUrl call takes effect.
+     */
+    protected WebView(Context context, AttributeSet attrs, int defStyleAttr,
+            Map<String, Object> javaScriptInterfaces, boolean privateBrowsing) {
+        this(context, attrs, defStyleAttr, 0, javaScriptInterfaces, privateBrowsing);
+    }
+
+    /**
+     * @hide
+     */
+    @SuppressWarnings("deprecation")  // for super() call into deprecated base class constructor.
+    protected WebView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes,
+            Map<String, Object> javaScriptInterfaces, boolean privateBrowsing) {
+        super(context, attrs, defStyleAttr, defStyleRes);
+
+        // WebView is important by default, unless app developer overrode attribute.
+        if (getImportantForAutofill() == IMPORTANT_FOR_AUTOFILL_AUTO) {
+            setImportantForAutofill(IMPORTANT_FOR_AUTOFILL_YES);
+        }
+
+        if (context == null) {
+            throw new IllegalArgumentException("Invalid context argument");
+        }
+        if (mWebViewThread == null) {
+            throw new RuntimeException(
+                "WebView cannot be initialized on a thread that has no Looper.");
+        }
+        sEnforceThreadChecking = context.getApplicationInfo().targetSdkVersion >=
+                Build.VERSION_CODES.JELLY_BEAN_MR2;
+        checkThread();
+
+        ensureProviderCreated();
+        mProvider.init(javaScriptInterfaces, privateBrowsing);
+        // Post condition of creating a webview is the CookieSyncManager.getInstance() is allowed.
+        CookieSyncManager.setGetInstanceIsAllowed();
+    }
+
+    /**
+     * Specifies whether the horizontal scrollbar has overlay style.
+     *
+     * @deprecated This method has no effect.
+     * @param overlay {@code true} if horizontal scrollbar should have overlay style
+     */
+    @Deprecated
     public void setHorizontalScrollbarOverlay(boolean overlay) {
     }
 
+    /**
+     * Specifies whether the vertical scrollbar has overlay style.
+     *
+     * @deprecated This method has no effect.
+     * @param overlay {@code true} if vertical scrollbar should have overlay style
+     */
+    @Deprecated
     public void setVerticalScrollbarOverlay(boolean overlay) {
     }
 
+    /**
+     * Gets whether horizontal scrollbar has overlay style.
+     *
+     * @deprecated This method is now obsolete.
+     * @return {@code true}
+     */
+    @Deprecated
     public boolean overlayHorizontalScrollbar() {
-        return false;
+        // The old implementation defaulted to true, so return true for consistency
+        return true;
     }
 
+    /**
+     * Gets whether vertical scrollbar has overlay style.
+     *
+     * @deprecated This method is now obsolete.
+     * @return {@code false}
+     */
+    @Deprecated
     public boolean overlayVerticalScrollbar() {
+        // The old implementation defaulted to false, so return false for consistency
         return false;
     }
 
-    public void savePassword(String host, String username, String password) {
+    /**
+     * Gets the visible height (in pixels) of the embedded title bar (if any).
+     *
+     * @deprecated This method is now obsolete.
+     * @hide Since API level {@link android.os.Build.VERSION_CODES#JELLY_BEAN_MR1}
+     */
+    @Deprecated
+    public int getVisibleTitleHeight() {
+        checkThread();
+        return mProvider.getVisibleTitleHeight();
     }
 
+    /**
+     * Gets the SSL certificate for the main top-level page or {@code null} if there is
+     * no certificate (the site is not secure).
+     *
+     * @return the SSL certificate for the main top-level page
+     */
+    @Nullable
+    public SslCertificate getCertificate() {
+        checkThread();
+        return mProvider.getCertificate();
+    }
+
+    /**
+     * Sets the SSL certificate for the main top-level page.
+     *
+     * @deprecated Calling this function has no useful effect, and will be
+     * ignored in future releases.
+     */
+    @Deprecated
+    public void setCertificate(SslCertificate certificate) {
+        checkThread();
+        mProvider.setCertificate(certificate);
+    }
+
+    //-------------------------------------------------------------------------
+    // Methods called by activity
+    //-------------------------------------------------------------------------
+
+    /**
+     * Sets a username and password pair for the specified host. This data is
+     * used by the WebView to autocomplete username and password fields in web
+     * forms. Note that this is unrelated to the credentials used for HTTP
+     * authentication.
+     *
+     * @param host the host that required the credentials
+     * @param username the username for the given host
+     * @param password the password for the given host
+     * @see WebViewDatabase#clearUsernamePassword
+     * @see WebViewDatabase#hasUsernamePassword
+     * @deprecated Saving passwords in WebView will not be supported in future versions.
+     */
+    @Deprecated
+    public void savePassword(String host, String username, String password) {
+        checkThread();
+        mProvider.savePassword(host, username, password);
+    }
+
+    /**
+     * Stores HTTP authentication credentials for a given host and realm to the {@link WebViewDatabase}
+     * instance.
+     *
+     * @param host the host to which the credentials apply
+     * @param realm the realm to which the credentials apply
+     * @param username the username
+     * @param password the password
+     * @deprecated Use {@link WebViewDatabase#setHttpAuthUsernamePassword} instead
+     */
+    @Deprecated
     public void setHttpAuthUsernamePassword(String host, String realm,
             String username, String password) {
+        checkThread();
+        mProvider.setHttpAuthUsernamePassword(host, realm, username, password);
     }
 
+    /**
+     * Retrieves HTTP authentication credentials for a given host and realm from the {@link
+     * WebViewDatabase} instance.
+     * @param host the host to which the credentials apply
+     * @param realm the realm to which the credentials apply
+     * @return the credentials as a String array, if found. The first element
+     *         is the username and the second element is the password. {@code null} if
+     *         no credentials are found.
+     * @deprecated Use {@link WebViewDatabase#getHttpAuthUsernamePassword} instead
+     */
+    @Deprecated
+    @Nullable
     public String[] getHttpAuthUsernamePassword(String host, String realm) {
-        return null;
+        checkThread();
+        return mProvider.getHttpAuthUsernamePassword(host, realm);
     }
 
+    /**
+     * Destroys the internal state of this WebView. This method should be called
+     * after this WebView has been removed from the view system. No other
+     * methods may be called on this WebView after destroy.
+     */
     public void destroy() {
+        checkThread();
+        mProvider.destroy();
     }
 
+    /**
+     * Enables platform notifications of data state and proxy changes.
+     * Notifications are enabled by default.
+     *
+     * @deprecated This method is now obsolete.
+     * @hide Since API level {@link android.os.Build.VERSION_CODES#JELLY_BEAN_MR1}
+     */
+    @Deprecated
     public static void enablePlatformNotifications() {
+        // noop
     }
 
+    /**
+     * Disables platform notifications of data state and proxy changes.
+     * Notifications are enabled by default.
+     *
+     * @deprecated This method is now obsolete.
+     * @hide Since API level {@link android.os.Build.VERSION_CODES#JELLY_BEAN_MR1}
+     */
+    @Deprecated
     public static void disablePlatformNotifications() {
+        // noop
     }
 
+    /**
+     * Used only by internal tests to free up memory.
+     *
+     * @hide
+     */
+    public static void freeMemoryForTests() {
+        getFactory().getStatics().freeMemoryForTests();
+    }
+
+    /**
+     * Informs WebView of the network state. This is used to set
+     * the JavaScript property window.navigator.isOnline and
+     * generates the online/offline event as specified in HTML5, sec. 5.7.7
+     *
+     * @param networkUp a boolean indicating if network is available
+     */
+    public void setNetworkAvailable(boolean networkUp) {
+        checkThread();
+        mProvider.setNetworkAvailable(networkUp);
+    }
+
+    /**
+     * Saves the state of this WebView used in
+     * {@link android.app.Activity#onSaveInstanceState}. Please note that this
+     * method no longer stores the display data for this WebView. The previous
+     * behavior could potentially leak files if {@link #restoreState} was never
+     * called.
+     *
+     * @param outState the Bundle to store this WebView's state
+     * @return the same copy of the back/forward list used to save the state, {@code null} if the
+     *         method fails.
+     */
+    @Nullable
+    public WebBackForwardList saveState(Bundle outState) {
+        checkThread();
+        return mProvider.saveState(outState);
+    }
+
+    /**
+     * Saves the current display data to the Bundle given. Used in conjunction
+     * with {@link #saveState}.
+     * @param b a Bundle to store the display data
+     * @param dest the file to store the serialized picture data. Will be
+     *             overwritten with this WebView's picture data.
+     * @return {@code true} if the picture was successfully saved
+     * @deprecated This method is now obsolete.
+     * @hide Since API level {@link android.os.Build.VERSION_CODES#JELLY_BEAN_MR1}
+     */
+    @Deprecated
+    public boolean savePicture(Bundle b, final File dest) {
+        checkThread();
+        return mProvider.savePicture(b, dest);
+    }
+
+    /**
+     * Restores the display data that was saved in {@link #savePicture}. Used in
+     * conjunction with {@link #restoreState}. Note that this will not work if
+     * this WebView is hardware accelerated.
+     *
+     * @param b a Bundle containing the saved display data
+     * @param src the file where the picture data was stored
+     * @return {@code true} if the picture was successfully restored
+     * @deprecated This method is now obsolete.
+     * @hide Since API level {@link android.os.Build.VERSION_CODES#JELLY_BEAN_MR1}
+     */
+    @Deprecated
+    public boolean restorePicture(Bundle b, File src) {
+        checkThread();
+        return mProvider.restorePicture(b, src);
+    }
+
+    /**
+     * Restores the state of this WebView from the given Bundle. This method is
+     * intended for use in {@link android.app.Activity#onRestoreInstanceState}
+     * and should be called to restore the state of this WebView. If
+     * it is called after this WebView has had a chance to build state (load
+     * pages, create a back/forward list, etc.) there may be undesirable
+     * side-effects. Please note that this method no longer restores the
+     * display data for this WebView.
+     *
+     * @param inState the incoming Bundle of state
+     * @return the restored back/forward list or {@code null} if restoreState failed
+     */
+    @Nullable
+    public WebBackForwardList restoreState(Bundle inState) {
+        checkThread();
+        return mProvider.restoreState(inState);
+    }
+
+    /**
+     * Loads the given URL with the specified additional HTTP headers.
+     * <p>
+     * Also see compatibility note on {@link #evaluateJavascript}.
+     *
+     * @param url the URL of the resource to load
+     * @param additionalHttpHeaders the additional headers to be used in the
+     *            HTTP request for this URL, specified as a map from name to
+     *            value. Note that if this map contains any of the headers
+     *            that are set by default by this WebView, such as those
+     *            controlling caching, accept types or the User-Agent, their
+     *            values may be overridden by this WebView's defaults.
+     */
+    public void loadUrl(String url, Map<String, String> additionalHttpHeaders) {
+        checkThread();
+        mProvider.loadUrl(url, additionalHttpHeaders);
+    }
+
+    /**
+     * Loads the given URL.
+     * <p>
+     * Also see compatibility note on {@link #evaluateJavascript}.
+     *
+     * @param url the URL of the resource to load
+     */
     public void loadUrl(String url) {
+        checkThread();
+        mProvider.loadUrl(url);
     }
 
-    public void loadData(String data, String mimeType, String encoding) {
+    /**
+     * Loads the URL with postData using "POST" method into this WebView. If url
+     * is not a network URL, it will be loaded with {@link #loadUrl(String)}
+     * instead, ignoring the postData param.
+     *
+     * @param url the URL of the resource to load
+     * @param postData the data will be passed to "POST" request, which must be
+     *     be "application/x-www-form-urlencoded" encoded.
+     */
+    public void postUrl(String url, byte[] postData) {
+        checkThread();
+        if (URLUtil.isNetworkUrl(url)) {
+            mProvider.postUrl(url, postData);
+        } else {
+            mProvider.loadUrl(url);
+        }
     }
 
-    public void loadDataWithBaseURL(String baseUrl, String data,
-            String mimeType, String encoding, String failUrl) {
+    /**
+     * Loads the given data into this WebView using a 'data' scheme URL.
+     * <p>
+     * Note that JavaScript's same origin policy means that script running in a
+     * page loaded using this method will be unable to access content loaded
+     * using any scheme other than 'data', including 'http(s)'. To avoid this
+     * restriction, use {@link
+     * #loadDataWithBaseURL(String,String,String,String,String)
+     * loadDataWithBaseURL()} with an appropriate base URL.
+     * <p>
+     * The {@code encoding} parameter specifies whether the data is base64 or URL
+     * encoded. If the data is base64 encoded, the value of the encoding
+     * parameter must be 'base64'. HTML can be encoded with {@link
+     * android.util.Base64#encodeToString(byte[],int)} like so:
+     * <pre>
+     * String unencodedHtml =
+     *     "&lt;html&gt;&lt;body&gt;'%28' is the code for '('&lt;/body&gt;&lt;/html&gt;";
+     * String encodedHtml = Base64.encodeToString(unencodedHtml.getBytes(), Base64.NO_PADDING);
+     * webView.loadData(encodedHtml, "text/html", "base64");
+     * </pre>
+     * <p>
+     * For all other values of {@code encoding} (including {@code null}) it is assumed that the
+     * data uses ASCII encoding for octets inside the range of safe URL characters and use the
+     * standard %xx hex encoding of URLs for octets outside that range. See <a
+     * href="https://tools.ietf.org/html/rfc3986#section-2.2">RFC 3986</a> for more information.
+     * <p>
+     * The {@code mimeType} parameter specifies the format of the data.
+     * If WebView can't handle the specified MIME type, it will download the data.
+     * If {@code null}, defaults to 'text/html'.
+     * <p>
+     * The 'data' scheme URL formed by this method uses the default US-ASCII
+     * charset. If you need need to set a different charset, you should form a
+     * 'data' scheme URL which explicitly specifies a charset parameter in the
+     * mediatype portion of the URL and call {@link #loadUrl(String)} instead.
+     * Note that the charset obtained from the mediatype portion of a data URL
+     * always overrides that specified in the HTML or XML document itself.
+     *
+     * @param data a String of data in the given encoding
+     * @param mimeType the MIME type of the data, e.g. 'text/html'.
+     * @param encoding the encoding of the data
+     */
+    public void loadData(String data, @Nullable String mimeType, @Nullable String encoding) {
+        checkThread();
+        mProvider.loadData(data, mimeType, encoding);
     }
 
+    /**
+     * Loads the given data into this WebView, using baseUrl as the base URL for
+     * the content. The base URL is used both to resolve relative URLs and when
+     * applying JavaScript's same origin policy. The historyUrl is used for the
+     * history entry.
+     * <p>
+     * The {@code mimeType} parameter specifies the format of the data.
+     * If WebView can't handle the specified MIME type, it will download the data.
+     * If {@code null}, defaults to 'text/html'.
+     * <p>
+     * Note that content specified in this way can access local device files
+     * (via 'file' scheme URLs) only if baseUrl specifies a scheme other than
+     * 'http', 'https', 'ftp', 'ftps', 'about' or 'javascript'.
+     * <p>
+     * If the base URL uses the data scheme, this method is equivalent to
+     * calling {@link #loadData(String,String,String) loadData()} and the
+     * historyUrl is ignored, and the data will be treated as part of a data: URL.
+     * If the base URL uses any other scheme, then the data will be loaded into
+     * the WebView as a plain string (i.e. not part of a data URL) and any URL-encoded
+     * entities in the string will not be decoded.
+     * <p>
+     * Note that the baseUrl is sent in the 'Referer' HTTP header when
+     * requesting subresources (images, etc.) of the page loaded using this method.
+     *
+     * @param baseUrl the URL to use as the page's base URL. If {@code null} defaults to
+     *                'about:blank'.
+     * @param data a String of data in the given encoding
+     * @param mimeType the MIME type of the data, e.g. 'text/html'.
+     * @param encoding the encoding of the data
+     * @param historyUrl the URL to use as the history entry. If {@code null} defaults
+     *                   to 'about:blank'. If non-null, this must be a valid URL.
+     */
+    public void loadDataWithBaseURL(@Nullable String baseUrl, String data,
+            @Nullable String mimeType, @Nullable String encoding, @Nullable String historyUrl) {
+        checkThread();
+        mProvider.loadDataWithBaseURL(baseUrl, data, mimeType, encoding, historyUrl);
+    }
+
+    /**
+     * Asynchronously evaluates JavaScript in the context of the currently displayed page.
+     * If non-null, |resultCallback| will be invoked with any result returned from that
+     * execution. This method must be called on the UI thread and the callback will
+     * be made on the UI thread.
+     * <p>
+     * Compatibility note. Applications targeting {@link android.os.Build.VERSION_CODES#N} or
+     * later, JavaScript state from an empty WebView is no longer persisted across navigations like
+     * {@link #loadUrl(String)}. For example, global variables and functions defined before calling
+     * {@link #loadUrl(String)} will not exist in the loaded page. Applications should use
+     * {@link #addJavascriptInterface} instead to persist JavaScript objects across navigations.
+     *
+     * @param script the JavaScript to execute.
+     * @param resultCallback A callback to be invoked when the script execution
+     *                       completes with the result of the execution (if any).
+     *                       May be {@code null} if no notification of the result is required.
+     */
+    public void evaluateJavascript(String script, @Nullable ValueCallback<String> resultCallback) {
+        checkThread();
+        mProvider.evaluateJavaScript(script, resultCallback);
+    }
+
+    /**
+     * Saves the current view as a web archive.
+     *
+     * @param filename the filename where the archive should be placed
+     */
+    public void saveWebArchive(String filename) {
+        checkThread();
+        mProvider.saveWebArchive(filename);
+    }
+
+    /**
+     * Saves the current view as a web archive.
+     *
+     * @param basename the filename where the archive should be placed
+     * @param autoname if {@code false}, takes basename to be a file. If {@code true}, basename
+     *                 is assumed to be a directory in which a filename will be
+     *                 chosen according to the URL of the current page.
+     * @param callback called after the web archive has been saved. The
+     *                 parameter for onReceiveValue will either be the filename
+     *                 under which the file was saved, or {@code null} if saving the
+     *                 file failed.
+     */
+    public void saveWebArchive(String basename, boolean autoname, @Nullable ValueCallback<String>
+            callback) {
+        checkThread();
+        mProvider.saveWebArchive(basename, autoname, callback);
+    }
+
+    /**
+     * Stops the current load.
+     */
     public void stopLoading() {
+        checkThread();
+        mProvider.stopLoading();
     }
 
+    /**
+     * Reloads the current URL.
+     */
     public void reload() {
+        checkThread();
+        mProvider.reload();
     }
 
+    /**
+     * Gets whether this WebView has a back history item.
+     *
+     * @return {@code true} if this WebView has a back history item
+     */
     public boolean canGoBack() {
-        return false;
+        checkThread();
+        return mProvider.canGoBack();
     }
 
+    /**
+     * Goes back in the history of this WebView.
+     */
     public void goBack() {
+        checkThread();
+        mProvider.goBack();
     }
 
+    /**
+     * Gets whether this WebView has a forward history item.
+     *
+     * @return {@code true} if this WebView has a forward history item
+     */
     public boolean canGoForward() {
-        return false;
+        checkThread();
+        return mProvider.canGoForward();
     }
 
+    /**
+     * Goes forward in the history of this WebView.
+     */
     public void goForward() {
+        checkThread();
+        mProvider.goForward();
     }
 
+    /**
+     * Gets whether the page can go back or forward the given
+     * number of steps.
+     *
+     * @param steps the negative or positive number of steps to move the
+     *              history
+     */
     public boolean canGoBackOrForward(int steps) {
-        return false;
+        checkThread();
+        return mProvider.canGoBackOrForward(steps);
     }
 
+    /**
+     * Goes to the history item that is the number of steps away from
+     * the current item. Steps is negative if backward and positive
+     * if forward.
+     *
+     * @param steps the number of steps to take back or forward in the back
+     *              forward list
+     */
     public void goBackOrForward(int steps) {
+        checkThread();
+        mProvider.goBackOrForward(steps);
     }
 
+    /**
+     * Gets whether private browsing is enabled in this WebView.
+     */
+    public boolean isPrivateBrowsingEnabled() {
+        checkThread();
+        return mProvider.isPrivateBrowsingEnabled();
+    }
+
+    /**
+     * Scrolls the contents of this WebView up by half the view size.
+     *
+     * @param top {@code true} to jump to the top of the page
+     * @return {@code true} if the page was scrolled
+     */
     public boolean pageUp(boolean top) {
-        return false;
+        checkThread();
+        return mProvider.pageUp(top);
     }
-    
+
+    /**
+     * Scrolls the contents of this WebView down by half the page size.
+     *
+     * @param bottom {@code true} to jump to bottom of page
+     * @return {@code true} if the page was scrolled
+     */
     public boolean pageDown(boolean bottom) {
-        return false;
+        checkThread();
+        return mProvider.pageDown(bottom);
     }
 
+    /**
+     * Posts a {@link VisualStateCallback}, which will be called when
+     * the current state of the WebView is ready to be drawn.
+     *
+     * <p>Because updates to the DOM are processed asynchronously, updates to the DOM may not
+     * immediately be reflected visually by subsequent {@link WebView#onDraw} invocations. The
+     * {@link VisualStateCallback} provides a mechanism to notify the caller when the contents of
+     * the DOM at the current time are ready to be drawn the next time the {@link WebView}
+     * draws.
+     *
+     * <p>The next draw after the callback completes is guaranteed to reflect all the updates to the
+     * DOM up to the point at which the {@link VisualStateCallback} was posted, but it may also
+     * contain updates applied after the callback was posted.
+     *
+     * <p>The state of the DOM covered by this API includes the following:
+     * <ul>
+     * <li>primitive HTML elements (div, img, span, etc..)</li>
+     * <li>images</li>
+     * <li>CSS animations</li>
+     * <li>WebGL</li>
+     * <li>canvas</li>
+     * </ul>
+     * It does not include the state of:
+     * <ul>
+     * <li>the video tag</li>
+     * </ul>
+     *
+     * <p>To guarantee that the {@link WebView} will successfully render the first frame
+     * after the {@link VisualStateCallback#onComplete} method has been called a set of conditions
+     * must be met:
+     * <ul>
+     * <li>If the {@link WebView}'s visibility is set to {@link View#VISIBLE VISIBLE} then
+     * the {@link WebView} must be attached to the view hierarchy.</li>
+     * <li>If the {@link WebView}'s visibility is set to {@link View#INVISIBLE INVISIBLE}
+     * then the {@link WebView} must be attached to the view hierarchy and must be made
+     * {@link View#VISIBLE VISIBLE} from the {@link VisualStateCallback#onComplete} method.</li>
+     * <li>If the {@link WebView}'s visibility is set to {@link View#GONE GONE} then the
+     * {@link WebView} must be attached to the view hierarchy and its
+     * {@link AbsoluteLayout.LayoutParams LayoutParams}'s width and height need to be set to fixed
+     * values and must be made {@link View#VISIBLE VISIBLE} from the
+     * {@link VisualStateCallback#onComplete} method.</li>
+     * </ul>
+     *
+     * <p>When using this API it is also recommended to enable pre-rasterization if the {@link
+     * WebView} is off screen to avoid flickering. See {@link WebSettings#setOffscreenPreRaster} for
+     * more details and do consider its caveats.
+     *
+     * @param requestId An id that will be returned in the callback to allow callers to match
+     *                  requests with callbacks.
+     * @param callback  The callback to be invoked.
+     */
+    public void postVisualStateCallback(long requestId, VisualStateCallback callback) {
+        checkThread();
+        mProvider.insertVisualStateCallback(requestId, callback);
+    }
+
+    /**
+     * Clears this WebView so that onDraw() will draw nothing but white background,
+     * and onMeasure() will return 0 if MeasureSpec is not MeasureSpec.EXACTLY.
+     * @deprecated Use WebView.loadUrl("about:blank") to reliably reset the view state
+     *             and release page resources (including any running JavaScript).
+     */
+    @Deprecated
     public void clearView() {
+        checkThread();
+        mProvider.clearView();
     }
-    
+
+    /**
+     * Gets a new picture that captures the current contents of this WebView.
+     * The picture is of the entire document being displayed, and is not
+     * limited to the area currently displayed by this WebView. Also, the
+     * picture is a static copy and is unaffected by later changes to the
+     * content being displayed.
+     * <p>
+     * Note that due to internal changes, for API levels between
+     * {@link android.os.Build.VERSION_CODES#HONEYCOMB} and
+     * {@link android.os.Build.VERSION_CODES#ICE_CREAM_SANDWICH} inclusive, the
+     * picture does not include fixed position elements or scrollable divs.
+     * <p>
+     * Note that from {@link android.os.Build.VERSION_CODES#JELLY_BEAN_MR1} the returned picture
+     * should only be drawn into bitmap-backed Canvas - using any other type of Canvas will involve
+     * additional conversion at a cost in memory and performance. Also the
+     * {@link android.graphics.Picture#createFromStream} and
+     * {@link android.graphics.Picture#writeToStream} methods are not supported on the
+     * returned object.
+     *
+     * @deprecated Use {@link #onDraw} to obtain a bitmap snapshot of the WebView, or
+     * {@link #saveWebArchive} to save the content to a file.
+     *
+     * @return a picture that captures the current contents of this WebView
+     */
+    @Deprecated
     public Picture capturePicture() {
-        return null;
+        checkThread();
+        return mProvider.capturePicture();
     }
 
+    /**
+     * @deprecated Use {@link #createPrintDocumentAdapter(String)} which requires user
+     *             to provide a print document name.
+     */
+    @Deprecated
+    public PrintDocumentAdapter createPrintDocumentAdapter() {
+        checkThread();
+        return mProvider.createPrintDocumentAdapter("default");
+    }
+
+    /**
+     * Creates a PrintDocumentAdapter that provides the content of this WebView for printing.
+     *
+     * The adapter works by converting the WebView contents to a PDF stream. The WebView cannot
+     * be drawn during the conversion process - any such draws are undefined. It is recommended
+     * to use a dedicated off screen WebView for the printing. If necessary, an application may
+     * temporarily hide a visible WebView by using a custom PrintDocumentAdapter instance
+     * wrapped around the object returned and observing the onStart and onFinish methods. See
+     * {@link android.print.PrintDocumentAdapter} for more information.
+     *
+     * @param documentName  The user-facing name of the printed document. See
+     *                      {@link android.print.PrintDocumentInfo}
+     */
+    public PrintDocumentAdapter createPrintDocumentAdapter(String documentName) {
+        checkThread();
+        return mProvider.createPrintDocumentAdapter(documentName);
+    }
+
+    /**
+     * Gets the current scale of this WebView.
+     *
+     * @return the current scale
+     *
+     * @deprecated This method is prone to inaccuracy due to race conditions
+     * between the web rendering and UI threads; prefer
+     * {@link WebViewClient#onScaleChanged}.
+     */
+    @Deprecated
+    @ViewDebug.ExportedProperty(category = "webview")
     public float getScale() {
-        return 0;
+        checkThread();
+        return mProvider.getScale();
     }
 
+    /**
+     * Sets the initial scale for this WebView. 0 means default.
+     * The behavior for the default scale depends on the state of
+     * {@link WebSettings#getUseWideViewPort()} and
+     * {@link WebSettings#getLoadWithOverviewMode()}.
+     * If the content fits into the WebView control by width, then
+     * the zoom is set to 100%. For wide content, the behavior
+     * depends on the state of {@link WebSettings#getLoadWithOverviewMode()}.
+     * If its value is {@code true}, the content will be zoomed out to be fit
+     * by width into the WebView control, otherwise not.
+     *
+     * If initial scale is greater than 0, WebView starts with this value
+     * as initial scale.
+     * Please note that unlike the scale properties in the viewport meta tag,
+     * this method doesn't take the screen density into account.
+     *
+     * @param scaleInPercent the initial scale in percent
+     */
     public void setInitialScale(int scaleInPercent) {
+        checkThread();
+        mProvider.setInitialScale(scaleInPercent);
     }
 
+    /**
+     * Invokes the graphical zoom picker widget for this WebView. This will
+     * result in the zoom widget appearing on the screen to control the zoom
+     * level of this WebView.
+     */
     public void invokeZoomPicker() {
+        checkThread();
+        mProvider.invokeZoomPicker();
     }
 
-    public void requestFocusNodeHref(Message hrefMsg) {
+    /**
+     * Gets a HitTestResult based on the current cursor node. If a HTML::a
+     * tag is found and the anchor has a non-JavaScript URL, the HitTestResult
+     * type is set to SRC_ANCHOR_TYPE and the URL is set in the "extra" field.
+     * If the anchor does not have a URL or if it is a JavaScript URL, the type
+     * will be UNKNOWN_TYPE and the URL has to be retrieved through
+     * {@link #requestFocusNodeHref} asynchronously. If a HTML::img tag is
+     * found, the HitTestResult type is set to IMAGE_TYPE and the URL is set in
+     * the "extra" field. A type of
+     * SRC_IMAGE_ANCHOR_TYPE indicates an anchor with a URL that has an image as
+     * a child node. If a phone number is found, the HitTestResult type is set
+     * to PHONE_TYPE and the phone number is set in the "extra" field of
+     * HitTestResult. If a map address is found, the HitTestResult type is set
+     * to GEO_TYPE and the address is set in the "extra" field of HitTestResult.
+     * If an email address is found, the HitTestResult type is set to EMAIL_TYPE
+     * and the email is set in the "extra" field of HitTestResult. Otherwise,
+     * HitTestResult type is set to UNKNOWN_TYPE.
+     */
+    public HitTestResult getHitTestResult() {
+        checkThread();
+        return mProvider.getHitTestResult();
     }
 
+    /**
+     * Requests the anchor or image element URL at the last tapped point.
+     * If hrefMsg is {@code null}, this method returns immediately and does not
+     * dispatch hrefMsg to its target. If the tapped point hits an image,
+     * an anchor, or an image in an anchor, the message associates
+     * strings in named keys in its data. The value paired with the key
+     * may be an empty string.
+     *
+     * @param hrefMsg the message to be dispatched with the result of the
+     *                request. The message data contains three keys. "url"
+     *                returns the anchor's href attribute. "title" returns the
+     *                anchor's text. "src" returns the image's src attribute.
+     */
+    public void requestFocusNodeHref(@Nullable Message hrefMsg) {
+        checkThread();
+        mProvider.requestFocusNodeHref(hrefMsg);
+    }
+
+    /**
+     * Requests the URL of the image last touched by the user. msg will be sent
+     * to its target with a String representing the URL as its object.
+     *
+     * @param msg the message to be dispatched with the result of the request
+     *            as the data member with "url" as key. The result can be {@code null}.
+     */
     public void requestImageRef(Message msg) {
+        checkThread();
+        mProvider.requestImageRef(msg);
     }
 
+    /**
+     * Gets the URL for the current page. This is not always the same as the URL
+     * passed to WebViewClient.onPageStarted because although the load for
+     * that URL has begun, the current page may not have changed.
+     *
+     * @return the URL for the current page
+     */
+    @ViewDebug.ExportedProperty(category = "webview")
     public String getUrl() {
-        return null;
+        checkThread();
+        return mProvider.getUrl();
     }
 
+    /**
+     * Gets the original URL for the current page. This is not always the same
+     * as the URL passed to WebViewClient.onPageStarted because although the
+     * load for that URL has begun, the current page may not have changed.
+     * Also, there may have been redirects resulting in a different URL to that
+     * originally requested.
+     *
+     * @return the URL that was originally requested for the current page
+     */
+    @ViewDebug.ExportedProperty(category = "webview")
+    public String getOriginalUrl() {
+        checkThread();
+        return mProvider.getOriginalUrl();
+    }
+
+    /**
+     * Gets the title for the current page. This is the title of the current page
+     * until WebViewClient.onReceivedTitle is called.
+     *
+     * @return the title for the current page
+     */
+    @ViewDebug.ExportedProperty(category = "webview")
     public String getTitle() {
-        return null;
+        checkThread();
+        return mProvider.getTitle();
     }
 
+    /**
+     * Gets the favicon for the current page. This is the favicon of the current
+     * page until WebViewClient.onReceivedIcon is called.
+     *
+     * @return the favicon for the current page
+     */
     public Bitmap getFavicon() {
-        return null;
+        checkThread();
+        return mProvider.getFavicon();
     }
 
+    /**
+     * Gets the touch icon URL for the apple-touch-icon <link> element, or
+     * a URL on this site's server pointing to the standard location of a
+     * touch icon.
+     *
+     * @hide
+     */
+    public String getTouchIconUrl() {
+        return mProvider.getTouchIconUrl();
+    }
+
+    /**
+     * Gets the progress for the current page.
+     *
+     * @return the progress for the current page between 0 and 100
+     */
     public int getProgress() {
-        return 0;
+        checkThread();
+        return mProvider.getProgress();
     }
-    
+
+    /**
+     * Gets the height of the HTML content.
+     *
+     * @return the height of the HTML content
+     */
+    @ViewDebug.ExportedProperty(category = "webview")
     public int getContentHeight() {
-        return 0;
+        checkThread();
+        return mProvider.getContentHeight();
     }
 
+    /**
+     * Gets the width of the HTML content.
+     *
+     * @return the width of the HTML content
+     * @hide
+     */
+    @ViewDebug.ExportedProperty(category = "webview")
+    public int getContentWidth() {
+        return mProvider.getContentWidth();
+    }
+
+    /**
+     * Pauses all layout, parsing, and JavaScript timers for all WebViews. This
+     * is a global requests, not restricted to just this WebView. This can be
+     * useful if the application has been paused.
+     */
     public void pauseTimers() {
+        checkThread();
+        mProvider.pauseTimers();
     }
 
+    /**
+     * Resumes all layout, parsing, and JavaScript timers for all WebViews.
+     * This will resume dispatching all timers.
+     */
     public void resumeTimers() {
+        checkThread();
+        mProvider.resumeTimers();
     }
 
-    public void clearCache() {
+    /**
+     * Does a best-effort attempt to pause any processing that can be paused
+     * safely, such as animations and geolocation. Note that this call
+     * does not pause JavaScript. To pause JavaScript globally, use
+     * {@link #pauseTimers}.
+     *
+     * To resume WebView, call {@link #onResume}.
+     */
+    public void onPause() {
+        checkThread();
+        mProvider.onPause();
     }
 
+    /**
+     * Resumes a WebView after a previous call to {@link #onPause}.
+     */
+    public void onResume() {
+        checkThread();
+        mProvider.onResume();
+    }
+
+    /**
+     * Gets whether this WebView is paused, meaning onPause() was called.
+     * Calling onResume() sets the paused state back to {@code false}.
+     *
+     * @hide
+     */
+    public boolean isPaused() {
+        return mProvider.isPaused();
+    }
+
+    /**
+     * Informs this WebView that memory is low so that it can free any available
+     * memory.
+     * @deprecated Memory caches are automatically dropped when no longer needed, and in response
+     *             to system memory pressure.
+     */
+    @Deprecated
+    public void freeMemory() {
+        checkThread();
+        mProvider.freeMemory();
+    }
+
+    /**
+     * Clears the resource cache. Note that the cache is per-application, so
+     * this will clear the cache for all WebViews used.
+     *
+     * @param includeDiskFiles if {@code false}, only the RAM cache is cleared
+     */
+    public void clearCache(boolean includeDiskFiles) {
+        checkThread();
+        mProvider.clearCache(includeDiskFiles);
+    }
+
+    /**
+     * Removes the autocomplete popup from the currently focused form field, if
+     * present. Note this only affects the display of the autocomplete popup,
+     * it does not remove any saved form data from this WebView's store. To do
+     * that, use {@link WebViewDatabase#clearFormData}.
+     */
     public void clearFormData() {
+        checkThread();
+        mProvider.clearFormData();
     }
 
+    /**
+     * Tells this WebView to clear its internal back/forward list.
+     */
     public void clearHistory() {
+        checkThread();
+        mProvider.clearHistory();
     }
 
+    /**
+     * Clears the SSL preferences table stored in response to proceeding with
+     * SSL certificate errors.
+     */
     public void clearSslPreferences() {
+        checkThread();
+        mProvider.clearSslPreferences();
     }
 
+    /**
+     * Clears the client certificate preferences stored in response
+     * to proceeding/cancelling client cert requests. Note that WebView
+     * automatically clears these preferences when it receives a
+     * {@link KeyChain#ACTION_STORAGE_CHANGED} intent. The preferences are
+     * shared by all the WebViews that are created by the embedder application.
+     *
+     * @param onCleared  A runnable to be invoked when client certs are cleared.
+     *                   The runnable will be called in UI thread.
+     */
+    public static void clearClientCertPreferences(@Nullable Runnable onCleared) {
+        getFactory().getStatics().clearClientCertPreferences(onCleared);
+    }
+
+    /**
+     * Starts Safe Browsing initialization.
+     * <p>
+     * URL loads are not guaranteed to be protected by Safe Browsing until after {@code callback} is
+     * invoked with {@code true}. Safe Browsing is not fully supported on all devices. For those
+     * devices {@code callback} will receive {@code false}.
+     * <p>
+     * This should not be called if Safe Browsing has been disabled by manifest tag or {@link
+     * WebSettings#setSafeBrowsingEnabled}. This prepares resources used for Safe Browsing.
+     * <p>
+     * This should be called with the Application Context (and will always use the Application
+     * context to do its work regardless).
+     *
+     * @param context Application Context.
+     * @param callback will be called on the UI thread with {@code true} if initialization is
+     * successful, {@code false} otherwise.
+     */
+    public static void startSafeBrowsing(@NonNull Context context,
+            @Nullable ValueCallback<Boolean> callback) {
+        getFactory().getStatics().initSafeBrowsing(context, callback);
+    }
+
+    /**
+     * Sets the list of hosts (domain names/IP addresses) that are exempt from SafeBrowsing checks.
+     * The list is global for all the WebViews.
+     * <p>
+     * Each rule should take one of these:
+     * <table>
+     * <tr><th> Rule </th> <th> Example </th> <th> Matches Subdomain</th> </tr>
+     * <tr><td> HOSTNAME </td> <td> example.com </td> <td> Yes </td> </tr>
+     * <tr><td> .HOSTNAME </td> <td> .example.com </td> <td> No </td> </tr>
+     * <tr><td> IPV4_LITERAL </td> <td> 192.168.1.1 </td> <td> No </td></tr>
+     * <tr><td> IPV6_LITERAL_WITH_BRACKETS </td><td>[10:20:30:40:50:60:70:80]</td><td>No</td></tr>
+     * </table>
+     * <p>
+     * All other rules, including wildcards, are invalid.
+     * <p>
+     * The correct syntax for hosts is defined by <a
+     * href="https://tools.ietf.org/html/rfc3986#section-3.2.2">RFC 3986</a>.
+     *
+     * @param hosts the list of hosts
+     * @param callback will be called with {@code true} if hosts are successfully added to the
+     * whitelist. It will be called with {@code false} if any hosts are malformed. The callback
+     * will be run on the UI thread
+     */
+    public static void setSafeBrowsingWhitelist(@NonNull List<String> hosts,
+            @Nullable ValueCallback<Boolean> callback) {
+        getFactory().getStatics().setSafeBrowsingWhitelist(hosts, callback);
+    }
+
+    /**
+     * Returns a URL pointing to the privacy policy for Safe Browsing reporting.
+     *
+     * @return the url pointing to a privacy policy document which can be displayed to users.
+     */
+    @NonNull
+    public static Uri getSafeBrowsingPrivacyPolicyUrl() {
+        return getFactory().getStatics().getSafeBrowsingPrivacyPolicyUrl();
+    }
+
+    /**
+     * Gets the WebBackForwardList for this WebView. This contains the
+     * back/forward list for use in querying each item in the history stack.
+     * This is a copy of the private WebBackForwardList so it contains only a
+     * snapshot of the current state. Multiple calls to this method may return
+     * different objects. The object returned from this method will not be
+     * updated to reflect any new state.
+     */
+    public WebBackForwardList copyBackForwardList() {
+        checkThread();
+        return mProvider.copyBackForwardList();
+
+    }
+
+    /**
+     * Registers the listener to be notified as find-on-page operations
+     * progress. This will replace the current listener.
+     *
+     * @param listener an implementation of {@link FindListener}
+     */
+    public void setFindListener(FindListener listener) {
+        checkThread();
+        setupFindListenerIfNeeded();
+        mFindListener.mUserFindListener = listener;
+    }
+
+    /**
+     * Highlights and scrolls to the next match found by
+     * {@link #findAllAsync}, wrapping around page boundaries as necessary.
+     * Notifies any registered {@link FindListener}. If {@link #findAllAsync(String)}
+     * has not been called yet, or if {@link #clearMatches} has been called since the
+     * last find operation, this function does nothing.
+     *
+     * @param forward the direction to search
+     * @see #setFindListener
+     */
+    public void findNext(boolean forward) {
+        checkThread();
+        mProvider.findNext(forward);
+    }
+
+    /**
+     * Finds all instances of find on the page and highlights them.
+     * Notifies any registered {@link FindListener}.
+     *
+     * @param find the string to find
+     * @return the number of occurrences of the String "find" that were found
+     * @deprecated {@link #findAllAsync} is preferred.
+     * @see #setFindListener
+     */
+    @Deprecated
+    public int findAll(String find) {
+        checkThread();
+        StrictMode.noteSlowCall("findAll blocks UI: prefer findAllAsync");
+        return mProvider.findAll(find);
+    }
+
+    /**
+     * Finds all instances of find on the page and highlights them,
+     * asynchronously. Notifies any registered {@link FindListener}.
+     * Successive calls to this will cancel any pending searches.
+     *
+     * @param find the string to find.
+     * @see #setFindListener
+     */
+    public void findAllAsync(String find) {
+        checkThread();
+        mProvider.findAllAsync(find);
+    }
+
+    /**
+     * Starts an ActionMode for finding text in this WebView.  Only works if this
+     * WebView is attached to the view system.
+     *
+     * @param text if non-null, will be the initial text to search for.
+     *             Otherwise, the last String searched for in this WebView will
+     *             be used to start.
+     * @param showIme if {@code true}, show the IME, assuming the user will begin typing.
+     *                If {@code false} and text is non-null, perform a find all.
+     * @return {@code true} if the find dialog is shown, {@code false} otherwise
+     * @deprecated This method does not work reliably on all Android versions;
+     *             implementing a custom find dialog using WebView.findAllAsync()
+     *             provides a more robust solution.
+     */
+    @Deprecated
+    public boolean showFindDialog(@Nullable String text, boolean showIme) {
+        checkThread();
+        return mProvider.showFindDialog(text, showIme);
+    }
+
+    /**
+     * Gets the first substring consisting of the address of a physical
+     * location. Currently, only addresses in the United States are detected,
+     * and consist of:
+     * <ul>
+     *   <li>a house number</li>
+     *   <li>a street name</li>
+     *   <li>a street type (Road, Circle, etc), either spelled out or
+     *       abbreviated</li>
+     *   <li>a city name</li>
+     *   <li>a state or territory, either spelled out or two-letter abbr</li>
+     *   <li>an optional 5 digit or 9 digit zip code</li>
+     * </ul>
+     * All names must be correctly capitalized, and the zip code, if present,
+     * must be valid for the state. The street type must be a standard USPS
+     * spelling or abbreviation. The state or territory must also be spelled
+     * or abbreviated using USPS standards. The house number may not exceed
+     * five digits.
+     *
+     * @param addr the string to search for addresses
+     * @return the address, or if no address is found, {@code null}
+     * @deprecated this method is superseded by {@link TextClassifier#generateLinks(
+     * android.view.textclassifier.TextLinks.Request)}. Avoid using this method even when targeting
+     * API levels where no alternative is available.
+     */
+    @Nullable
+    @Deprecated
     public static String findAddress(String addr) {
-        return null;
+        if (addr == null) {
+            throw new NullPointerException("addr is null");
+        }
+        return FindAddress.findAddress(addr);
     }
 
+    /**
+     * For apps targeting the L release, WebView has a new default behavior that reduces
+     * memory footprint and increases performance by intelligently choosing
+     * the portion of the HTML document that needs to be drawn. These
+     * optimizations are transparent to the developers. However, under certain
+     * circumstances, an App developer may want to disable them:
+     * <ol>
+     *   <li>When an app uses {@link #onDraw} to do own drawing and accesses portions
+     *       of the page that is way outside the visible portion of the page.</li>
+     *   <li>When an app uses {@link #capturePicture} to capture a very large HTML document.
+     *       Note that capturePicture is a deprecated API.</li>
+     * </ol>
+     * Enabling drawing the entire HTML document has a significant performance
+     * cost. This method should be called before any WebViews are created.
+     */
+    public static void enableSlowWholeDocumentDraw() {
+        getFactory().getStatics().enableSlowWholeDocumentDraw();
+    }
+
+    /**
+     * Clears the highlighting surrounding text matches created by
+     * {@link #findAllAsync}.
+     */
+    public void clearMatches() {
+        checkThread();
+        mProvider.clearMatches();
+    }
+
+    /**
+     * Queries the document to see if it contains any image references. The
+     * message object will be dispatched with arg1 being set to 1 if images
+     * were found and 0 if the document does not reference any images.
+     *
+     * @param response the message that will be dispatched with the result
+     */
     public void documentHasImages(Message response) {
+        checkThread();
+        mProvider.documentHasImages(response);
     }
 
+    /**
+     * Sets the WebViewClient that will receive various notifications and
+     * requests. This will replace the current handler.
+     *
+     * @param client an implementation of WebViewClient
+     * @see #getWebViewClient
+     */
     public void setWebViewClient(WebViewClient client) {
+        checkThread();
+        mProvider.setWebViewClient(client);
     }
 
+    /**
+     * Gets the WebViewClient.
+     *
+     * @return the WebViewClient, or a default client if not yet set
+     * @see #setWebViewClient
+     */
+    public WebViewClient getWebViewClient() {
+        checkThread();
+        return mProvider.getWebViewClient();
+    }
+
+    /**
+     * Registers the interface to be used when content can not be handled by
+     * the rendering engine, and should be downloaded instead. This will replace
+     * the current handler.
+     *
+     * @param listener an implementation of DownloadListener
+     */
     public void setDownloadListener(DownloadListener listener) {
+        checkThread();
+        mProvider.setDownloadListener(listener);
     }
 
+    /**
+     * Sets the chrome handler. This is an implementation of WebChromeClient for
+     * use in handling JavaScript dialogs, favicons, titles, and the progress.
+     * This will replace the current handler.
+     *
+     * @param client an implementation of WebChromeClient
+     * @see #getWebChromeClient
+     */
     public void setWebChromeClient(WebChromeClient client) {
+        checkThread();
+        mProvider.setWebChromeClient(client);
     }
 
-    public void addJavascriptInterface(Object obj, String interfaceName) {
+    /**
+     * Gets the chrome handler.
+     *
+     * @return the WebChromeClient, or {@code null} if not yet set
+     * @see #setWebChromeClient
+     */
+    @Nullable
+    public WebChromeClient getWebChromeClient() {
+        checkThread();
+        return mProvider.getWebChromeClient();
     }
 
+    /**
+     * Sets the Picture listener. This is an interface used to receive
+     * notifications of a new Picture.
+     *
+     * @param listener an implementation of WebView.PictureListener
+     * @deprecated This method is now obsolete.
+     */
+    @Deprecated
+    public void setPictureListener(PictureListener listener) {
+        checkThread();
+        mProvider.setPictureListener(listener);
+    }
+
+    /**
+     * Injects the supplied Java object into this WebView. The object is
+     * injected into the JavaScript context of the main frame, using the
+     * supplied name. This allows the Java object's methods to be
+     * accessed from JavaScript. For applications targeted to API
+     * level {@link android.os.Build.VERSION_CODES#JELLY_BEAN_MR1}
+     * and above, only public methods that are annotated with
+     * {@link android.webkit.JavascriptInterface} can be accessed from JavaScript.
+     * For applications targeted to API level {@link android.os.Build.VERSION_CODES#JELLY_BEAN} or below,
+     * all public methods (including the inherited ones) can be accessed, see the
+     * important security note below for implications.
+     * <p> Note that injected objects will not appear in JavaScript until the page is next
+     * (re)loaded. JavaScript should be enabled before injecting the object. For example:
+     * <pre>
+     * class JsObject {
+     *    {@literal @}JavascriptInterface
+     *    public String toString() { return "injectedObject"; }
+     * }
+     * webview.getSettings().setJavaScriptEnabled(true);
+     * webView.addJavascriptInterface(new JsObject(), "injectedObject");
+     * webView.loadData("<!DOCTYPE html><title></title>", "text/html", null);
+     * webView.loadUrl("javascript:alert(injectedObject.toString())");</pre>
+     * <p>
+     * <strong>IMPORTANT:</strong>
+     * <ul>
+     * <li> This method can be used to allow JavaScript to control the host
+     * application. This is a powerful feature, but also presents a security
+     * risk for apps targeting {@link android.os.Build.VERSION_CODES#JELLY_BEAN} or earlier.
+     * Apps that target a version later than {@link android.os.Build.VERSION_CODES#JELLY_BEAN}
+     * are still vulnerable if the app runs on a device running Android earlier than 4.2.
+     * The most secure way to use this method is to target {@link android.os.Build.VERSION_CODES#JELLY_BEAN_MR1}
+     * and to ensure the method is called only when running on Android 4.2 or later.
+     * With these older versions, JavaScript could use reflection to access an
+     * injected object's public fields. Use of this method in a WebView
+     * containing untrusted content could allow an attacker to manipulate the
+     * host application in unintended ways, executing Java code with the
+     * permissions of the host application. Use extreme care when using this
+     * method in a WebView which could contain untrusted content.</li>
+     * <li> JavaScript interacts with Java object on a private, background
+     * thread of this WebView. Care is therefore required to maintain thread
+     * safety.
+     * </li>
+     * <li> The Java object's fields are not accessible.</li>
+     * <li> For applications targeted to API level {@link android.os.Build.VERSION_CODES#LOLLIPOP}
+     * and above, methods of injected Java objects are enumerable from
+     * JavaScript.</li>
+     * </ul>
+     *
+     * @param object the Java object to inject into this WebView's JavaScript
+     *               context. {@code null} values are ignored.
+     * @param name the name used to expose the object in JavaScript
+     */
+    public void addJavascriptInterface(Object object, String name) {
+        checkThread();
+        mProvider.addJavascriptInterface(object, name);
+    }
+
+    /**
+     * Removes a previously injected Java object from this WebView. Note that
+     * the removal will not be reflected in JavaScript until the page is next
+     * (re)loaded. See {@link #addJavascriptInterface}.
+     *
+     * @param name the name used to expose the object in JavaScript
+     */
+    public void removeJavascriptInterface(@NonNull String name) {
+        checkThread();
+        mProvider.removeJavascriptInterface(name);
+    }
+
+    /**
+     * Creates a message channel to communicate with JS and returns the message
+     * ports that represent the endpoints of this message channel. The HTML5 message
+     * channel functionality is described
+     * <a href="https://html.spec.whatwg.org/multipage/comms.html#messagechannel">here
+     * </a>
+     *
+     * <p>The returned message channels are entangled and already in started state.
+     *
+     * @return the two message ports that form the message channel.
+     */
+    public WebMessagePort[] createWebMessageChannel() {
+        checkThread();
+        return mProvider.createWebMessageChannel();
+    }
+
+    /**
+     * Post a message to main frame. The embedded application can restrict the
+     * messages to a certain target origin. See
+     * <a href="https://html.spec.whatwg.org/multipage/comms.html#posting-messages">
+     * HTML5 spec</a> for how target origin can be used.
+     * <p>
+     * A target origin can be set as a wildcard ("*"). However this is not recommended.
+     * See the page above for security issues.
+     *
+     * @param message the WebMessage
+     * @param targetOrigin the target origin.
+     */
+    public void postWebMessage(WebMessage message, Uri targetOrigin) {
+        checkThread();
+        mProvider.postMessageToMainFrame(message, targetOrigin);
+    }
+
+    /**
+     * Gets the WebSettings object used to control the settings for this
+     * WebView.
+     *
+     * @return a WebSettings object that can be used to control this WebView's
+     *         settings
+     */
+    public WebSettings getSettings() {
+        checkThread();
+        return mProvider.getSettings();
+    }
+
+    /**
+     * Enables debugging of web contents (HTML / CSS / JavaScript)
+     * loaded into any WebViews of this application. This flag can be enabled
+     * in order to facilitate debugging of web layouts and JavaScript
+     * code running inside WebViews. Please refer to WebView documentation
+     * for the debugging guide.
+     *
+     * The default is {@code false}.
+     *
+     * @param enabled whether to enable web contents debugging
+     */
+    public static void setWebContentsDebuggingEnabled(boolean enabled) {
+        getFactory().getStatics().setWebContentsDebuggingEnabled(enabled);
+    }
+
+    /**
+     * Gets the list of currently loaded plugins.
+     *
+     * @return the list of currently loaded plugins
+     * @deprecated This was used for Gears, which has been deprecated.
+     * @hide
+     */
+    @Deprecated
+    public static synchronized PluginList getPluginList() {
+        return new PluginList();
+    }
+
+    /**
+     * Define the directory used to store WebView data for the current process.
+     * The provided suffix will be used when constructing data and cache
+     * directory paths. If this API is not called, no suffix will be used.
+     * Each directory can be used by only one process in the application. If more
+     * than one process in an app wishes to use WebView, only one process can use
+     * the default directory, and other processes must call this API to define
+     * a unique suffix.
+     * <p>
+     * This means that different processes in the same application cannot directly
+     * share WebView-related data, since the data directories must be distinct.
+     * Applications that use this API may have to explicitly pass data between
+     * processes. For example, login cookies may have to be copied from one
+     * process's cookie jar to the other using {@link CookieManager} if both
+     * processes' WebViews are intended to be logged in.
+     * <p>
+     * Most applications should simply ensure that all components of the app
+     * that rely on WebView are in the same process, to avoid needing multiple
+     * data directories. The {@link #disableWebView} method can be used to ensure
+     * that the other processes do not use WebView by accident in this case.
+     * <p>
+     * This API must be called before any instances of WebView are created in
+     * this process and before any other methods in the android.webkit package
+     * are called by this process.
+     *
+     * @param suffix The directory name suffix to be used for the current
+     *               process. Must not contain a path separator.
+     * @throws IllegalStateException if WebView has already been initialized
+     *                               in the current process.
+     * @throws IllegalArgumentException if the suffix contains a path separator.
+     */
+    public static void setDataDirectorySuffix(String suffix) {
+        WebViewFactory.setDataDirectorySuffix(suffix);
+    }
+
+    /**
+     * Indicate that the current process does not intend to use WebView, and
+     * that an exception should be thrown if a WebView is created or any other
+     * methods in the android.webkit package are used.
+     * <p>
+     * Applications with multiple processes may wish to call this in processes
+     * that are not intended to use WebView to avoid accidentally incurring
+     * the memory usage of initializing WebView in long-lived processes that
+     * have no need for it, and to prevent potential data directory conflicts
+     * (see {@link #setDataDirectorySuffix}).
+     * <p>
+     * For example, an audio player application with one process for its
+     * activities and another process for its playback service may wish to call
+     * this method in the playback service's {@link android.app.Service#onCreate}.
+     *
+     * @throws IllegalStateException if WebView has already been initialized
+     *                               in the current process.
+     */
+    public static void disableWebView() {
+        WebViewFactory.disableWebView();
+    }
+
+
+    /**
+     * @deprecated This was used for Gears, which has been deprecated.
+     * @hide
+     */
+    @Deprecated
+    public void refreshPlugins(boolean reloadOpenPages) {
+        checkThread();
+    }
+
+    /**
+     * Puts this WebView into text selection mode. Do not rely on this
+     * functionality; it will be deprecated in the future.
+     *
+     * @deprecated This method is now obsolete.
+     * @hide Since API level {@link android.os.Build.VERSION_CODES#JELLY_BEAN_MR1}
+     */
+    @Deprecated
+    public void emulateShiftHeld() {
+        checkThread();
+    }
+
+    /**
+     * @deprecated WebView no longer needs to implement
+     * ViewGroup.OnHierarchyChangeListener.  This method does nothing now.
+     */
+    @Override
+    // Cannot add @hide as this can always be accessed via the interface.
+    @Deprecated
+    public void onChildViewAdded(View parent, View child) {}
+
+    /**
+     * @deprecated WebView no longer needs to implement
+     * ViewGroup.OnHierarchyChangeListener.  This method does nothing now.
+     */
+    @Override
+    // Cannot add @hide as this can always be accessed via the interface.
+    @Deprecated
+    public void onChildViewRemoved(View p, View child) {}
+
+    /**
+     * @deprecated WebView should not have implemented
+     * ViewTreeObserver.OnGlobalFocusChangeListener. This method does nothing now.
+     */
+    @Override
+    // Cannot add @hide as this can always be accessed via the interface.
+    @Deprecated
+    public void onGlobalFocusChanged(View oldFocus, View newFocus) {
+    }
+
+    /**
+     * @deprecated Only the default case, {@code true}, will be supported in a future version.
+     */
+    @Deprecated
+    public void setMapTrackballToArrowKeys(boolean setMap) {
+        checkThread();
+        mProvider.setMapTrackballToArrowKeys(setMap);
+    }
+
+
+    public void flingScroll(int vx, int vy) {
+        checkThread();
+        mProvider.flingScroll(vx, vy);
+    }
+
+    /**
+     * Gets the zoom controls for this WebView, as a separate View. The caller
+     * is responsible for inserting this View into the layout hierarchy.
+     * <p/>
+     * API level {@link android.os.Build.VERSION_CODES#CUPCAKE} introduced
+     * built-in zoom mechanisms for the WebView, as opposed to these separate
+     * zoom controls. The built-in mechanisms are preferred and can be enabled
+     * using {@link WebSettings#setBuiltInZoomControls}.
+     *
+     * @deprecated the built-in zoom mechanisms are preferred
+     * @hide Since API level {@link android.os.Build.VERSION_CODES#JELLY_BEAN}
+     */
+    @Deprecated
     public View getZoomControls() {
-        return null;
+        checkThread();
+        return mProvider.getZoomControls();
     }
 
+    /**
+     * Gets whether this WebView can be zoomed in.
+     *
+     * @return {@code true} if this WebView can be zoomed in
+     *
+     * @deprecated This method is prone to inaccuracy due to race conditions
+     * between the web rendering and UI threads; prefer
+     * {@link WebViewClient#onScaleChanged}.
+     */
+    @Deprecated
+    public boolean canZoomIn() {
+        checkThread();
+        return mProvider.canZoomIn();
+    }
+
+    /**
+     * Gets whether this WebView can be zoomed out.
+     *
+     * @return {@code true} if this WebView can be zoomed out
+     *
+     * @deprecated This method is prone to inaccuracy due to race conditions
+     * between the web rendering and UI threads; prefer
+     * {@link WebViewClient#onScaleChanged}.
+     */
+    @Deprecated
+    public boolean canZoomOut() {
+        checkThread();
+        return mProvider.canZoomOut();
+    }
+
+    /**
+     * Performs a zoom operation in this WebView.
+     *
+     * @param zoomFactor the zoom factor to apply. The zoom factor will be clamped to the WebView's
+     * zoom limits. This value must be in the range 0.01 to 100.0 inclusive.
+     */
+    public void zoomBy(float zoomFactor) {
+        checkThread();
+        if (zoomFactor < 0.01)
+            throw new IllegalArgumentException("zoomFactor must be greater than 0.01.");
+        if (zoomFactor > 100.0)
+            throw new IllegalArgumentException("zoomFactor must be less than 100.");
+        mProvider.zoomBy(zoomFactor);
+    }
+
+    /**
+     * Performs zoom in in this WebView.
+     *
+     * @return {@code true} if zoom in succeeds, {@code false} if no zoom changes
+     */
     public boolean zoomIn() {
-        return false;
+        checkThread();
+        return mProvider.zoomIn();
     }
 
+    /**
+     * Performs zoom out in this WebView.
+     *
+     * @return {@code true} if zoom out succeeds, {@code false} if no zoom changes
+     */
     public boolean zoomOut() {
-        return false;
+        checkThread();
+        return mProvider.zoomOut();
+    }
+
+    /**
+     * @deprecated This method is now obsolete.
+     * @hide Since API level {@link android.os.Build.VERSION_CODES#JELLY_BEAN_MR1}
+     */
+    @Deprecated
+    public void debugDump() {
+        checkThread();
+    }
+
+    /**
+     * See {@link ViewDebug.HierarchyHandler#dumpViewHierarchyWithProperties(BufferedWriter, int)}
+     * @hide
+     */
+    @Override
+    public void dumpViewHierarchyWithProperties(BufferedWriter out, int level) {
+        mProvider.dumpViewHierarchyWithProperties(out, level);
+    }
+
+    /**
+     * See {@link ViewDebug.HierarchyHandler#findHierarchyView(String, int)}
+     * @hide
+     */
+    @Override
+    public View findHierarchyView(String className, int hashCode) {
+        return mProvider.findHierarchyView(className, hashCode);
+    }
+
+    /** @hide */
+    @IntDef(prefix = { "RENDERER_PRIORITY_" }, value = {
+            RENDERER_PRIORITY_WAIVED,
+            RENDERER_PRIORITY_BOUND,
+            RENDERER_PRIORITY_IMPORTANT
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface RendererPriority {}
+
+    /**
+     * The renderer associated with this WebView is bound with
+     * {@link Context#BIND_WAIVE_PRIORITY}. At this priority level
+     * {@link WebView} renderers will be strong targets for out of memory
+     * killing.
+     *
+     * Use with {@link #setRendererPriorityPolicy}.
+     */
+    public static final int RENDERER_PRIORITY_WAIVED = 0;
+    /**
+     * The renderer associated with this WebView is bound with
+     * the default priority for services.
+     *
+     * Use with {@link #setRendererPriorityPolicy}.
+     */
+    public static final int RENDERER_PRIORITY_BOUND = 1;
+    /**
+     * The renderer associated with this WebView is bound with
+     * {@link Context#BIND_IMPORTANT}.
+     *
+     * Use with {@link #setRendererPriorityPolicy}.
+     */
+    public static final int RENDERER_PRIORITY_IMPORTANT = 2;
+
+    /**
+     * Set the renderer priority policy for this {@link WebView}. The
+     * priority policy will be used to determine whether an out of
+     * process renderer should be considered to be a target for OOM
+     * killing.
+     *
+     * Because a renderer can be associated with more than one
+     * WebView, the final priority it is computed as the maximum of
+     * any attached WebViews. When a WebView is destroyed it will
+     * cease to be considerered when calculating the renderer
+     * priority. Once no WebViews remain associated with the renderer,
+     * the priority of the renderer will be reduced to
+     * {@link #RENDERER_PRIORITY_WAIVED}.
+     *
+     * The default policy is to set the priority to
+     * {@link #RENDERER_PRIORITY_IMPORTANT} regardless of visibility,
+     * and this should not be changed unless the caller also handles
+     * renderer crashes with
+     * {@link WebViewClient#onRenderProcessGone}. Any other setting
+     * will result in WebView renderers being killed by the system
+     * more aggressively than the application.
+     *
+     * @param rendererRequestedPriority the minimum priority at which
+     *        this WebView desires the renderer process to be bound.
+     * @param waivedWhenNotVisible if {@code true}, this flag specifies that
+     *        when this WebView is not visible, it will be treated as
+     *        if it had requested a priority of
+     *        {@link #RENDERER_PRIORITY_WAIVED}.
+     */
+    public void setRendererPriorityPolicy(
+            @RendererPriority int rendererRequestedPriority,
+            boolean waivedWhenNotVisible) {
+        mProvider.setRendererPriorityPolicy(rendererRequestedPriority, waivedWhenNotVisible);
+    }
+
+    /**
+     * Get the requested renderer priority for this WebView.
+     *
+     * @return the requested renderer priority policy.
+     */
+    @RendererPriority
+    public int getRendererRequestedPriority() {
+        return mProvider.getRendererRequestedPriority();
+    }
+
+    /**
+     * Return whether this WebView requests a priority of
+     * {@link #RENDERER_PRIORITY_WAIVED} when not visible.
+     *
+     * @return whether this WebView requests a priority of
+     * {@link #RENDERER_PRIORITY_WAIVED} when not visible.
+     */
+    public boolean getRendererPriorityWaivedWhenNotVisible() {
+        return mProvider.getRendererPriorityWaivedWhenNotVisible();
+    }
+
+    /**
+     * Sets the {@link TextClassifier} for this WebView.
+     */
+    public void setTextClassifier(@Nullable TextClassifier textClassifier) {
+        mProvider.setTextClassifier(textClassifier);
+    }
+
+    /**
+     * Returns the {@link TextClassifier} used by this WebView.
+     * If no TextClassifier has been set, this WebView uses the default set by the system.
+     */
+    @NonNull
+    public TextClassifier getTextClassifier() {
+        return mProvider.getTextClassifier();
+    }
+
+    /**
+     * Returns the {@link ClassLoader} used to load internal WebView classes.
+     * This method is meant for use by the WebView Support Library, there is no reason to use this
+     * method otherwise.
+     */
+    @NonNull
+    public static ClassLoader getWebViewClassLoader() {
+        return getFactory().getWebViewClassLoader();
+    }
+
+    /**
+     * Returns the {@link Looper} corresponding to the thread on which WebView calls must be made.
+     */
+    @NonNull
+    public Looper getWebViewLooper() {
+        return mWebViewThread;
+    }
+
+    //-------------------------------------------------------------------------
+    // Interface for WebView providers
+    //-------------------------------------------------------------------------
+
+    /**
+     * Gets the WebViewProvider. Used by providers to obtain the underlying
+     * implementation, e.g. when the application responds to
+     * WebViewClient.onCreateWindow() request.
+     *
+     * @hide WebViewProvider is not public API.
+     */
+    @SystemApi
+    public WebViewProvider getWebViewProvider() {
+        return mProvider;
+    }
+
+    /**
+     * Callback interface, allows the provider implementation to access non-public methods
+     * and fields, and make super-class calls in this WebView instance.
+     * @hide Only for use by WebViewProvider implementations
+     */
+    @SystemApi
+    public class PrivateAccess {
+        // ---- Access to super-class methods ----
+        public int super_getScrollBarStyle() {
+            return WebView.super.getScrollBarStyle();
+        }
+
+        public void super_scrollTo(int scrollX, int scrollY) {
+            WebView.super.scrollTo(scrollX, scrollY);
+        }
+
+        public void super_computeScroll() {
+            WebView.super.computeScroll();
+        }
+
+        public boolean super_onHoverEvent(MotionEvent event) {
+            return WebView.super.onHoverEvent(event);
+        }
+
+        public boolean super_performAccessibilityAction(int action, Bundle arguments) {
+            return WebView.super.performAccessibilityActionInternal(action, arguments);
+        }
+
+        public boolean super_performLongClick() {
+            return WebView.super.performLongClick();
+        }
+
+        public boolean super_setFrame(int left, int top, int right, int bottom) {
+            return WebView.super.setFrame(left, top, right, bottom);
+        }
+
+        public boolean super_dispatchKeyEvent(KeyEvent event) {
+            return WebView.super.dispatchKeyEvent(event);
+        }
+
+        public boolean super_onGenericMotionEvent(MotionEvent event) {
+            return WebView.super.onGenericMotionEvent(event);
+        }
+
+        public boolean super_requestFocus(int direction, Rect previouslyFocusedRect) {
+            return WebView.super.requestFocus(direction, previouslyFocusedRect);
+        }
+
+        public void super_setLayoutParams(ViewGroup.LayoutParams params) {
+            WebView.super.setLayoutParams(params);
+        }
+
+        public void super_startActivityForResult(Intent intent, int requestCode) {
+            WebView.super.startActivityForResult(intent, requestCode);
+        }
+
+        // ---- Access to non-public methods ----
+        public void overScrollBy(int deltaX, int deltaY,
+                int scrollX, int scrollY,
+                int scrollRangeX, int scrollRangeY,
+                int maxOverScrollX, int maxOverScrollY,
+                boolean isTouchEvent) {
+            WebView.this.overScrollBy(deltaX, deltaY, scrollX, scrollY, scrollRangeX, scrollRangeY,
+                    maxOverScrollX, maxOverScrollY, isTouchEvent);
+        }
+
+        public void awakenScrollBars(int duration) {
+            WebView.this.awakenScrollBars(duration);
+        }
+
+        public void awakenScrollBars(int duration, boolean invalidate) {
+            WebView.this.awakenScrollBars(duration, invalidate);
+        }
+
+        public float getVerticalScrollFactor() {
+            return WebView.this.getVerticalScrollFactor();
+        }
+
+        public float getHorizontalScrollFactor() {
+            return WebView.this.getHorizontalScrollFactor();
+        }
+
+        public void setMeasuredDimension(int measuredWidth, int measuredHeight) {
+            WebView.this.setMeasuredDimension(measuredWidth, measuredHeight);
+        }
+
+        public void onScrollChanged(int l, int t, int oldl, int oldt) {
+            WebView.this.onScrollChanged(l, t, oldl, oldt);
+        }
+
+        public int getHorizontalScrollbarHeight() {
+            return WebView.this.getHorizontalScrollbarHeight();
+        }
+
+        public void super_onDrawVerticalScrollBar(Canvas canvas, Drawable scrollBar,
+                int l, int t, int r, int b) {
+            WebView.super.onDrawVerticalScrollBar(canvas, scrollBar, l, t, r, b);
+        }
+
+        // ---- Access to (non-public) fields ----
+        /** Raw setter for the scroll X value, without invoking onScrollChanged handlers etc. */
+        public void setScrollXRaw(int scrollX) {
+            WebView.this.mScrollX = scrollX;
+        }
+
+        /** Raw setter for the scroll Y value, without invoking onScrollChanged handlers etc. */
+        public void setScrollYRaw(int scrollY) {
+            WebView.this.mScrollY = scrollY;
+        }
+
+    }
+
+    //-------------------------------------------------------------------------
+    // Package-private internal stuff
+    //-------------------------------------------------------------------------
+
+    // Only used by android.webkit.FindActionModeCallback.
+    void setFindDialogFindListener(FindListener listener) {
+        checkThread();
+        setupFindListenerIfNeeded();
+        mFindListener.mFindDialogFindListener = listener;
+    }
+
+    // Only used by android.webkit.FindActionModeCallback.
+    void notifyFindDialogDismissed() {
+        checkThread();
+        mProvider.notifyFindDialogDismissed();
+    }
+
+    //-------------------------------------------------------------------------
+    // Private internal stuff
+    //-------------------------------------------------------------------------
+
+    private WebViewProvider mProvider;
+
+    /**
+     * In addition to the FindListener that the user may set via the WebView.setFindListener
+     * API, FindActionModeCallback will register it's own FindListener. We keep them separate
+     * via this class so that the two FindListeners can potentially exist at once.
+     */
+    private class FindListenerDistributor implements FindListener {
+        private FindListener mFindDialogFindListener;
+        private FindListener mUserFindListener;
+
+        @Override
+        public void onFindResultReceived(int activeMatchOrdinal, int numberOfMatches,
+                boolean isDoneCounting) {
+            if (mFindDialogFindListener != null) {
+                mFindDialogFindListener.onFindResultReceived(activeMatchOrdinal, numberOfMatches,
+                        isDoneCounting);
+            }
+
+            if (mUserFindListener != null) {
+                mUserFindListener.onFindResultReceived(activeMatchOrdinal, numberOfMatches,
+                        isDoneCounting);
+            }
+        }
+    }
+    private FindListenerDistributor mFindListener;
+
+    private void setupFindListenerIfNeeded() {
+        if (mFindListener == null) {
+            mFindListener = new FindListenerDistributor();
+            mProvider.setFindListener(mFindListener);
+        }
+    }
+
+    private void ensureProviderCreated() {
+        checkThread();
+        if (mProvider == null) {
+            // As this can get called during the base class constructor chain, pass the minimum
+            // number of dependencies here; the rest are deferred to init().
+            mProvider = getFactory().createWebView(this, new PrivateAccess());
+        }
+    }
+
+    private static WebViewFactoryProvider getFactory() {
+        return WebViewFactory.getProvider();
+    }
+
+    private final Looper mWebViewThread = Looper.myLooper();
+
+    private void checkThread() {
+        // Ignore mWebViewThread == null because this can be called during in the super class
+        // constructor, before this class's own constructor has even started.
+        if (mWebViewThread != null && Looper.myLooper() != mWebViewThread) {
+            Throwable throwable = new Throwable(
+                    "A WebView method was called on thread '" +
+                    Thread.currentThread().getName() + "'. " +
+                    "All WebView methods must be called on the same thread. " +
+                    "(Expected Looper " + mWebViewThread + " called on " + Looper.myLooper() +
+                    ", FYI main Looper is " + Looper.getMainLooper() + ")");
+            Log.w(LOGTAG, Log.getStackTraceString(throwable));
+            StrictMode.onWebViewMethodCalledOnWrongThread(throwable);
+
+            if (sEnforceThreadChecking) {
+                throw new RuntimeException(throwable);
+            }
+        }
+    }
+
+    //-------------------------------------------------------------------------
+    // Override View methods
+    //-------------------------------------------------------------------------
+
+    // TODO: Add a test that enumerates all methods in ViewDelegte & ScrollDelegate, and ensures
+    // there's a corresponding override (or better, caller) for each of them in here.
+
+    @Override
+    protected void onAttachedToWindow() {
+        super.onAttachedToWindow();
+        mProvider.getViewDelegate().onAttachedToWindow();
+    }
+
+    /** @hide */
+    @Override
+    protected void onDetachedFromWindowInternal() {
+        mProvider.getViewDelegate().onDetachedFromWindow();
+        super.onDetachedFromWindowInternal();
+    }
+
+    /** @hide */
+    @Override
+    public void onMovedToDisplay(int displayId, Configuration config) {
+        mProvider.getViewDelegate().onMovedToDisplay(displayId, config);
+    }
+
+    @Override
+    public void setLayoutParams(ViewGroup.LayoutParams params) {
+        mProvider.getViewDelegate().setLayoutParams(params);
+    }
+
+    @Override
+    public void setOverScrollMode(int mode) {
+        super.setOverScrollMode(mode);
+        // This method may be called in the constructor chain, before the WebView provider is
+        // created.
+        ensureProviderCreated();
+        mProvider.getViewDelegate().setOverScrollMode(mode);
+    }
+
+    @Override
+    public void setScrollBarStyle(int style) {
+        mProvider.getViewDelegate().setScrollBarStyle(style);
+        super.setScrollBarStyle(style);
+    }
+
+    @Override
+    protected int computeHorizontalScrollRange() {
+        return mProvider.getScrollDelegate().computeHorizontalScrollRange();
+    }
+
+    @Override
+    protected int computeHorizontalScrollOffset() {
+        return mProvider.getScrollDelegate().computeHorizontalScrollOffset();
+    }
+
+    @Override
+    protected int computeVerticalScrollRange() {
+        return mProvider.getScrollDelegate().computeVerticalScrollRange();
+    }
+
+    @Override
+    protected int computeVerticalScrollOffset() {
+        return mProvider.getScrollDelegate().computeVerticalScrollOffset();
+    }
+
+    @Override
+    protected int computeVerticalScrollExtent() {
+        return mProvider.getScrollDelegate().computeVerticalScrollExtent();
+    }
+
+    @Override
+    public void computeScroll() {
+        mProvider.getScrollDelegate().computeScroll();
+    }
+
+    @Override
+    public boolean onHoverEvent(MotionEvent event) {
+        return mProvider.getViewDelegate().onHoverEvent(event);
+    }
+
+    @Override
+    public boolean onTouchEvent(MotionEvent event) {
+        return mProvider.getViewDelegate().onTouchEvent(event);
+    }
+
+    @Override
+    public boolean onGenericMotionEvent(MotionEvent event) {
+        return mProvider.getViewDelegate().onGenericMotionEvent(event);
+    }
+
+    @Override
+    public boolean onTrackballEvent(MotionEvent event) {
+        return mProvider.getViewDelegate().onTrackballEvent(event);
+    }
+
+    @Override
+    public boolean onKeyDown(int keyCode, KeyEvent event) {
+        return mProvider.getViewDelegate().onKeyDown(keyCode, event);
+    }
+
+    @Override
+    public boolean onKeyUp(int keyCode, KeyEvent event) {
+        return mProvider.getViewDelegate().onKeyUp(keyCode, event);
+    }
+
+    @Override
+    public boolean onKeyMultiple(int keyCode, int repeatCount, KeyEvent event) {
+        return mProvider.getViewDelegate().onKeyMultiple(keyCode, repeatCount, event);
+    }
+
+    /*
+    TODO: These are not currently implemented in WebViewClassic, but it seems inconsistent not
+    to be delegating them too.
+
+    @Override
+    public boolean onKeyPreIme(int keyCode, KeyEvent event) {
+        return mProvider.getViewDelegate().onKeyPreIme(keyCode, event);
+    }
+    @Override
+    public boolean onKeyLongPress(int keyCode, KeyEvent event) {
+        return mProvider.getViewDelegate().onKeyLongPress(keyCode, event);
+    }
+    @Override
+    public boolean onKeyShortcut(int keyCode, KeyEvent event) {
+        return mProvider.getViewDelegate().onKeyShortcut(keyCode, event);
+    }
+    */
+
+    @Override
+    public AccessibilityNodeProvider getAccessibilityNodeProvider() {
+        AccessibilityNodeProvider provider =
+                mProvider.getViewDelegate().getAccessibilityNodeProvider();
+        return provider == null ? super.getAccessibilityNodeProvider() : provider;
+    }
+
+    @Deprecated
+    @Override
+    public boolean shouldDelayChildPressedState() {
+        return mProvider.getViewDelegate().shouldDelayChildPressedState();
+    }
+
+    @Override
+    public CharSequence getAccessibilityClassName() {
+        return WebView.class.getName();
+    }
+
+    @Override
+    public void onProvideVirtualStructure(ViewStructure structure) {
+        mProvider.getViewDelegate().onProvideVirtualStructure(structure);
+    }
+
+    /**
+     * {@inheritDoc}
+     *
+     * <p>The {@link ViewStructure} traditionally represents a {@link View}, while for web pages
+     * it represent HTML nodes. Hence, it's necessary to "map" the HTML properties in a way that is
+     * understood by the {@link android.service.autofill.AutofillService} implementations:
+     *
+     * <ol>
+     *   <li>Only the HTML nodes inside a {@code FORM} are generated.
+     *   <li>The source of the HTML is set using {@link ViewStructure#setWebDomain(String)} in the
+     *   node representing the WebView.
+     *   <li>If a web page has multiple {@code FORM}s, only the data for the current form is
+     *   represented&mdash;if the user taps a field from another form, then the current autofill
+     *   context is canceled (by calling {@link android.view.autofill.AutofillManager#cancel()} and
+     *   a new context is created for that {@code FORM}.
+     *   <li>Similarly, if the page has {@code IFRAME} nodes, they are not initially represented in
+     *   the view structure until the user taps a field from a {@code FORM} inside the
+     *   {@code IFRAME}, in which case it would be treated the same way as multiple forms described
+     *   above, except that the {@link ViewStructure#setWebDomain(String) web domain} of the
+     *   {@code FORM} contains the {@code src} attribute from the {@code IFRAME} node.
+     *   <li>The W3C autofill field ({@code autocomplete} tag attribute) maps to
+     *   {@link ViewStructure#setAutofillHints(String[])}.
+     *   <li>If the view is editable, the {@link ViewStructure#setAutofillType(int)} and
+     *   {@link ViewStructure#setAutofillValue(AutofillValue)} must be set.
+     *   <li>The {@code placeholder} attribute maps to {@link ViewStructure#setHint(CharSequence)}.
+     *   <li>Other HTML attributes can be represented through
+     *   {@link ViewStructure#setHtmlInfo(android.view.ViewStructure.HtmlInfo)}.
+     * </ol>
+     *
+     * <p>If the WebView implementation can determine that the value of a field was set statically
+     * (for example, not through Javascript), it should also call
+     * {@code structure.setDataIsSensitive(false)}.
+     *
+     * <p>For example, an HTML form with 2 fields for username and password:
+     *
+     * <pre class="prettyprint">
+     *    &lt;label&gt;Username:&lt;/label&gt;
+     *    &lt;input type="text" name="username" id="user" value="Type your username" autocomplete="username" placeholder="Email or username"&gt;
+     *    &lt;label&gt;Password:&lt;/label&gt;
+     *    &lt;input type="password" name="password" id="pass" autocomplete="current-password" placeholder="Password"&gt;
+     * </pre>
+     *
+     * <p>Would map to:
+     *
+     * <pre class="prettyprint">
+     *     int index = structure.addChildCount(2);
+     *     ViewStructure username = structure.newChild(index);
+     *     username.setAutofillId(structure.getAutofillId(), 1); // id 1 - first child
+     *     username.setAutofillHints("username");
+     *     username.setHtmlInfo(username.newHtmlInfoBuilder("input")
+     *         .addAttribute("type", "text")
+     *         .addAttribute("name", "username")
+     *         .addAttribute("label", "Username:")
+     *         .build());
+     *     username.setHint("Email or username");
+     *     username.setAutofillType(View.AUTOFILL_TYPE_TEXT);
+     *     username.setAutofillValue(AutofillValue.forText("Type your username"));
+     *     // Value of the field is not sensitive because it was created statically and not changed.
+     *     username.setDataIsSensitive(false);
+     *
+     *     ViewStructure password = structure.newChild(index + 1);
+     *     username.setAutofillId(structure, 2); // id 2 - second child
+     *     password.setAutofillHints("current-password");
+     *     password.setHtmlInfo(password.newHtmlInfoBuilder("input")
+     *         .addAttribute("type", "password")
+     *         .addAttribute("name", "password")
+     *         .addAttribute("label", "Password:")
+     *         .build());
+     *     password.setHint("Password");
+     *     password.setAutofillType(View.AUTOFILL_TYPE_TEXT);
+     * </pre>
+     */
+    @Override
+    public void onProvideAutofillVirtualStructure(ViewStructure structure, int flags) {
+        mProvider.getViewDelegate().onProvideAutofillVirtualStructure(structure, flags);
+    }
+
+    @Override
+    public void autofill(SparseArray<AutofillValue>values) {
+        mProvider.getViewDelegate().autofill(values);
+    }
+
+    @Override
+    public boolean isVisibleToUserForAutofill(int virtualId) {
+        return mProvider.getViewDelegate().isVisibleToUserForAutofill(virtualId);
+    }
+
+    /** @hide */
+    @Override
+    public void onInitializeAccessibilityNodeInfoInternal(AccessibilityNodeInfo info) {
+        super.onInitializeAccessibilityNodeInfoInternal(info);
+        mProvider.getViewDelegate().onInitializeAccessibilityNodeInfo(info);
+    }
+
+    /** @hide */
+    @Override
+    public void onInitializeAccessibilityEventInternal(AccessibilityEvent event) {
+        super.onInitializeAccessibilityEventInternal(event);
+        mProvider.getViewDelegate().onInitializeAccessibilityEvent(event);
+    }
+
+    /** @hide */
+    @Override
+    public boolean performAccessibilityActionInternal(int action, Bundle arguments) {
+        return mProvider.getViewDelegate().performAccessibilityAction(action, arguments);
+    }
+
+    /** @hide */
+    @Override
+    protected void onDrawVerticalScrollBar(Canvas canvas, Drawable scrollBar,
+            int l, int t, int r, int b) {
+        mProvider.getViewDelegate().onDrawVerticalScrollBar(canvas, scrollBar, l, t, r, b);
+    }
+
+    @Override
+    protected void onOverScrolled(int scrollX, int scrollY, boolean clampedX, boolean clampedY) {
+        mProvider.getViewDelegate().onOverScrolled(scrollX, scrollY, clampedX, clampedY);
+    }
+
+    @Override
+    protected void onWindowVisibilityChanged(int visibility) {
+        super.onWindowVisibilityChanged(visibility);
+        mProvider.getViewDelegate().onWindowVisibilityChanged(visibility);
+    }
+
+    @Override
+    protected void onDraw(Canvas canvas) {
+        mProvider.getViewDelegate().onDraw(canvas);
+    }
+
+    @Override
+    public boolean performLongClick() {
+        return mProvider.getViewDelegate().performLongClick();
+    }
+
+    @Override
+    protected void onConfigurationChanged(Configuration newConfig) {
+        mProvider.getViewDelegate().onConfigurationChanged(newConfig);
+    }
+
+    /**
+     * Creates a new InputConnection for an InputMethod to interact with the WebView.
+     * This is similar to {@link View#onCreateInputConnection} but note that WebView
+     * calls InputConnection methods on a thread other than the UI thread.
+     * If these methods are overridden, then the overriding methods should respect
+     * thread restrictions when calling View methods or accessing data.
+     */
+    @Override
+    public InputConnection onCreateInputConnection(EditorInfo outAttrs) {
+        return mProvider.getViewDelegate().onCreateInputConnection(outAttrs);
+    }
+
+    @Override
+    public boolean onDragEvent(DragEvent event) {
+        return mProvider.getViewDelegate().onDragEvent(event);
+    }
+
+    @Override
+    protected void onVisibilityChanged(View changedView, int visibility) {
+        super.onVisibilityChanged(changedView, visibility);
+        // This method may be called in the constructor chain, before the WebView provider is
+        // created.
+        ensureProviderCreated();
+        mProvider.getViewDelegate().onVisibilityChanged(changedView, visibility);
+    }
+
+    @Override
+    public void onWindowFocusChanged(boolean hasWindowFocus) {
+        mProvider.getViewDelegate().onWindowFocusChanged(hasWindowFocus);
+        super.onWindowFocusChanged(hasWindowFocus);
+    }
+
+    @Override
+    protected void onFocusChanged(boolean focused, int direction, Rect previouslyFocusedRect) {
+        mProvider.getViewDelegate().onFocusChanged(focused, direction, previouslyFocusedRect);
+        super.onFocusChanged(focused, direction, previouslyFocusedRect);
+    }
+
+    /** @hide */
+    @Override
+    protected boolean setFrame(int left, int top, int right, int bottom) {
+        return mProvider.getViewDelegate().setFrame(left, top, right, bottom);
+    }
+
+    @Override
+    protected void onSizeChanged(int w, int h, int ow, int oh) {
+        super.onSizeChanged(w, h, ow, oh);
+        mProvider.getViewDelegate().onSizeChanged(w, h, ow, oh);
+    }
+
+    @Override
+    protected void onScrollChanged(int l, int t, int oldl, int oldt) {
+        super.onScrollChanged(l, t, oldl, oldt);
+        mProvider.getViewDelegate().onScrollChanged(l, t, oldl, oldt);
+    }
+
+    @Override
+    public boolean dispatchKeyEvent(KeyEvent event) {
+        return mProvider.getViewDelegate().dispatchKeyEvent(event);
+    }
+
+    @Override
+    public boolean requestFocus(int direction, Rect previouslyFocusedRect) {
+        return mProvider.getViewDelegate().requestFocus(direction, previouslyFocusedRect);
+    }
+
+    @Override
+    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+        mProvider.getViewDelegate().onMeasure(widthMeasureSpec, heightMeasureSpec);
+    }
+
+    @Override
+    public boolean requestChildRectangleOnScreen(View child, Rect rect, boolean immediate) {
+        return mProvider.getViewDelegate().requestChildRectangleOnScreen(child, rect, immediate);
+    }
+
+    @Override
+    public void setBackgroundColor(int color) {
+        mProvider.getViewDelegate().setBackgroundColor(color);
+    }
+
+    @Override
+    public void setLayerType(int layerType, Paint paint) {
+        super.setLayerType(layerType, paint);
+        mProvider.getViewDelegate().setLayerType(layerType, paint);
+    }
+
+    @Override
+    protected void dispatchDraw(Canvas canvas) {
+        mProvider.getViewDelegate().preDispatchDraw(canvas);
+        super.dispatchDraw(canvas);
+    }
+
+    @Override
+    public void onStartTemporaryDetach() {
+        super.onStartTemporaryDetach();
+        mProvider.getViewDelegate().onStartTemporaryDetach();
+    }
+
+    @Override
+    public void onFinishTemporaryDetach() {
+        super.onFinishTemporaryDetach();
+        mProvider.getViewDelegate().onFinishTemporaryDetach();
+    }
+
+    @Override
+    public Handler getHandler() {
+        return mProvider.getViewDelegate().getHandler(super.getHandler());
+    }
+
+    @Override
+    public View findFocus() {
+        return mProvider.getViewDelegate().findFocus(super.findFocus());
+    }
+
+    /**
+     * If WebView has already been loaded into the current process this method will return the
+     * package that was used to load it. Otherwise, the package that would be used if the WebView
+     * was loaded right now will be returned; this does not cause WebView to be loaded, so this
+     * information may become outdated at any time.
+     * The WebView package changes either when the current WebView package is updated, disabled, or
+     * uninstalled. It can also be changed through a Developer Setting.
+     * If the WebView package changes, any app process that has loaded WebView will be killed. The
+     * next time the app starts and loads WebView it will use the new WebView package instead.
+     * @return the current WebView package, or {@code null} if there is none.
+     */
+    @Nullable
+    public static PackageInfo getCurrentWebViewPackage() {
+        PackageInfo webviewPackage = WebViewFactory.getLoadedPackageInfo();
+        if (webviewPackage != null) {
+            return webviewPackage;
+        }
+
+        IWebViewUpdateService service = WebViewFactory.getUpdateService();
+        if (service == null) {
+            return null;
+        }
+        try {
+            return service.getCurrentWebViewPackage();
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Receive the result from a previous call to {@link #startActivityForResult(Intent, int)}.
+     *
+     * @param requestCode The integer request code originally supplied to
+     *                    startActivityForResult(), allowing you to identify who this
+     *                    result came from.
+     * @param resultCode The integer result code returned by the child activity
+     *                   through its setResult().
+     * @param data An Intent, which can return result data to the caller
+     *               (various data can be attached to Intent "extras").
+     * @hide
+     */
+    @Override
+    public void onActivityResult(int requestCode, int resultCode, Intent data) {
+        mProvider.getViewDelegate().onActivityResult(requestCode, resultCode, data);
+    }
+
+    @Override
+    public boolean onCheckIsTextEditor() {
+        return mProvider.getViewDelegate().onCheckIsTextEditor();
+    }
+
+    /** @hide */
+    @Override
+    protected void encodeProperties(@NonNull ViewHierarchyEncoder encoder) {
+        super.encodeProperties(encoder);
+
+        checkThread();
+        encoder.addProperty("webview:contentHeight", mProvider.getContentHeight());
+        encoder.addProperty("webview:contentWidth", mProvider.getContentWidth());
+        encoder.addProperty("webview:scale", mProvider.getScale());
+        encoder.addProperty("webview:title", mProvider.getTitle());
+        encoder.addProperty("webview:url", mProvider.getUrl());
+        encoder.addProperty("webview:originalUrl", mProvider.getOriginalUrl());
     }
 }
diff --git a/android/widget/Editor.java b/android/widget/Editor.java
index 9946726..dac100a 100644
--- a/android/widget/Editor.java
+++ b/android/widget/Editor.java
@@ -39,6 +39,7 @@
 import android.graphics.Matrix;
 import android.graphics.Paint;
 import android.graphics.Path;
+import android.graphics.Point;
 import android.graphics.PointF;
 import android.graphics.Rect;
 import android.graphics.RectF;
@@ -4837,14 +4838,48 @@
             return true;
         }
 
-        private boolean handleOverlapsMagnifier() {
-            final int handleY = mContainer.getDecorViewLayoutParams().y;
-            final int magnifierBottomWhenAtWindowTop =
-                    mTextView.getRootWindowInsets().getSystemWindowInsetTop()
-                        + mMagnifierAnimator.mMagnifier.getHeight();
-            return handleY <= magnifierBottomWhenAtWindowTop;
+        private boolean handleOverlapsMagnifier(@NonNull final HandleView handle,
+                @NonNull final Rect magnifierRect) {
+            final PopupWindow window = handle.mContainer;
+            if (!window.hasDecorView()) {
+                return false;
+            }
+            final Rect handleRect = new Rect(
+                    window.getDecorViewLayoutParams().x,
+                    window.getDecorViewLayoutParams().y,
+                    window.getDecorViewLayoutParams().x + window.getContentView().getWidth(),
+                    window.getDecorViewLayoutParams().y + window.getContentView().getHeight());
+            return Rect.intersects(handleRect, magnifierRect);
         }
 
+        private @Nullable HandleView getOtherSelectionHandle() {
+            final SelectionModifierCursorController controller = getSelectionController();
+            if (controller == null || !controller.isActive()) {
+                return null;
+            }
+            return controller.mStartHandle != this
+                    ? controller.mStartHandle
+                    : controller.mEndHandle;
+        }
+
+        private final Magnifier.Callback mHandlesVisibilityCallback = new Magnifier.Callback() {
+            @Override
+            public void onOperationComplete() {
+                final Point magnifierTopLeft = mMagnifierAnimator.mMagnifier.getWindowCoords();
+                if (magnifierTopLeft == null) {
+                    return;
+                }
+                final Rect magnifierRect = new Rect(magnifierTopLeft.x, magnifierTopLeft.y,
+                        magnifierTopLeft.x + mMagnifierAnimator.mMagnifier.getWidth(),
+                        magnifierTopLeft.y + mMagnifierAnimator.mMagnifier.getHeight());
+                setVisible(!handleOverlapsMagnifier(HandleView.this, magnifierRect));
+                final HandleView otherHandle = getOtherSelectionHandle();
+                if (otherHandle != null) {
+                    otherHandle.setVisible(!handleOverlapsMagnifier(otherHandle, magnifierRect));
+                }
+            }
+        };
+
         protected final void updateMagnifier(@NonNull final MotionEvent event) {
             if (mMagnifierAnimator == null) {
                 return;
@@ -4858,12 +4893,8 @@
                 mRenderCursorRegardlessTiming = true;
                 mTextView.invalidateCursorPath();
                 suspendBlink();
-                // Hide handle if it overlaps the magnifier.
-                if (handleOverlapsMagnifier()) {
-                    setVisible(false);
-                } else {
-                    setVisible(true);
-                }
+                mMagnifierAnimator.mMagnifier
+                        .setOnOperationCompleteCallback(mHandlesVisibilityCallback);
 
                 mMagnifierAnimator.show(showPosInView.x, showPosInView.y);
             } else {
@@ -4877,6 +4908,10 @@
                 mRenderCursorRegardlessTiming = false;
                 resumeBlink();
                 setVisible(true);
+                final HandleView otherHandle = getOtherSelectionHandle();
+                if (otherHandle != null) {
+                    otherHandle.setVisible(true);
+                }
             }
         }
 
@@ -6031,7 +6066,9 @@
             mSwitchedLines = false;
             final int selectionStart = mTextView.getSelectionStart();
             final int selectionEnd = mTextView.getSelectionEnd();
-            if (selectionStart > selectionEnd) {
+            if (selectionStart < 0 || selectionEnd < 0) {
+                Selection.removeSelection((Spannable) mTextView.getText());
+            } else if (selectionStart > selectionEnd) {
                 Selection.setSelection((Spannable) mTextView.getText(),
                         selectionEnd, selectionStart);
             }
diff --git a/android/widget/ImageView.java b/android/widget/ImageView.java
index 4b951fa..1372987 100644
--- a/android/widget/ImageView.java
+++ b/android/widget/ImageView.java
@@ -817,8 +817,6 @@
         if (mScaleType != scaleType) {
             mScaleType = scaleType;
 
-            setWillNotCacheDrawing(mScaleType == ScaleType.CENTER);
-
             requestLayout();
             invalidate();
         }
diff --git a/android/widget/LinearLayout.java b/android/widget/LinearLayout.java
index d32e93c..40f9652 100644
--- a/android/widget/LinearLayout.java
+++ b/android/widget/LinearLayout.java
@@ -217,6 +217,17 @@
 
     private int mLayoutDirection = View.LAYOUT_DIRECTION_UNDEFINED;
 
+    /**
+     * Signals that compatibility booleans have been initialized according to
+     * target SDK versions.
+     */
+    private static boolean sCompatibilityDone = false;
+
+    /**
+     * Behavior change in P; always remeasure weighted children, regardless of excess space.
+     */
+    private static boolean sRemeasureWeightedChildren = true;
+
     public LinearLayout(Context context) {
         this(context, null);
     }
@@ -232,6 +243,15 @@
     public LinearLayout(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
         super(context, attrs, defStyleAttr, defStyleRes);
 
+        if (!sCompatibilityDone && context != null) {
+            final int targetSdkVersion = context.getApplicationInfo().targetSdkVersion;
+
+            // Older apps only remeasure non-zero children
+            sRemeasureWeightedChildren = targetSdkVersion >= Build.VERSION_CODES.P;
+
+            sCompatibilityDone = true;
+        }
+
         final TypedArray a = context.obtainStyledAttributes(
                 attrs, com.android.internal.R.styleable.LinearLayout, defStyleAttr, defStyleRes);
 
@@ -917,7 +937,8 @@
         // measurement on any children, we need to measure them now.
         int remainingExcess = heightSize - mTotalLength
                 + (mAllowInconsistentMeasurement ? 0 : consumedExcessSpace);
-        if (skippedMeasure || totalWeight > 0.0f) {
+        if (skippedMeasure
+                || ((sRemeasureWeightedChildren || remainingExcess != 0) && totalWeight > 0.0f)) {
             float remainingWeightSum = mWeightSum > 0.0f ? mWeightSum : totalWeight;
 
             mTotalLength = 0;
@@ -1300,7 +1321,8 @@
         // measurement on any children, we need to measure them now.
         int remainingExcess = widthSize - mTotalLength
                 + (mAllowInconsistentMeasurement ? 0 : usedExcessSpace);
-        if (skippedMeasure || totalWeight > 0.0f) {
+        if (skippedMeasure
+                || ((sRemeasureWeightedChildren || remainingExcess != 0) && totalWeight > 0.0f)) {
             float remainingWeightSum = mWeightSum > 0.0f ? mWeightSum : totalWeight;
 
             maxAscent[0] = maxAscent[1] = maxAscent[2] = maxAscent[3] = -1;
diff --git a/android/widget/Magnifier.java b/android/widget/Magnifier.java
index 5eb6699..cb362e6 100644
--- a/android/widget/Magnifier.java
+++ b/android/widget/Magnifier.java
@@ -233,6 +233,17 @@
         return mZoom;
     }
 
+    /**
+     * @hide
+     */
+    @Nullable
+    public Point getWindowCoords() {
+        if (mWindow == null) {
+            return null;
+        }
+        return new Point(mWindow.mLastDrawContentPositionX, mWindow.mLastDrawContentPositionY);
+    }
+
     @Nullable
     private Surface getValidViewSurface() {
         // TODO: deduplicate this against the first part of #performPixelCopy
@@ -374,8 +385,11 @@
         private final Runnable mMagnifierUpdater;
         // The handler where the magnifier updater jobs will be post'd.
         private final Handler mHandler;
-        // The callback to be run after the next draw. Only used for testing.
+        // The callback to be run after the next draw.
         private Callback mCallback;
+        // The position of the magnifier content when the last draw was requested.
+        private int mLastDrawContentPositionX;
+        private int mLastDrawContentPositionY;
 
         // Members below describe the state of the magnifier. Reads/writes to them
         // have to be synchronized between the UI thread and the thread that handles
@@ -598,6 +612,8 @@
                     callback = null;
                 }
 
+                mLastDrawContentPositionX = mWindowPositionX + mOffsetX;
+                mLastDrawContentPositionY = mWindowPositionY + mOffsetY;
                 mFrameDrawScheduled = false;
             }
 
diff --git a/android/widget/SelectionActionModeHelper.java b/android/widget/SelectionActionModeHelper.java
index b3327a7..1f2b90a 100644
--- a/android/widget/SelectionActionModeHelper.java
+++ b/android/widget/SelectionActionModeHelper.java
@@ -33,9 +33,9 @@
 import android.text.TextUtils;
 import android.util.Log;
 import android.view.ActionMode;
-import android.view.textclassifier.Logger;
 import android.view.textclassifier.SelectionEvent;
 import android.view.textclassifier.SelectionEvent.InvocationMethod;
+import android.view.textclassifier.SelectionSessionLogger;
 import android.view.textclassifier.TextClassification;
 import android.view.textclassifier.TextClassificationConstants;
 import android.view.textclassifier.TextClassificationManager;
@@ -663,7 +663,6 @@
         private static final String LOG_TAG = "SelectionMetricsLogger";
         private static final Pattern PATTERN_WHITESPACE = Pattern.compile("\\s+");
 
-        private final Logger mLogger;
         private final boolean mEditTextLogger;
         private final BreakIterator mTokenIterator;
 
@@ -673,10 +672,8 @@
 
         SelectionMetricsLogger(TextView textView) {
             Preconditions.checkNotNull(textView);
-            mLogger = textView.getTextClassifier().getLogger(
-                    new Logger.Config(textView.getContext(), getWidetType(textView), null));
             mEditTextLogger = textView.isTextEditable();
-            mTokenIterator = mLogger.getTokenIterator(textView.getTextLocale());
+            mTokenIterator = SelectionSessionLogger.getTokenIterator(textView.getTextLocale());
         }
 
         @TextClassifier.WidgetType
@@ -702,8 +699,6 @@
                 }
                 mTokenIterator.setText(mText);
                 mStartIndex = index;
-                mLogger.logSelectionStartedEvent(invocationMethod, 0);
-                // TODO: Remove the above legacy logging.
                 mClassificationSession = classificationSession;
                 mClassificationSession.onSelectionEvent(
                         SelectionEvent.createSelectionStartedEvent(invocationMethod, 0));
@@ -720,27 +715,18 @@
                 Preconditions.checkArgumentInRange(end, start, mText.length(), "end");
                 int[] wordIndices = getWordDelta(start, end);
                 if (selection != null) {
-                    mLogger.logSelectionModifiedEvent(
-                            wordIndices[0], wordIndices[1], selection);
-                    // TODO: Remove the above legacy logging.
                     if (mClassificationSession != null) {
                         mClassificationSession.onSelectionEvent(
                                 SelectionEvent.createSelectionModifiedEvent(
                                         wordIndices[0], wordIndices[1], selection));
                     }
                 } else if (classification != null) {
-                    mLogger.logSelectionModifiedEvent(
-                            wordIndices[0], wordIndices[1], classification);
-                    // TODO: Remove the above legacy logging.
                     if (mClassificationSession != null) {
                         mClassificationSession.onSelectionEvent(
                                 SelectionEvent.createSelectionModifiedEvent(
                                         wordIndices[0], wordIndices[1], classification));
                     }
                 } else {
-                    mLogger.logSelectionModifiedEvent(
-                            wordIndices[0], wordIndices[1]);
-                    // TODO: Remove the above legacy logging.
                     if (mClassificationSession != null) {
                         mClassificationSession.onSelectionEvent(
                                 SelectionEvent.createSelectionModifiedEvent(
@@ -762,18 +748,12 @@
                 Preconditions.checkArgumentInRange(end, start, mText.length(), "end");
                 int[] wordIndices = getWordDelta(start, end);
                 if (classification != null) {
-                    mLogger.logSelectionActionEvent(
-                            wordIndices[0], wordIndices[1], action, classification);
-                    // TODO: Remove the above legacy logging.
                     if (mClassificationSession != null) {
                         mClassificationSession.onSelectionEvent(
                                 SelectionEvent.createSelectionActionEvent(
                                         wordIndices[0], wordIndices[1], action, classification));
                     }
                 } else {
-                    mLogger.logSelectionActionEvent(
-                            wordIndices[0], wordIndices[1], action);
-                    // TODO: Remove the above legacy logging.
                     if (mClassificationSession != null) {
                         mClassificationSession.onSelectionEvent(
                                 SelectionEvent.createSelectionActionEvent(
@@ -989,7 +969,7 @@
             mHot = true;
             trimText();
             final TextSelection selection;
-            if (mContext.getApplicationInfo().targetSdkVersion > Build.VERSION_CODES.O_MR1) {
+            if (mContext.getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.P) {
                 final TextSelection.Request request = new TextSelection.Request.Builder(
                         mTrimmedText, mRelativeStart, mRelativeEnd)
                         .setDefaultLocales(mDefaultLocales)
@@ -1043,7 +1023,7 @@
 
                 trimText();
                 final TextClassification classification;
-                if (mContext.getApplicationInfo().targetSdkVersion > Build.VERSION_CODES.O_MR1) {
+                if (mContext.getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.P) {
                     final TextClassification.Request request =
                             new TextClassification.Request.Builder(
                                     mTrimmedText, mRelativeStart, mRelativeEnd)
diff --git a/android/widget/TextClock.java b/android/widget/TextClock.java
index 53318c9..d8a9cca 100644
--- a/android/widget/TextClock.java
+++ b/android/widget/TextClock.java
@@ -408,6 +408,15 @@
     }
 
     /**
+     * Update the displayed time if necessary and invalidate the view.
+     * @hide
+     */
+    public void refresh() {
+        onTimeChanged();
+        invalidate();
+    }
+
+    /**
      * Indicates whether the system is currently using the 24-hour mode.
      *
      * When the system is in 24-hour mode, this view will use the pattern
diff --git a/android/widget/TextView.java b/android/widget/TextView.java
index 11db6b6..7b9ecca 100644
--- a/android/widget/TextView.java
+++ b/android/widget/TextView.java
@@ -317,7 +317,6 @@
  * @attr ref android.R.styleable#TextView_autoSizeMaxTextSize
  * @attr ref android.R.styleable#TextView_autoSizeStepGranularity
  * @attr ref android.R.styleable#TextView_autoSizePresetSizes
- * @attr ref android.R.styleable#TextView_accessibilityHeading
  */
 @RemoteView
 public class TextView extends View implements ViewTreeObserver.OnPreDrawListener {
@@ -417,7 +416,6 @@
     private int mCurTextColor;
     private int mCurHintTextColor;
     private boolean mFreezesText;
-    private boolean mIsAccessibilityHeading;
 
     private Editable.Factory mEditableFactory = Editable.Factory.getInstance();
     private Spannable.Factory mSpannableFactory = Spannable.Factory.getInstance();
@@ -1294,8 +1292,6 @@
                 case com.android.internal.R.styleable.TextView_lineHeight:
                     lineHeight = a.getDimensionPixelSize(attr, -1);
                     break;
-                case com.android.internal.R.styleable.TextView_accessibilityHeading:
-                    mIsAccessibilityHeading = a.getBoolean(attr, false);
             }
         }
 
@@ -5213,32 +5209,6 @@
     }
 
     /**
-     * Gets whether this view is a heading for accessibility purposes.
-     *
-     * @return {@code true} if the view is a heading, {@code false} otherwise.
-     *
-     * @attr ref android.R.styleable#TextView_accessibilityHeading
-     */
-    public boolean isAccessibilityHeading() {
-        return mIsAccessibilityHeading;
-    }
-
-    /**
-     * Set if view is a heading for a section of content for accessibility purposes.
-     *
-     * @param isHeading {@code true} if the view is a heading, {@code false} otherwise.
-     *
-     * @attr ref android.R.styleable#TextView_accessibilityHeading
-     */
-    public void setAccessibilityHeading(boolean isHeading) {
-        if (isHeading != mIsAccessibilityHeading) {
-            mIsAccessibilityHeading = isHeading;
-            notifyViewAccessibilityStateChangedIfNeeded(
-                    AccessibilityEvent.CONTENT_CHANGE_TYPE_UNDEFINED);
-        }
-    }
-
-    /**
      * Convenience method to append the specified text to the TextView's
      * display buffer, upgrading it to {@link android.widget.TextView.BufferType#EDITABLE}
      * if it was not already editable.
@@ -9380,7 +9350,7 @@
         final int selectionStart = getSelectionStart();
         final int selectionEnd = getSelectionEnd();
 
-        return selectionStart >= 0 && selectionStart != selectionEnd;
+        return selectionStart >= 0 && selectionEnd > 0 && selectionStart != selectionEnd;
     }
 
     String getSelectedText() {
@@ -10833,7 +10803,6 @@
         info.setText(getTextForAccessibility());
         info.setHintText(mHint);
         info.setShowingHintText(isShowingHint());
-        info.setHeading(mIsAccessibilityHeading);
 
         if (mBufferType == BufferType.EDITABLE) {
             info.setEditable(true);